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: Don't negate resulted offsets when `offset` is subtraction by 0 --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index cb44eccae68..f16b98883b8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -960,8 +960,8 @@ fn detect_manual_memcpy<'a, 'tcx>( let print_sum = |arg1: &Offset, arg2: &Offset| -> String { match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) { ("0", _, "0", _) => "".into(), - ("0", _, x, false) | (x, false, "0", false) => x.into(), - ("0", _, x, true) | (x, false, "0", true) => format!("-{}", x), + ("0", _, x, false) | (x, false, "0", _) => x.into(), + ("0", _, x, true) => format!("-{}", x), (x, false, y, false) => format!("({} + {})", x, y), (x, false, y, true) => { if x == y { -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f16b98883b8..6b5a8498dc9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -959,7 +959,7 @@ fn detect_manual_memcpy<'a, 'tcx>( if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { let print_sum = |arg1: &Offset, arg2: &Offset| -> String { match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) { - ("0", _, "0", _) => "".into(), + ("0", _, "0", _) => "0".into(), ("0", _, x, false) | (x, false, "0", _) => x.into(), ("0", _, x, true) => format!("-{}", x), (x, false, y, false) => format!("({} + {})", x, y), @@ -981,6 +981,15 @@ fn detect_manual_memcpy<'a, 'tcx>( } }; + let print_offset = |start_str: &Offset, inline_offset: &Offset| -> String { + let offset = print_sum(start_str, inline_offset); + if offset.as_str() == "0" { + "".into() + } else { + offset + } + }; + let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| { if let Some(end) = *end { if_chain! { @@ -1020,9 +1029,9 @@ fn detect_manual_memcpy<'a, 'tcx>( .into_iter() .map(|(dst_var, src_var)| { let start_str = Offset::positive(snippet(cx, start.span, "").to_string()); - let dst_offset = print_sum(&start_str, &dst_var.offset); + let dst_offset = print_offset(&start_str, &dst_var.offset); let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name); - let src_offset = print_sum(&start_str, &src_var.offset); + let src_offset = print_offset(&start_str, &src_var.offset); let src_limit = print_limit(end, src_var.offset, &src_var.var_name); let dst = if dst_offset == "" && dst_limit == "" { dst_var.var_name diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 3dbb2155d4d..ec80f6070d6 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -58,13 +58,13 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:94:14 | LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:98:14 | LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])` + | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:105:14 -- cgit 1.4.1-3-g733a5 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: Do not trigger `manual_memcpy` for `RangeTo` --- clippy_lints/src/loops.rs | 50 ++++++++++++++++++++----------------------- tests/ui/manual_memcpy.rs | 5 +++++ tests/ui/manual_memcpy.stderr | 2 +- 3 files changed, 29 insertions(+), 28 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6b5a8498dc9..ca61c97e3e3 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -951,7 +951,7 @@ fn detect_manual_memcpy<'a, 'tcx>( ) { if let Some(higher::Range { start: Some(start), - ref end, + end: Some(end), limits, }) = higher::range(cx, arg) { @@ -990,35 +990,31 @@ fn detect_manual_memcpy<'a, 'tcx>( } }; - let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| { - if let Some(end) = *end { - if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if snippet(cx, arg.span, "??") == var_name; - then { - return if offset.negate { - format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) - } else { - String::new() - }; - } + let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { + if_chain! { + if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if snippet(cx, arg.span, "??") == var_name; + then { + return if offset.negate { + format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) + } else { + String::new() + }; } + } - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; + 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() - } + print_sum(&Offset::positive(end_str), &offset) }; // The only statements in the for loops can be indexed assignments from diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index aa347288875..1f41838fa16 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -98,6 +98,11 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in from..from + 3 { dst[i] = src[i - from]; } + + // `RangeTo` `for` loop - don't trigger lint + for i in 0.. { + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index ec80f6070d6..95114c46f36 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -67,7 +67,7 @@ LL | for i in from..from + 3 { | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:105:14 + --> $DIR/manual_memcpy.rs:110:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -- cgit 1.4.1-3-g733a5 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: Remove all `ref` keyword --- clippy_lints/src/loops.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ca61c97e3e3..502bd42214e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -772,8 +772,8 @@ fn check_for_loop<'a, 'tcx>( fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if_chain! { - if let ExprKind::Path(ref qpath) = expr.kind; - if let QPath::Resolved(None, ref path) = *qpath; + if let ExprKind::Path(qpath) = &expr.kind; + if let QPath::Resolved(None, path) = qpath; if path.segments.len() == 1; if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id); // our variable! @@ -821,8 +821,8 @@ fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool { fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option { fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { - match e.kind { - ExprKind::Lit(ref l) => match l.node { + match &e.kind { + ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, @@ -831,14 +831,14 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } - if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind { + if let ExprKind::Index(seqexpr, idx) = expr.kind { let ty = cx.tables.expr_ty(seqexpr); if !is_slice_like(cx, ty) { return None; } let offset = match idx.kind { - ExprKind::Binary(op, ref lhs, ref rhs) => match op.node { + ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { let offset_opt = if same_var(cx, lhs, var) { extract_offset(cx, rhs, var) @@ -878,7 +878,7 @@ fn fetch_cloned_fixed_offset_var<'a, 'tcx>( var: HirId, ) -> Option { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind; + if let ExprKind::MethodCall(method, _, args) = expr.kind; if method.ident.name == sym!(clone); if args.len() == 1; if let Some(arg) = args.get(0); @@ -900,7 +900,7 @@ fn get_indexed_assignments<'a, 'tcx>( e: &Expr<'_>, var: HirId, ) -> Option<(FixedOffsetVar, FixedOffsetVar)> { - if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind { + if let ExprKind::Assign(lhs, rhs, _) = e.kind { match ( get_fixed_offset_var(cx, lhs, var), fetch_cloned_fixed_offset_var(cx, rhs, var), @@ -920,16 +920,14 @@ fn get_indexed_assignments<'a, 'tcx>( } } - if let ExprKind::Block(ref b, _) = body.kind { - let Block { - ref stmts, ref expr, .. - } = **b; + if let ExprKind::Block(b, _) = body.kind { + let Block { stmts, expr, .. } = *b; stmts .iter() .map(|stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => Some(get_assignment(cx, e, var)), + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(get_assignment(cx, e, var)), }) .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var)))) .filter_map(|op| op) @@ -992,7 +990,7 @@ fn detect_manual_memcpy<'a, 'tcx>( let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; + if let ExprKind::MethodCall(method, _, len_args) = end.kind; if method.ident.name == sym!(len); if len_args.len() == 1; if let Some(arg) = len_args.get(0); -- cgit 1.4.1-3-g733a5 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: Refactor `if` to use `else` and iterator combinators --- clippy_lints/src/loops.rs | 50 +++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 502bd42214e..3dd3a79b287 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -779,11 +779,11 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) - // our variable! if local_id == var; then { - return true; + true + } else { + false } } - - false } struct Offset { @@ -853,13 +853,7 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), _ => None, }, - ExprKind::Path(..) => { - if same_var(cx, idx, var) { - Some(Offset::positive("0".into())) - } else { - None - } - }, + ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), _ => None, }; @@ -883,11 +877,11 @@ fn fetch_cloned_fixed_offset_var<'a, 'tcx>( if args.len() == 1; if let Some(arg) = args.get(0); then { - return get_fixed_offset_var(cx, arg, var); + get_fixed_offset_var(cx, arg, var) + } else { + get_fixed_offset_var(cx, expr, var) } } - - get_fixed_offset_var(cx, expr, var) } fn get_indexed_assignments<'a, 'tcx>( @@ -925,12 +919,12 @@ fn get_indexed_assignments<'a, 'tcx>( stmts .iter() - .map(|stmt| match stmt.kind { + .filter_map(|stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => Some(get_assignment(cx, e, var)), + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) - .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var)))) - .filter_map(|op| op) + .chain(expr.into_iter()) + .map(|op| get_assignment(cx, op, var)) .collect::>>() .unwrap_or_default() } else { @@ -996,23 +990,23 @@ fn detect_manual_memcpy<'a, 'tcx>( if let Some(arg) = len_args.get(0); if snippet(cx, arg.span, "??") == var_name; then { - return if offset.negate { + if offset.negate { format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) } else { String::new() + } + } else { + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + format!("{}", end + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), }; + + print_sum(&Offset::positive(end_str), &offset) } } - - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&Offset::positive(end_str), &offset) }; // The only statements in the for loops can be indexed assignments from -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3dd3a79b287..321d5265d0c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -786,20 +786,29 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) - } } +#[derive(Clone, Copy)] +enum OffsetSign { + Positive, + Negative, +} + struct Offset { value: String, - negate: bool, + sign: OffsetSign, } impl Offset { - fn negative(s: String) -> Self { - Self { value: s, negate: true } + fn negative(value: String) -> Self { + Self { + value, + sign: OffsetSign::Negative, + } } - fn positive(s: String) -> Self { + fn positive(value: String) -> Self { Self { - value: s, - negate: false, + value, + sign: OffsetSign::Positive, } } } @@ -949,31 +958,23 @@ fn detect_manual_memcpy<'a, 'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let print_sum = |arg1: &Offset, arg2: &Offset| -> String { - match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) { - ("0", _, "0", _) => "0".into(), - ("0", _, x, false) | (x, false, "0", _) => x.into(), - ("0", _, x, true) => format!("-{}", x), - (x, false, y, false) => format!("({} + {})", x, y), - (x, false, y, true) => { + let print_sum = |arg1: &str, arg2: &Offset| -> String { + match (arg1, &arg2.value[..], arg2.sign) { + ("0", "0", _) => "0".into(), + ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), + ("0", x, OffsetSign::Negative) => format!("-{}", x), + (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), + (x, y, OffsetSign::Negative) => { if x == y { "0".into() } else { format!("({} - {})", x, y) } }, - (x, true, y, false) => { - if x == y { - "0".into() - } else { - format!("({} - {})", y, x) - } - }, - (x, true, y, true) => format!("-({} + {})", x, y), } }; - let print_offset = |start_str: &Offset, inline_offset: &Offset| -> String { + let print_offset = |start_str: &str, inline_offset: &Offset| -> String { let offset = print_sum(start_str, inline_offset); if offset.as_str() == "0" { "".into() @@ -990,10 +991,9 @@ fn detect_manual_memcpy<'a, 'tcx>( if let Some(arg) = len_args.get(0); if snippet(cx, arg.span, "??") == var_name; then { - if offset.negate { - format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) - } else { - String::new() + match offset.sign { + OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), + OffsetSign::Positive => "".into(), } } else { let end_str = match limits { @@ -1004,7 +1004,7 @@ fn detect_manual_memcpy<'a, 'tcx>( ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), }; - print_sum(&Offset::positive(end_str), &offset) + print_sum(&end_str, &offset) } } }; @@ -1016,7 +1016,7 @@ fn detect_manual_memcpy<'a, 'tcx>( let big_sugg = manual_copies .into_iter() .map(|(dst_var, src_var)| { - let start_str = Offset::positive(snippet(cx, start.span, "").to_string()); + let start_str = snippet(cx, start.span, "").to_string(); let dst_offset = print_offset(&start_str, &dst_var.offset); let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name); let src_offset = print_offset(&start_str, &src_var.offset); -- cgit 1.4.1-3-g733a5 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: Use `fn` instead of closures where unnecessary --- clippy_lints/src/loops.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 321d5265d0c..e37c44dc026 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -958,7 +958,7 @@ fn detect_manual_memcpy<'a, 'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let print_sum = |arg1: &str, arg2: &Offset| -> String { + fn print_sum(arg1: &str, arg2: &Offset) -> String { match (arg1, &arg2.value[..], arg2.sign) { ("0", "0", _) => "0".into(), ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), @@ -972,16 +972,16 @@ fn detect_manual_memcpy<'a, 'tcx>( } }, } - }; + } - let print_offset = |start_str: &str, inline_offset: &Offset| -> String { + fn print_offset(start_str: &str, inline_offset: &Offset) -> String { let offset = print_sum(start_str, inline_offset); if offset.as_str() == "0" { "".into() } else { offset } - }; + } let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { if_chain! { -- cgit 1.4.1-3-g733a5 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: Extract `get_fixed_offset_var`` from `fetch_cloned_fixed_offset_var` --- clippy_lints/src/loops.rs | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index e37c44dc026..2dc95f53078 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -828,6 +828,16 @@ fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool { is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) } +fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + if_chain! { + if let ExprKind::MethodCall(method, _, args) = expr.kind; + if method.ident.name == sym!(clone); + if args.len() == 1; + if let Some(arg) = args.get(0); + then { arg } else { expr } + } +} + fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option { fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { match &e.kind { @@ -875,24 +885,6 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } -fn fetch_cloned_fixed_offset_var<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, - expr: &Expr<'_>, - var: HirId, -) -> Option { - if_chain! { - if let ExprKind::MethodCall(method, _, args) = expr.kind; - if method.ident.name == sym!(clone); - if args.len() == 1; - if let Some(arg) = args.get(0); - then { - get_fixed_offset_var(cx, arg, var) - } else { - get_fixed_offset_var(cx, expr, var) - } - } -} - fn get_indexed_assignments<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, body: &Expr<'_>, @@ -906,7 +898,7 @@ fn get_indexed_assignments<'a, 'tcx>( if let ExprKind::Assign(lhs, rhs, _) = e.kind { match ( get_fixed_offset_var(cx, lhs, var), - fetch_cloned_fixed_offset_var(cx, rhs, var), + get_fixed_offset_var(cx, fetch_cloned_expr(rhs), var), ) { (Some(offset_left), Some(offset_right)) => { // Source and destination must be different -- cgit 1.4.1-3-g733a5 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: Extract roles getting indexes from `get_indexed_assignments` --- clippy_lints/src/loops.rs | 106 ++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 51 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2dc95f53078..0753b23e45b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -10,7 +10,6 @@ use crate::utils::{ }; use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg}; use if_chain::if_chain; -use itertools::Itertools; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -885,52 +884,39 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } -fn get_indexed_assignments<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, - body: &Expr<'_>, - var: HirId, -) -> Vec<(FixedOffsetVar, FixedOffsetVar)> { - fn get_assignment<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, - e: &Expr<'_>, - var: HirId, - ) -> Option<(FixedOffsetVar, FixedOffsetVar)> { +fn get_assignments<'a, 'tcx>( + body: &'tcx Expr<'tcx>, +) -> impl Iterator, &'tcx Expr<'tcx>)>> { + fn get_assignment<'a, 'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Assign(lhs, rhs, _) = e.kind { - match ( - get_fixed_offset_var(cx, lhs, var), - get_fixed_offset_var(cx, fetch_cloned_expr(rhs), var), - ) { - (Some(offset_left), Some(offset_right)) => { - // Source and destination must be different - if offset_left.var_name == offset_right.var_name { - None - } else { - Some((offset_left, offset_right)) - } - }, - _ => None, - } + Some((lhs, rhs)) } else { None } } + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + if let ExprKind::Block(b, _) = body.kind { let Block { stmts, expr, .. } = *b; - stmts + iter_a = stmts .iter() .filter_map(|stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain(expr.into_iter()) - .map(|op| get_assignment(cx, op, var)) - .collect::>>() - .unwrap_or_default() + .map(get_assignment) + .into() } else { - get_assignment(cx, body, var).into_iter().collect() + iter_b = Some(get_assignment(body)) } + + iter_a.into_iter().flatten().chain(iter_b.into_iter()) } /// Checks for for loops that sequentially copy items from one slice-like @@ -1003,30 +989,48 @@ fn detect_manual_memcpy<'a, 'tcx>( // The only statements in the for loops can be indexed assignments from // indexed retrievals. - let manual_copies = get_indexed_assignments(cx, body, canonical_id); - - let big_sugg = 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) - }; + 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); + + // Source and destination must be different + if offset_left.var_name != offset_right.var_name; + then { + Some((offset_left, offset_right)) + } else { + return None + } + } + }) + }) + .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) + }; - format!( - "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var.var_name, src_offset, src_limit - ) + format!( + "{}.clone_from_slice(&{}[{}..{}])", + dst, src_var.var_name, src_offset, src_limit + ) + }) }) - .join("\n "); + .collect::>>() + .filter(|v| !v.is_empty()) + .map(|v| v.join("\n ")); - if !big_sugg.is_empty() { + if let Some(big_sugg) = big_sugg { span_lint_and_sugg( cx, MANUAL_MEMCPY, -- cgit 1.4.1-3-g733a5 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: Separate getting offsets and getting index expressions --- clippy_lints/src/loops.rs | 63 +++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0753b23e45b..75955997af2 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -837,7 +837,7 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option { +fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) -> Option { fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { @@ -849,38 +849,24 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v } } - if let ExprKind::Index(seqexpr, idx) = expr.kind { - let ty = cx.tables.expr_ty(seqexpr); - if !is_slice_like(cx, ty) { - return None; - } - - 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 - }; + 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 } } }) -- cgit 1.4.1-3-g733a5 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: Delay getting the snippet from slices --- clippy_lints/src/loops.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 75955997af2..8ab35556670 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -812,8 +812,8 @@ impl Offset { } } -struct FixedOffsetVar { - var_name: String, +struct FixedOffsetVar<'hir> { + var: &'hir Expr<'hir>, offset: Offset, } @@ -947,13 +947,13 @@ fn detect_manual_memcpy<'a, 'tcx>( } } - let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { + let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { if_chain! { if let ExprKind::MethodCall(method, _, len_args) = end.kind; if method.ident.name == sym!(len); if len_args.len() == 1; if let Some(arg) = len_args.get(0); - if snippet(cx, arg.span, "??") == var_name; + if var_def_id(cx, arg) == var_def_id(cx, var); then { match offset.sign { OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), @@ -986,14 +986,12 @@ fn detect_manual_memcpy<'a, 'tcx>( && is_slice_like(cx, cx.tables.expr_ty(seqexpr_right)); if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); - let var_name_left = snippet_opt(cx, seqexpr_left.span).unwrap_or_else(|| "???".into()); - let var_name_right = snippet_opt(cx, seqexpr_right.span).unwrap_or_else(|| "???".into()); // Source and destination must be different - if var_name_left != var_name_right; + if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right); then { - Some((FixedOffsetVar { var_name: var_name_left, offset: offset_left }, - FixedOffsetVar { var_name: var_name_right, offset: offset_right })) + Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left }, + FixedOffsetVar { var: seqexpr_right, offset: offset_right })) } else { None } @@ -1004,18 +1002,22 @@ fn detect_manual_memcpy<'a, 'tcx>( o.map(|(dst_var, src_var)| { let start_str = snippet(cx, start.span, "").to_string(); let dst_offset = print_offset(&start_str, &dst_var.offset); - let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name); + let dst_limit = print_limit(end, dst_var.offset, dst_var.var); let src_offset = print_offset(&start_str, &src_var.offset); - let src_limit = print_limit(end, src_var.offset, &src_var.var_name); + let src_limit = print_limit(end, src_var.offset, src_var.var); + + let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into()); + let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into()); + let dst = if dst_offset == "" && dst_limit == "" { - dst_var.var_name + dst_var_name } else { - format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit) + format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var.var_name, src_offset, src_limit + dst, src_var_name, src_offset, src_limit ) }) }) -- cgit 1.4.1-3-g733a5 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: Extract building the suggestion of `manual_memcpy` --- clippy_lints/src/loops.rs | 154 ++++++++++++++++++++++++---------------------- 1 file changed, 80 insertions(+), 74 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8ab35556670..7cf3e16bef9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -905,6 +905,85 @@ fn get_assignments<'a, 'tcx>( iter_a.into_iter().flatten().chain(iter_b.into_iter()) } +fn build_manual_memcpy_suggestion<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + start: &Expr<'_>, + end: &Expr<'_>, + limits: ast::RangeLimits, + dst_var: FixedOffsetVar<'_>, + src_var: FixedOffsetVar<'_>, +) -> String { + fn print_sum(arg1: &str, arg2: &Offset) -> String { + match (arg1, &arg2.value[..], arg2.sign) { + ("0", "0", _) => "0".into(), + ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), + ("0", x, OffsetSign::Negative) => format!("-{}", x), + (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), + (x, y, OffsetSign::Negative) => { + if x == y { + "0".into() + } else { + format!("({} - {})", x, y) + } + }, + } + } + + fn print_offset(start_str: &str, inline_offset: &Offset) -> String { + let offset = print_sum(start_str, inline_offset); + if offset.as_str() == "0" { + "".into() + } else { + offset + } + } + + let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if var_def_id(cx, arg) == var_def_id(cx, var); + then { + match offset.sign { + OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), + OffsetSign::Positive => "".into(), + } + } else { + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + format!("{}", end + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), + }; + + print_sum(&end_str, &offset) + } + } + }; + + let start_str = snippet(cx, start.span, "").to_string(); + let dst_offset = print_offset(&start_str, &dst_var.offset); + let dst_limit = print_limit(end, dst_var.offset, dst_var.var); + let src_offset = print_offset(&start_str, &src_var.offset); + let src_limit = print_limit(end, src_var.offset, src_var.var); + + let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into()); + let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into()); + + let dst = if dst_offset == "" && dst_limit == "" { + dst_var_name + } else { + format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) + }; + + format!( + "{}.clone_from_slice(&{}[{}..{}])", + dst, src_var_name, src_offset, src_limit + ) +} /// Checks for for loops that sequentially copy items from one slice-like /// object to another. fn detect_manual_memcpy<'a, 'tcx>( @@ -922,57 +1001,6 @@ fn detect_manual_memcpy<'a, 'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - fn print_sum(arg1: &str, arg2: &Offset) -> String { - match (arg1, &arg2.value[..], arg2.sign) { - ("0", "0", _) => "0".into(), - ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), - ("0", x, OffsetSign::Negative) => format!("-{}", x), - (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), - (x, y, OffsetSign::Negative) => { - if x == y { - "0".into() - } else { - format!("({} - {})", x, y) - } - }, - } - } - - fn print_offset(start_str: &str, inline_offset: &Offset) -> String { - let offset = print_sum(start_str, inline_offset); - if offset.as_str() == "0" { - "".into() - } else { - offset - } - } - - let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { - if_chain! { - if let ExprKind::MethodCall(method, _, len_args) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, var); - then { - match offset.sign { - OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), - OffsetSign::Positive => "".into(), - } - } else { - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&end_str, &offset) - } - } - }; - // The only statements in the for loops can be indexed assignments from // indexed retrievals. let big_sugg = get_assignments(body) @@ -998,29 +1026,7 @@ fn detect_manual_memcpy<'a, 'tcx>( } }) }) - .map(|o| { - o.map(|(dst_var, src_var)| { - let start_str = snippet(cx, start.span, "").to_string(); - let dst_offset = print_offset(&start_str, &dst_var.offset); - let dst_limit = print_limit(end, dst_var.offset, dst_var.var); - let src_offset = print_offset(&start_str, &src_var.offset); - let src_limit = print_limit(end, src_var.offset, src_var.var); - - let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into()); - let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into()); - - let dst = if dst_offset == "" && dst_limit == "" { - dst_var_name - } else { - format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) - }; - - format!( - "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var_name, src_offset, src_limit - ) - }) - }) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src))) .collect::>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); -- cgit 1.4.1-3-g733a5 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: Removed unused lifetimes and a needless bool --- clippy_lints/src/loops.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7cf3e16bef9..5f7f0897943 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -775,10 +775,9 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) - if let QPath::Resolved(None, path) = qpath; if path.segments.len() == 1; if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id); - // our variable! - if local_id == var; then { - true + // our variable! + local_id == var } else { false } @@ -870,10 +869,8 @@ fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) } } -fn get_assignments<'a, 'tcx>( - body: &'tcx Expr<'tcx>, -) -> impl Iterator, &'tcx Expr<'tcx>)>> { - fn get_assignment<'a, 'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { +fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator, &'tcx Expr<'tcx>)>> { + fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Assign(lhs, rhs, _) = e.kind { Some((lhs, rhs)) } else { -- cgit 1.4.1-3-g733a5 From 1afb6e6e3b23bd5555f34cc4dcd20349dfd789de Mon Sep 17 00:00:00 2001 From: Stanislav Tkach Date: Tue, 28 Apr 2020 12:08:38 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index adfd8dfb1c1..62ee051624b 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -24,8 +24,25 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```ignore - /// let { a: _, b: ref b, c: _ } = .. + /// ```rust + /// # struct Foo { + /// # a: i32, + /// # b: i32, + /// # c: i32, + /// # } + /// let f = Foo { a: 0, b: 0, c: 0 }; + /// + /// // Bad + /// match f { + /// Foo { a: _, b: 0, .. } => {}, + /// Foo { a: _, b: _, c: _ } => {}, + /// } + /// + /// // Good + /// match f { + /// Foo { b: 0, .. } => {}, + /// Foo { .. } => {}, + /// } /// ``` pub UNNEEDED_FIELD_PATTERN, restriction, -- cgit 1.4.1-3-g733a5 From f072ded3bf6286668ff8eade5b58e471dbe66f2a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 1 May 2020 01:44:17 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 575773580c0..facf363e371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1423,6 +1423,7 @@ Released 2018-09-13 [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c995be5edc2..64f3a8a0ebb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -247,6 +247,7 @@ mod literal_representation; mod loops; mod macro_use; mod main_recursion; +mod manual_non_exhaustive; mod map_clone; mod map_unit_fn; mod match_on_vec_items; @@ -628,6 +629,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_LET_ON_ITERATOR, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, + &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, &map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); + store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1280,6 +1283,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1474,6 +1478,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs new file mode 100644 index 00000000000..ca2a2cf2e1e --- /dev/null +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -0,0 +1,167 @@ +use crate::utils::{snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast::{Attribute, Ident, Item, ItemKind, StructField, TyKind, Variant, VariantData, VisibilityKind}; +use rustc_attr as attr; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. + /// + /// **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent + /// and allows possible optimizations when applied to enums. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct S { + /// pub a: i32, + /// pub b: i32, + /// _c: (), + /// } + /// + /// enum E { + /// A, + /// B, + /// #[doc(hidden)] + /// _C, + /// } + /// + /// struct T(pub i32, pub i32, ()); + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// struct S { + /// pub a: i32, + /// pub b: i32, + /// } + /// + /// #[non_exhaustive] + /// enum E { + /// A, + /// B, + /// } + /// + /// #[non_exhaustive] + /// struct T(pub i32, pub i32); + /// ``` + pub MANUAL_NON_EXHAUSTIVE, + style, + "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" +} + +declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); + +impl EarlyLintPass for ManualNonExhaustive { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + match &item.kind { + ItemKind::Enum(def, _) => { + check_manual_non_exhaustive_enum(cx, item, &def.variants); + }, + ItemKind::Struct(variant_data, _) => { + if let VariantData::Unit(..) = variant_data { + return; + } + + check_manual_non_exhaustive_struct(cx, item, variant_data); + }, + _ => {}, + } + } +} + +fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { + fn is_non_exhaustive_marker(variant: &Variant) -> bool { + matches!(variant.data, VariantData::Unit(_)) + && variant.ident.as_str().starts_with('_') + && variant.attrs.iter().any(|a| is_doc_hidden(a)) + } + + fn is_doc_hidden(attr: &Attribute) -> bool { + attr.check_name(sym!(doc)) + && match attr.meta_item_list() { + Some(l) => attr::list_contains_name(&l, sym!(hidden)), + None => false, + } + } + + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)).count(); + if markers == 1 && variants.len() > markers; + if let Some(variant) = variants.last(); + if is_non_exhaustive_marker(variant); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + item.span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + diag.span_help(variant.span, "and remove this variant"); + } + }); + } + } +} + +fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { + fn is_private(field: &StructField) -> bool { + matches!(field.vis.node, VisibilityKind::Inherited) + } + + fn is_non_exhaustive_marker(name: &Option) -> bool { + name.map(|n| n.as_str().starts_with('_')).unwrap_or(true) + } + + let fields = data.fields(); + let private_fields = fields.iter().filter(|f| is_private(f)).count(); + let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); + + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + if private_fields == 1 && public_fields >= private_fields && public_fields == fields.len() - 1; + if let Some(field) = fields.iter().find(|f| is_private(f)); + if is_non_exhaustive_marker(&field.ident); + if let TyKind::Tup(tup_fields) = &field.ty.kind; + if tup_fields.is_empty(); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + let delimiter = match data { + VariantData::Struct(..) => '{', + VariantData::Tuple(..) => '(', + _ => unreachable!(), + }; + let header_span = cx.sess.source_map().span_until_char(item.span, delimiter); + + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + item.span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + diag.span_help(field.span, "and remove this field"); + } + }); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 72675c25175..c5360002fa0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1088,6 +1088,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "loops", }, + Lint { + name: "manual_non_exhaustive", + group: "style", + desc: "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]", + deprecation: None, + module: "manual_non_exhaustive", + }, Lint { name: "manual_saturating_arithmetic", group: "style", diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive.rs new file mode 100644 index 00000000000..9c239db6e00 --- /dev/null +++ b/tests/ui/manual_non_exhaustive.rs @@ -0,0 +1,124 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // last variant does not have doc hidden attribute, should be ignored + enum NoDocHidden { + A, + B, + _C, + } + + // name of variant with doc hidden does not start with underscore, should be ignored + enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, + } + + // variant with doc hidden is not unit, should be ignored + enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), + } + + // variant with doc hidden is not the last one, should be ignored + enum NotLast { + A, + #[doc(hidden)] + _B, + C, + } + + // variant with doc hidden is the only one, should be ignored + enum OnlyMarker { + #[doc(hidden)] + _A, + } + + // variant with multiple non-exhaustive "markers", should be ignored + enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, + } + + // already non_exhaustive, should be ignored + #[non_exhaustive] + enum NonExhaustive { + A, + B, + } +} + +mod structs { + struct S { + pub a: i32, + pub b: i32, + _c: (), + } + + // some other fields are private, should be ignored + struct PrivateFields { + a: i32, + pub b: i32, + _c: (), + } + + // private field name does not start with underscore, should be ignored + struct NoUnderscore { + pub a: i32, + pub b: i32, + c: (), + } + + // private field is not unit type, should be ignored + struct NotUnit { + pub a: i32, + pub b: i32, + _c: i32, + } + + // private field is the only field, should be ignored + struct OnlyMarker { + _a: (), + } + + // already non exhaustive, should be ignored + #[non_exhaustive] + struct NonExhaustive { + pub a: i32, + pub b: i32, + } +} + +mod tuple_structs { + struct T(pub i32, pub i32, ()); + + // some other fields are private, should be ignored + struct PrivateFields(pub i32, i32, ()); + + // private field is not unit type, should be ignored + struct NotUnit(pub i32, pub i32, i32); + + // private field is the only field, should be ignored + struct OnlyMarker(()); + + // already non exhaustive, should be ignored + #[non_exhaustive] + struct NonExhaustive(pub i32, pub i32); +} + +fn main() {} diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive.stderr new file mode 100644 index 00000000000..d6719bca0d4 --- /dev/null +++ b/tests/ui/manual_non_exhaustive.stderr @@ -0,0 +1,48 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:5:5 + | +LL | / enum E { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ help: add the attribute: `#[non_exhaustive] enum E` + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: and remove this variant + --> $DIR/manual_non_exhaustive.rs:9:9 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:67:5 + | +LL | / struct S { +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ help: add the attribute: `#[non_exhaustive] struct S` + | +help: and remove this field + --> $DIR/manual_non_exhaustive.rs:70:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:108:5 + | +LL | struct T(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[non_exhaustive] struct T` + | +help: and remove this field + --> $DIR/manual_non_exhaustive.rs:108:32 + | +LL | struct T(pub i32, pub i32, ()); + | ^^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 42b0b4754c881101cefb0307c489d6159c19b2f3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 1 May 2020 22:37:14 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index ca2a2cf2e1e..a4273da1d74 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,10 +1,11 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, Ident, Item, ItemKind, StructField, TyKind, Variant, VariantData, VisibilityKind}; +use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -90,11 +91,9 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); - let markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)).count(); - if markers == 1 && variants.len() > markers; - if let Some(variant) = variants.last(); - if is_non_exhaustive_marker(variant); + let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); + if let Some(marker) = markers.next(); + if markers.count() == 0 && variants.len() > 1; then { span_lint_and_then( cx, @@ -102,17 +101,20 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let header_span = cx.sess.source_map().span_until_char(item.span, '{'); - - if let Some(snippet) = snippet_opt(cx, header_span) { - diag.span_suggestion( - item.span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - diag.span_help(variant.span, "and remove this variant"); + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + if let Some(snippet) = snippet_opt(cx, header_span); + then { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } } + diag.span_help(marker.span, "remove this variant"); }); } } @@ -123,8 +125,18 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: matches!(field.vis.node, VisibilityKind::Inherited) } - fn is_non_exhaustive_marker(name: &Option) -> bool { - name.map(|n| n.as_str().starts_with('_')).unwrap_or(true) + fn is_non_exhaustive_marker(field: &StructField) -> bool { + is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + } + + fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { + let delimiter = match data { + VariantData::Struct(..) => '{', + VariantData::Tuple(..) => '(', + _ => unreachable!("`VariantData::Unit` is already handled above"), + }; + + cx.sess.source_map().span_until_char(item.span, delimiter) } let fields = data.fields(); @@ -132,12 +144,8 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); - if private_fields == 1 && public_fields >= private_fields && public_fields == fields.len() - 1; - if let Some(field) = fields.iter().find(|f| is_private(f)); - if is_non_exhaustive_marker(&field.ident); - if let TyKind::Tup(tup_fields) = &field.ty.kind; - if tup_fields.is_empty(); + if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; + if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); then { span_lint_and_then( cx, @@ -145,22 +153,20 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - _ => unreachable!(), - }; - let header_span = cx.sess.source_map().span_until_char(item.span, delimiter); - - if let Some(snippet) = snippet_opt(cx, header_span) { - diag.span_suggestion( - item.span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - diag.span_help(field.span, "and remove this field"); + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let header_span = find_header_span(cx, item, data); + if let Some(snippet) = snippet_opt(cx, header_span); + then { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } } + diag.span_help(marker.span, "remove this field"); }); } } diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive.rs index 9c239db6e00..7a788f48520 100644 --- a/tests/ui/manual_non_exhaustive.rs +++ b/tests/ui/manual_non_exhaustive.rs @@ -9,7 +9,16 @@ mod enums { _C, } - // last variant does not have doc hidden attribute, should be ignored + // user forgot to remove the marker + #[non_exhaustive] + enum Ep { + A, + B, + #[doc(hidden)] + _C, + } + + // marker variant does not have doc hidden attribute, should be ignored enum NoDocHidden { A, B, @@ -32,21 +41,13 @@ mod enums { _C(bool), } - // variant with doc hidden is not the last one, should be ignored - enum NotLast { - A, - #[doc(hidden)] - _B, - C, - } - // variant with doc hidden is the only one, should be ignored enum OnlyMarker { #[doc(hidden)] _A, } - // variant with multiple non-exhaustive "markers", should be ignored + // variant with multiple markers, should be ignored enum MultipleMarkers { A, #[doc(hidden)] @@ -55,7 +56,7 @@ mod enums { _C, } - // already non_exhaustive, should be ignored + // already non_exhaustive and no markers, should be ignored #[non_exhaustive] enum NonExhaustive { A, @@ -70,6 +71,14 @@ mod structs { _c: (), } + // user forgot to remove the private field + #[non_exhaustive] + struct Sp { + pub a: i32, + pub b: i32, + _c: (), + } + // some other fields are private, should be ignored struct PrivateFields { a: i32, @@ -96,7 +105,7 @@ mod structs { _a: (), } - // already non exhaustive, should be ignored + // already non exhaustive and no private fields, should be ignored #[non_exhaustive] struct NonExhaustive { pub a: i32, @@ -107,6 +116,10 @@ mod structs { mod tuple_structs { struct T(pub i32, pub i32, ()); + // user forgot to remove the private field + #[non_exhaustive] + struct Tp(pub i32, pub i32, ()); + // some other fields are private, should be ignored struct PrivateFields(pub i32, i32, ()); @@ -116,7 +129,7 @@ mod tuple_structs { // private field is the only field, should be ignored struct OnlyMarker(()); - // already non exhaustive, should be ignored + // already non exhaustive and no private fields, should be ignored #[non_exhaustive] struct NonExhaustive(pub i32, pub i32); } diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive.stderr index d6719bca0d4..613c5e8ca1d 100644 --- a/tests/ui/manual_non_exhaustive.stderr +++ b/tests/ui/manual_non_exhaustive.stderr @@ -1,48 +1,103 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> $DIR/manual_non_exhaustive.rs:5:5 | -LL | / enum E { +LL | enum E { + | ^----- + | | + | _____help: add the attribute: `#[non_exhaustive] enum E` + | | LL | | A, LL | | B, LL | | #[doc(hidden)] LL | | _C, LL | | } - | |_____^ help: add the attribute: `#[non_exhaustive] enum E` + | |_____^ | = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: and remove this variant +help: remove this variant --> $DIR/manual_non_exhaustive.rs:9:9 | LL | _C, | ^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:67:5 + --> $DIR/manual_non_exhaustive.rs:14:5 | -LL | / struct S { +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive.rs:18:9 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:68:5 + | +LL | struct S { + | ^------- + | | + | _____help: add the attribute: `#[non_exhaustive] struct S` + | | +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:71:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:76:5 + | +LL | / struct Sp { LL | | pub a: i32, LL | | pub b: i32, LL | | _c: (), LL | | } - | |_____^ help: 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:79: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: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 -- cgit 1.4.1-3-g733a5 From 10e3f9bdb854e3cabbc4fda69ed713388344d524 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 1 May 2020 23:02:31 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c995be5edc2..06e21a5272e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,6 +1138,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), @@ -1283,7 +1284,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_OVERLAPPING_ARM), @@ -1647,7 +1647,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(&methods::CLONE_DOUBLE_REF), diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 4071406cc84..421571d2f2f 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// } /// ``` pub MATCH_ON_VEC_ITEMS, - correctness, + pedantic, "matching on vector elements can panic" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 72675c25175..f337db72ba0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1146,7 +1146,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "match_on_vec_items", - group: "correctness", + group: "pedantic", desc: "matching on vector elements can panic", deprecation: None, module: "match_on_vec_items", -- cgit 1.4.1-3-g733a5 From 350c17de24c0bc7ee1b17981fe02f88ca6ec50a4 Mon Sep 17 00:00:00 2001 From: ebroto Date: Fri, 1 May 2020 23:00:16 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index a4273da1d74..f3b8902e26f 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -133,7 +133,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: let delimiter = match data { VariantData::Struct(..) => '{', VariantData::Tuple(..) => '(', - _ => unreachable!("`VariantData::Unit` is already handled above"), + VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), }; cx.sess.source_map().span_until_char(item.span, delimiter) -- cgit 1.4.1-3-g733a5 From 72ce6d5be9c54775b847bc0641f8d909b2977126 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 24 Apr 2020 16:46:56 +0200 Subject: Fix unwrap lint when checks are done in parameters --- clippy_lints/src/unwrap.rs | 21 ++++++++++++----- tests/ui/checked_unwrap/simple_conditionals.rs | 27 ++++++++++++++++++++++ tests/ui/checked_unwrap/simple_conditionals.stderr | 24 +++++++++---------- 3 files changed, 54 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 5235c98efab..f3844c7d3b6 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,4 +1,7 @@ -use crate::utils::{higher::if_block, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated}; +use crate::utils::{ + differing_macro_contexts, higher::if_block, is_type_diagnostic_item, span_lint_and_then, + usage::is_potentially_mutated, +}; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp}; @@ -73,6 +76,8 @@ struct UnwrapInfo<'tcx> { ident: &'tcx Path<'tcx>, /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, + /// The branch where the check takes place, like `if x.is_ok() { .. }` + branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). safe_to_unwrap: bool, } @@ -82,19 +87,20 @@ struct UnwrapInfo<'tcx> { fn collect_unwrap_info<'a, 'tcx>( cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, + branch: &'tcx Expr<'_>, invert: bool, ) -> Vec> { if let ExprKind::Binary(op, left, right) = &expr.kind { match (invert, op.node) { (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => { - let mut unwrap_info = collect_unwrap_info(cx, left, invert); - unwrap_info.append(&mut collect_unwrap_info(cx, right, invert)); + let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert); + unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert)); return unwrap_info; }, _ => (), } } else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind { - return collect_unwrap_info(cx, expr, !invert); + return collect_unwrap_info(cx, expr, branch, !invert); } else { if_chain! { if let ExprKind::MethodCall(method_name, _, args) = &expr.kind; @@ -111,7 +117,7 @@ fn collect_unwrap_info<'a, 'tcx>( _ => unreachable!(), }; let safe_to_unwrap = unwrappable != invert; - return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }]; + return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }]; } } } @@ -121,7 +127,7 @@ fn collect_unwrap_info<'a, 'tcx>( impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) { let prev_len = self.unwrappables.len(); - for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) { + for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) { if is_potentially_mutated(unwrap_info.ident, cond, self.cx) || is_potentially_mutated(unwrap_info.ident, branch, self.cx) { @@ -158,6 +164,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { let call_to_unwrap = method_name.ident.name == sym!(unwrap); if let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.ident.res == path.res); + // Span contexts should not differ with the conditional branch + if !differing_macro_contexts(unwrappable.branch.span, expr.span); + if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span); then { if call_to_unwrap == unwrappable.safe_to_unwrap { span_lint_and_then( diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index b0fc26ff76d..3e7b4b390ba 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -9,6 +9,30 @@ macro_rules! m { }; } +macro_rules! checks_in_param { + ($a:expr, $b:expr) => { + if $a { + $b; + } + }; +} + +macro_rules! checks_unwrap { + ($a:expr, $b:expr) => { + if $a.is_some() { + $b; + } + }; +} + +macro_rules! checks_some { + ($a:expr, $b:expr) => { + if $a { + $b.unwrap(); + } + }; +} + fn main() { let x = Some(()); if x.is_some() { @@ -22,6 +46,9 @@ fn main() { x.unwrap(); // unnecessary } m!(x); + checks_in_param!(x.is_some(), x.unwrap()); // ok + checks_unwrap!(x, x.unwrap()); // ok + checks_some!(x.is_some(), x); // ok let mut x: Result<(), ()> = Ok(()); if x.is_ok() { x.unwrap(); // unnecessary diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index e40542e2e4f..4013d1ed667 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,5 +1,5 @@ error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:15:9 + --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { | ----------- the check is happening here @@ -13,7 +13,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:17:9 + --> $DIR/simple_conditionals.rs:41:9 | LL | if x.is_some() { | ----------- because of this check @@ -28,7 +28,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:20:9 + --> $DIR/simple_conditionals.rs:44:9 | LL | if x.is_none() { | ----------- because of this check @@ -36,7 +36,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:22:9 + --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { | ----------- the check is happening here @@ -58,7 +58,7 @@ LL | m!(x); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:27:9 + --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { | --------- the check is happening here @@ -66,7 +66,7 @@ LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ error: This call to `unwrap_err()` will always panic. - --> $DIR/simple_conditionals.rs:28:9 + --> $DIR/simple_conditionals.rs:55:9 | LL | if x.is_ok() { | --------- because of this check @@ -75,7 +75,7 @@ LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:30:9 + --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { | --------- because of this check @@ -84,7 +84,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:31:9 + --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { | --------- the check is happening here @@ -93,7 +93,7 @@ LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:34:9 + --> $DIR/simple_conditionals.rs:61:9 | LL | if x.is_err() { | ---------- because of this check @@ -101,7 +101,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:35:9 + --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { | ---------- the check is happening here @@ -110,7 +110,7 @@ LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:37:9 + --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { | ---------- the check is happening here @@ -119,7 +119,7 @@ LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ error: This call to `unwrap_err()` will always panic. - --> $DIR/simple_conditionals.rs:38:9 + --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { | ---------- because of this check -- cgit 1.4.1-3-g733a5 From d0c1f8ada2306801f2a6ce193e1f9f75471dbb3c Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 2 May 2020 14:18:27 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 04b4b423761..1c7b40fa908 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -933,6 +933,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Exp } /// Returns `true` if a pattern is refutable. +// TODO: should be implemented using rustc/mir_build/hair machinery pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool { fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool { matches!( @@ -946,27 +947,34 @@ pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool { } match pat.kind { - PatKind::Binding(..) | PatKind::Wild => false, + PatKind::Wild => false, + PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat), PatKind::Lit(..) | PatKind::Range(..) => true, PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), - PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), + PatKind::Or(ref pats) => { + // TODO: should be the honest check, that pats is exhaustive set + are_refutable(cx, pats.iter().map(|pat| &**pat)) + }, + PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), PatKind::Struct(ref qpath, ref fields, _) => { - if is_enum_variant(cx, qpath, pat.hir_id) { - true - } else { - are_refutable(cx, fields.iter().map(|field| &*field.pat)) - } + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) }, PatKind::TupleStruct(ref qpath, ref pats, _) => { - if is_enum_variant(cx, qpath, pat.hir_id) { - true - } else { - are_refutable(cx, pats.iter().map(|pat| &**pat)) - } + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat)) }, PatKind::Slice(ref head, ref middle, ref tail) => { - are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)) + match &cx.tables.node_type(pat.hir_id).kind { + ty::Slice(..) => { + // [..] is the only irrefutable slice pattern. + !head.is_empty() || middle.is_none() || !tail.is_empty() + }, + ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)), + _ => { + // unreachable!() + true + }, + } }, } } diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index f5fcabf63fd..e99c98ac79f 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -2,6 +2,7 @@ #![warn(clippy::while_let_on_iterator)] #![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![feature(or_patterns)] fn base() { let mut iter = 1..20; @@ -77,6 +78,62 @@ fn refutable() { // */ } +fn refutable2() { + // Issue 3780 + { + let v = vec![1, 2, 3]; + let mut it = v.windows(2); + while let Some([x, y]) = it.next() { + println!("x: {}", x); + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([x, ..]) = it.next() { + println!("x: {}", x); + } + + let mut it = v.windows(2); + while let Some([.., y]) = it.next() { + println!("y: {}", y); + } + + let mut it = v.windows(2); + for [..] in it {} + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some([1]) = it.next() {} + + let mut it = v.iter(); + for [_x] in it {} + } + + // binding + { + let v = vec![1, 2, 3]; + let mut it = v.iter(); + while let Some(x @ 1) = it.next() { + println!("{}", x); + } + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + for x @ [_] in it { + println!("{:?}", x); + } + } + + // false negative + { + let v = vec![1, 2, 3]; + let mut it = v.iter().map(Some); + while let Some(Some(_) | None) = it.next() { + println!("1"); + } + } +} + fn nested_loops() { let a = [42, 1337]; let mut y = a.iter(); @@ -152,6 +209,7 @@ fn issue1654() { fn main() { base(); refutable(); + refutable2(); nested_loops(); issue1121(); issue2965(); diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 04dce8a0289..ba13172428e 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -2,6 +2,7 @@ #![warn(clippy::while_let_on_iterator)] #![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![feature(or_patterns)] fn base() { let mut iter = 1..20; @@ -77,6 +78,62 @@ fn refutable() { // */ } +fn refutable2() { + // Issue 3780 + { + let v = vec![1, 2, 3]; + let mut it = v.windows(2); + while let Some([x, y]) = it.next() { + println!("x: {}", x); + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([x, ..]) = it.next() { + println!("x: {}", x); + } + + let mut it = v.windows(2); + while let Some([.., y]) = it.next() { + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([..]) = it.next() {} + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some([1]) = it.next() {} + + let mut it = v.iter(); + while let Some([_x]) = it.next() {} + } + + // binding + { + let v = vec![1, 2, 3]; + let mut it = v.iter(); + while let Some(x @ 1) = it.next() { + println!("{}", x); + } + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some(x @ [_]) = it.next() { + println!("{:?}", x); + } + } + + // false negative + { + let v = vec![1, 2, 3]; + let mut it = v.iter().map(Some); + while let Some(Some(_) | None) = it.next() { + println!("1"); + } + } +} + fn nested_loops() { let a = [42, 1337]; let mut y = a.iter(); @@ -152,6 +209,7 @@ fn issue1654() { fn main() { base(); refutable(); + refutable2(); nested_loops(); issue1121(); issue2965(); diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 6de138d7227..aa980d9965c 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:8:5 + --> $DIR/while_let_on_iterator.rs:9:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,22 +7,40 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:13:5 + --> $DIR/while_let_on_iterator.rs:14:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:18:5 + --> $DIR/while_let_on_iterator.rs:19:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:97:9 + --> $DIR/while_let_on_iterator.rs:102:9 + | +LL | while let Some([..]) = it.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:109:9 + | +LL | while let Some([_x]) = it.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:122:9 + | +LL | while let Some(x @ [_]) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:154:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` -error: aborting due to 4 previous errors +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From e7138e06291d13163e8ab2a57fe2465451fac193 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 2 May 2020 14:25:45 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 421571d2f2f..236362130e5 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource}; @@ -75,10 +75,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems { fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Index(ref array, _) = expr.kind; - let ty = cx.tables.expr_ty(array); - let ty = walk_ptrs_ty(ty); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)); + if let ExprKind::Index(ref array, ref index) = expr.kind; + if is_vector(cx, array); + if !is_full_range(cx, index); then { return Some(expr); @@ -87,3 +86,15 @@ fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) None } + +fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + let ty = walk_ptrs_ty(ty); + is_type_diagnostic_item(cx, ty, sym!(vec_type)) +} + +fn is_full_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + let ty = walk_ptrs_ty(ty); + match_type(cx, ty, &["core", "ops", "range", "RangeFull"]) +} diff --git a/tests/ui/match_on_vec_items.rs b/tests/ui/match_on_vec_items.rs index 0bb39d77e46..30415e3b94d 100644 --- a/tests/ui/match_on_vec_items.rs +++ b/tests/ui/match_on_vec_items.rs @@ -120,6 +120,27 @@ fn match_with_array() { } } +fn match_with_endless_range() { + let arr = vec![0, 1, 2, 3]; + let range = ..; + + // Ok + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + [0, 1, 2, 3] => println!("0, 1, 2, 3"), + _ => {}, + } + + // Ok + match arr[..] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + [0, 1, 2, 3] => println!("0, 1, 2, 3"), + _ => {}, + } +} + fn main() { match_with_wildcard(); match_without_wildcard(); @@ -127,4 +148,5 @@ fn main() { match_vec_ref(); match_with_get(); match_with_array(); + match_with_endless_range(); } -- cgit 1.4.1-3-g733a5 From de58c5644de0d9ffe46e7e2923d2301eaf4dd347 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 2 May 2020 17:36:26 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 236362130e5..ee69628e9f0 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{self, is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource}; @@ -96,5 +96,5 @@ fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { fn is_full_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { let ty = cx.tables.expr_ty(expr); let ty = walk_ptrs_ty(ty); - match_type(cx, ty, &["core", "ops", "range", "RangeFull"]) + match_type(cx, ty, &utils::paths::RANGE_FULL) } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index ca2c4ade155..79ca6845c6f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -85,7 +85,7 @@ pub const RANGE: [&str; 3] = ["core", "ops", "Range"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"]; pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"]; -pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"]; +pub const RANGE_FULL: [&str; 4] = ["core", "ops", "range", "RangeFull"]; pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"]; pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"]; pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"]; -- cgit 1.4.1-3-g733a5 From 7744cf4e53c4e19e5753e2063be420b9d0c0d38a Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 4 May 2020 15:13:07 +0200 Subject: Update to rustc changes --- clippy_lints/src/redundant_clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index d5cace0c647..d563eb886ae 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -591,7 +591,7 @@ struct PossibleBorrowerMap<'a, 'tcx> { impl PossibleBorrowerMap<'_, '_> { /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after(at); + self.maybe_live.seek_after_primary_effect(at); self.bitset.0.clear(); let maybe_live = &mut self.maybe_live; -- cgit 1.4.1-3-g733a5 From 3b58d66b22fe9107a78b99c267c71322276aa15a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 21:41:23 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index facf363e371..8457d6ad05c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1422,6 +1422,7 @@ Released 2018-09-13 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97785cba883..fb2e9932b8e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -247,6 +247,7 @@ mod literal_representation; mod loops; mod macro_use; mod main_recursion; +mod manual_async_fn; mod manual_non_exhaustive; mod map_clone; mod map_unit_fn; @@ -629,6 +630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_LET_ON_ITERATOR, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, + &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, &map_unit_fn::OPTION_MAP_UNIT_FN, @@ -1067,6 +1069,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); + store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1284,6 +1287,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1478,6 +1482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs new file mode 100644 index 00000000000..ab8829abbf2 --- /dev/null +++ b/clippy_lints/src/manual_async_fn.rs @@ -0,0 +1,160 @@ +use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD}; +use crate::utils::{match_function_call, match_path, snippet_block, snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, + ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** It checks for manual implementations of `async` functions. + /// + /// **Why is this bad?** It's more idiomatic to use the dedicated syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::future::Future; + /// + /// fn foo() -> Future { async { 42 } } + /// ``` + /// Use instead: + /// ```rust + /// use std::future::Future; + /// + /// async fn foo() -> i32 { 42 } + /// ``` + pub MANUAL_ASYNC_FN, + style, + "manual implementations of `async` functions can be simplified using the dedicated syntax" +} + +declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + _: HirId, + ) { + if_chain! { + if let Some(header) = kind.header(); + if let IsAsync::NotAsync = header.asyncness; + // Check that this function returns `impl Future` + if let FnRetTy::Return(ret_ty) = decl.output; + if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some(output) = future_output_ty(trait_ref); + // Check that the body of the function consists of one async block + if let ExprKind::Block(block, _) = body.value.kind; + if block.stmts.is_empty(); + if let Some(closure_body) = desugared_async_block(cx, block); + then { + let header_span = span.with_hi(ret_ty.span.hi()); + + span_lint_and_then( + cx, + MANUAL_ASYNC_FN, + header_span, + "this function can be simplified using async syntax", + |diag| { + if_chain! { + if let Some(header_snip) = snippet_opt(cx, header_span); + if let Some(ret_pos) = header_snip.rfind("->"); + if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); + then { + let help = format!("make the function `async` and {}", ret_sugg); + diag.span_suggestion( + header_span, + &help, + format!("async {}{}", &header_snip[..ret_pos], ret_snip), + Applicability::MachineApplicable + ); + + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); + diag.span_suggestion( + block.span, + "move the body of the async block to the enclosing function", + body_snip.to_string(), + Applicability::MachineApplicable + ); + } + } + }, + ); + } + } + } +} + +fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { + if_chain! { + if let TyKind::Def(item_id, _) = ty.kind; + let item = cx.tcx.hir().item(item_id.id); + if let ItemKind::OpaqueTy(opaque) = &item.kind; + if opaque.bounds.len() == 1; + if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; + let path = poly.trait_ref.path; + if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD); + then { + return Some(&poly.trait_ref); + } + } + + None +} + +fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { + if_chain! { + if let Some(segment) = trait_ref.path.segments.last(); + if let Some(args) = segment.args; + if args.bindings.len() == 1; + let binding = &args.bindings[0]; + if binding.ident.as_str() == "Output"; + if let TypeBindingKind::Equality{ty: output} = binding.kind; + then { + return Some(output) + } + } + + None +} + +fn desugared_async_block<'tcx>(cx: &LateContext<'_, 'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { + if_chain! { + if let Some(block_expr) = block.expr; + if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + if args.len() == 1; + if let Expr{kind: ExprKind::Closure(_, _, body_id, ..), ..} = args[0]; + let closure_body = cx.tcx.hir().body(body_id); + if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind; + then { + return Some(closure_body); + } + } + + None +} + +fn suggested_ret(cx: &LateContext<'_, '_>, output: &Ty<'_>) -> Option<(&'static str, String)> { + match output.kind { + TyKind::Tup(tys) if tys.is_empty() => { + let sugg = "remove the return type"; + Some((sugg, "".into())) + }, + _ => { + let sugg = "return the output of the future directly"; + snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip))) + }, + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 79ca6845c6f..74040a96c78 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,6 +42,9 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; +pub const FUTURE_CORE: [&str; 3] = ["core", "future", "Future"]; +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; +pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"]; pub const HASH: [&str; 2] = ["hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7685f67211..51d1cb2216a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1081,6 +1081,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "main_recursion", }, + Lint { + name: "manual_async_fn", + group: "style", + desc: "manual implementations of `async` functions can be simplified using the dedicated syntax", + deprecation: None, + module: "manual_async_fn", + }, Lint { name: "manual_memcpy", group: "perf", diff --git a/tests/ui/future_not_send.rs b/tests/ui/future_not_send.rs index 6d09d71a630..d3a920de4b6 100644 --- a/tests/ui/future_not_send.rs +++ b/tests/ui/future_not_send.rs @@ -41,6 +41,7 @@ impl Dummy { self.private_future().await; } + #[allow(clippy::manual_async_fn)] pub fn public_send(&self) -> impl std::future::Future { async { false } } diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index 3b4968ef0a6..d1863701bfe 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -96,13 +96,13 @@ LL | } = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:49:37 + --> $DIR/future_not_send.rs:50:37 | LL | async fn generic_future(t: T) -> T | ^ future returned by `generic_future` is not `Send` | note: future is not `Send` as this value is used across an await - --> $DIR/future_not_send.rs:54:5 + --> $DIR/future_not_send.rs:55:5 | LL | let rt = &t; | -- has type `&T` which is not `Send` @@ -114,7 +114,7 @@ LL | } = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:65:34 + --> $DIR/future_not_send.rs:66:34 | LL | async fn unclear_future(t: T) {} | ^ diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed new file mode 100644 index 00000000000..84c02bba4dc --- /dev/null +++ b/tests/ui/manual_async_fn.fixed @@ -0,0 +1,55 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +async fn fut() -> i32 { 42 } + +async fn empty_fut() {} + +async fn core_fut() -> i32 { 42 } + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + async fn inh_fut() -> i32 { 42 } + + async fn meth_fut(&self) -> i32 { 42 } + + async fn empty_fut(&self) {} + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs new file mode 100644 index 00000000000..bd5f9d1cf5f --- /dev/null +++ b/tests/ui/manual_async_fn.rs @@ -0,0 +1,67 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +fn fut() -> impl Future { + async { 42 } +} + +fn empty_fut() -> impl Future { + async {} +} + +fn core_fut() -> impl core::future::Future { + async move { 42 } +} + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + fn inh_fut() -> impl Future { + async { 42 } + } + + fn meth_fut(&self) -> impl Future { + async { 42 } + } + + fn empty_fut(&self) -> impl Future { + async {} + } + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr new file mode 100644 index 00000000000..016fdf95977 --- /dev/null +++ b/tests/ui/manual_async_fn.stderr @@ -0,0 +1,93 @@ +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:8:1 + | +LL | fn fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-async-fn` implied by `-D warnings` +help: make the function `async` and return the output of the future directly + | +LL | async fn fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut() -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:12:1 + | +LL | fn empty_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut() { + | ^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut() -> impl Future {} + | ^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:16:1 + | +LL | fn core_fut() -> impl core::future::Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn core_fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn core_fut() -> impl core::future::Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:38:5 + | +LL | fn inh_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn inh_fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn inh_fut() -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:42:5 + | +LL | fn meth_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn meth_fut(&self) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn meth_fut(&self) -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:46:5 + | +LL | fn empty_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut(&self) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut(&self) -> impl Future {} + | ^^ + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 4ac348b30849ec472564a81797b7e9ae42985460 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 22:03:38 +0200 Subject: Fix doc comment in lint declaration --- clippy_lints/src/manual_async_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index ab8829abbf2..b4be3ecfd16 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ```rust /// use std::future::Future; /// - /// fn foo() -> Future { async { 42 } } + /// fn foo() -> impl Future { async { 42 } } /// ``` /// Use instead: /// ```rust -- cgit 1.4.1-3-g733a5 From 3e4bc026e2706b1cb21bad6d55726f8b5a1d4cf1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 22:40:28 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index b4be3ecfd16..cb72a240582 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ -use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD}; -use crate::utils::{match_function_call, match_path, snippet_block, snippet_opt, span_lint_and_then}; +use crate::utils::paths::FUTURE_FROM_GENERATOR; +use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -66,7 +66,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn { cx, MANUAL_ASYNC_FN, header_span, - "this function can be simplified using async syntax", + "this function can be simplified using the `async fn` syntax", |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); @@ -104,8 +104,7 @@ fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Opt if let ItemKind::OpaqueTy(opaque) = &item.kind; if opaque.bounds.len() == 1; if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - let path = poly.trait_ref.path; - if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD); + if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { return Some(&poly.trait_ref); } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 74040a96c78..b3ad2ad9d99 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,9 +42,7 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; -pub const FUTURE_CORE: [&str; 3] = ["core", "future", "Future"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"]; pub const HASH: [&str; 2] = ["hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 84c02bba4dc..6bb1032a172 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -29,7 +29,19 @@ async fn already_async() -> impl Future { struct S {} impl S { - async fn inh_fut() -> i32 { 42 } + async fn inh_fut() -> i32 { + // NOTE: this code is here just to check that the identation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } async fn meth_fut(&self) -> i32 { 42 } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index bd5f9d1cf5f..d50c919188b 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -36,7 +36,19 @@ async fn already_async() -> impl Future { struct S {} impl S { fn inh_fut() -> impl Future { - async { 42 } + async { + // NOTE: this code is here just to check that the identation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } } fn meth_fut(&self) -> impl Future { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index 016fdf95977..f278ee41aa3 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -1,4 +1,4 @@ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:8:1 | LL | fn fut() -> impl Future { @@ -14,7 +14,7 @@ help: move the body of the async block to the enclosing function LL | fn fut() -> impl Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:12:1 | LL | fn empty_fut() -> impl Future { @@ -29,7 +29,7 @@ help: move the body of the async block to the enclosing function LL | fn empty_fut() -> impl Future {} | ^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:16:1 | LL | fn core_fut() -> impl core::future::Future { @@ -44,7 +44,7 @@ help: move the body of the async block to the enclosing function LL | fn core_fut() -> impl core::future::Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:38:5 | LL | fn inh_fut() -> impl Future { @@ -56,11 +56,16 @@ LL | async fn inh_fut() -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn inh_fut() -> impl Future { 42 } - | ^^^^^^ +LL | fn inh_fut() -> impl Future { +LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | let a = 42; +LL | let b = 21; +LL | if a < b { +LL | let c = 21; + ... -error: this function can be simplified using async syntax - --> $DIR/manual_async_fn.rs:42:5 +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:54:5 | LL | fn meth_fut(&self) -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,8 +79,8 @@ help: move the body of the async block to the enclosing function LL | fn meth_fut(&self) -> impl Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax - --> $DIR/manual_async_fn.rs:46:5 +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:58:5 | LL | fn empty_fut(&self) -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 0c14ea8ed79ebf0b7368659282136e876f247cc9 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sun, 3 May 2020 10:56:25 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index f3038861cee..70ad9a60a02 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -3,7 +3,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ def::{DefKind, Res}, - Item, ItemKind, UseKind, + Item, ItemKind, PathSegment, UseKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -83,8 +83,8 @@ impl LateLintPass<'_, '_> for WildcardImports { if_chain! { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - // don't lint prelude glob imports - if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude"); + if !is_prelude_import(use_path.segments); + if !is_super_only_import_in_test(use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -154,3 +154,16 @@ impl LateLintPass<'_, '_> for WildcardImports { } } } + +// Allow "...prelude::*" imports. +// Many crates have a prelude, and it is imported as a glob by design. +fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { + segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude") +} + +// Allow "super::*" imports. +// This is intended primarily to ease the process of writing unit tests. +fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { + segments.iter().len() == 1 && + segments.first().map_or(false, |ps| ps.ident.as_str() == "super") +} diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ed6cc00ef04..003f11009a0 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -155,3 +155,41 @@ fn test_weird_formatting() { exported(); foo(); } + +mod test_super_imports { + fn foofoo() {} + + mod use_super { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit { + use test_super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit { + use super::super::test_super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index c6d6efaece8..7bd57c7965a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -156,3 +156,41 @@ fn test_weird_formatting() { exported(); foo(); } + +mod test_super_imports { + fn foofoo() {} + + mod use_super { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit { + use test_super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit { + use super::super::test_super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 050e4c6304f..858dc28797f 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -92,5 +92,23 @@ LL | use crate:: fn_mod:: LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` -error: aborting due to 15 previous errors +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:172:13 + | +LL | use test_super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:181:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:190:13 + | +LL | use super::super::test_super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From bdc75dbb7bbdc379b1f8cc346151fac4e63d7deb Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sun, 3 May 2020 11:18:10 -0700 Subject: Run `cargo dev fmt` --- clippy_lints/src/wildcard_imports.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 70ad9a60a02..e7400642b07 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -158,12 +158,14 @@ impl LateLintPass<'_, '_> for WildcardImports { // Allow "...prelude::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude") + segments + .iter() + .last() + .map_or(false, |ps| ps.ident.as_str() == "prelude") } // Allow "super::*" imports. // This is intended primarily to ease the process of writing unit tests. fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { - segments.iter().len() == 1 && - segments.first().map_or(false, |ps| ps.ident.as_str() == "super") + segments.iter().len() == 1 && segments.first().map_or(false, |ps| ps.ident.as_str() == "super") } -- cgit 1.4.1-3-g733a5 From 56f4e1c3a8be54c1c80de366618e83d8d7a6e9eb Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 08:06:30 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7400642b07..a22d0e6775d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -84,7 +84,7 @@ impl LateLintPass<'_, '_> for WildcardImports { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if !is_prelude_import(use_path.segments); - if !is_super_only_import_in_test(use_path.segments); + if !(is_super_only_import(use_path.segments) && is_in_test_module(cx, item)); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -109,8 +109,7 @@ impl LateLintPass<'_, '_> for WildcardImports { span = use_path.span.with_hi(item.span.hi() - BytePos(1)); } ( - span, - false, + span, false, ) }; @@ -164,8 +163,14 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { .map_or(false, |ps| ps.ident.as_str() == "prelude") } -// Allow "super::*" imports. -// This is intended primarily to ease the process of writing unit tests. -fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { - segments.iter().len() == 1 && segments.first().map_or(false, |ps| ps.ident.as_str() == "super") +// Allow "super::*" imports in tests. +fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { + segments.len() == 1 && segments[0].ident.as_str() == "super" +} + +fn is_in_test_module(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { + let parent = cx.tcx.hir().get_parent_node(item.hir_id); + let parent_item = cx.tcx.hir().expect_item(parent); + let parent_name = parent_item.ident.name.as_str(); + parent_name.contains("test") } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 003f11009a0..1c5c01f65d1 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -159,7 +159,15 @@ fn test_weird_formatting() { mod test_super_imports { fn foofoo() {} - mod use_super { + mod use_super_should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_super_in_test_should_pass { use super::*; fn with_super() { @@ -167,7 +175,7 @@ mod test_super_imports { } } - mod use_explicit { + mod use_explicit_should_be_replaced { use test_super_imports::foofoo; fn with_explicit() { @@ -175,7 +183,7 @@ mod test_super_imports { } } - mod use_double_super { + mod use_double_super_should_be_replaced { mod inner { use super::super::foofoo; @@ -185,7 +193,7 @@ mod test_super_imports { } } - mod use_super_explicit { + mod use_super_explicit_should_be_replaced { use super::super::test_super_imports::foofoo; fn with_super_explicit() { diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 7bd57c7965a..f783149ef93 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -160,7 +160,7 @@ fn test_weird_formatting() { mod test_super_imports { fn foofoo() {} - mod use_super { + mod use_super_should_be_replaced { use super::*; fn with_super() { @@ -168,7 +168,15 @@ mod test_super_imports { } } - mod use_explicit { + mod use_super_in_test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit_should_be_replaced { use test_super_imports::*; fn with_explicit() { @@ -176,7 +184,7 @@ mod test_super_imports { } } - mod use_double_super { + mod use_double_super_should_be_replaced { mod inner { use super::super::*; @@ -186,7 +194,7 @@ mod test_super_imports { } } - mod use_super_explicit { + mod use_super_explicit_should_be_replaced { use super::super::test_super_imports::*; fn with_super_explicit() { diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 858dc28797f..649d550a88d 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -93,22 +93,28 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:164:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:180:13 | LL | use test_super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:181:17 + --> $DIR/wildcard_imports.rs:189:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:190:13 + --> $DIR/wildcard_imports.rs:198:13 | LL | use super::super::test_super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From ad92486d52fdede5c712dd27c868c942d8240704 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 14:41:54 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb2e9932b8e..4b67c84e38e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1058,7 +1058,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_struct_bools = conf.max_struct_bools; store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)); store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap); - store.register_late_pass(|| box wildcard_imports::WildcardImports); + let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; + store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)); store.register_early_pass(|| box macro_use::MacroUseImports); store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4b81ff33495..57b9eafd14d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -158,6 +158,8 @@ define_Conf! { (max_struct_bools, "max_struct_bools": u64, 3), /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have (max_fn_params_bools, "max_fn_params_bools": u64, 3), + /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). + (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), } impl Default for Conf { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index a22d0e6775d..43d0d1b9e96 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -6,7 +6,7 @@ use rustc_hir::{ Item, ItemKind, PathSegment, UseKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::BytePos; declare_clippy_lint! { @@ -73,18 +73,38 @@ declare_clippy_lint! { "lint `use _::*` statements" } -declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); +#[derive(Default)] +pub struct WildcardImports { + warn_on_all: bool, + is_test_module: bool, + test_modules_deep: u32, +} + +impl WildcardImports { + pub fn new(warn_on_all: bool) -> Self { + Self { + warn_on_all, + is_test_module: false, + test_modules_deep: 0, + } + } +} + +impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_, '_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { return; } + if is_test_module(item) { + self.is_test_module = true; + self.test_modules_deep += 1; + } if_chain! { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if !is_prelude_import(use_path.segments); - if !(is_super_only_import(use_path.segments) && is_in_test_module(cx, item)); + if self.warn_on_all || !self.check_exceptions(use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -152,6 +172,19 @@ impl LateLintPass<'_, '_> for WildcardImports { } } } + + fn check_item_post(&mut self, _: &LateContext<'_, '_>, _: &Item<'_>) { + if self.is_test_module { + self.is_test_module = false; + self.test_modules_deep -= 1; + } + } +} + +impl WildcardImports { + fn check_exceptions(&self, segments: &[PathSegment<'_>]) -> bool { + is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + } } // Allow "...prelude::*" imports. @@ -168,9 +201,6 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.as_str() == "super" } -fn is_in_test_module(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { - let parent = cx.tcx.hir().get_parent_node(item.hir_id); - let parent_item = cx.tcx.hir().expect_item(parent); - let parent_name = parent_item.ident.name.as_str(); - parent_name.contains("test") +fn is_test_module(item: &Item<'_>) -> bool { + item.ident.name.as_str().contains("test") } diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 18f5d994ba8..53970af4107 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 1c5c01f65d1..98bf6acfe55 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -156,10 +156,10 @@ fn test_weird_formatting() { foo(); } -mod test_super_imports { +mod super_imports { fn foofoo() {} - mod use_super_should_be_replaced { + mod should_be_replaced { use super::foofoo; fn with_super() { @@ -167,7 +167,7 @@ mod test_super_imports { } } - mod use_super_in_test_should_pass { + mod test_should_pass { use super::*; fn with_super() { @@ -175,8 +175,15 @@ mod test_super_imports { } } + mod inner { + fn test_should_pass() { + use super::*; + let _ = foofoo(); + } + } + mod use_explicit_should_be_replaced { - use test_super_imports::foofoo; + use super_imports::foofoo; fn with_explicit() { let _ = foofoo(); @@ -194,7 +201,7 @@ mod test_super_imports { } mod use_super_explicit_should_be_replaced { - use super::super::test_super_imports::foofoo; + use super::super::super_imports::foofoo; fn with_super_explicit() { let _ = foofoo(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index f783149ef93..9275c5a0928 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -157,10 +157,10 @@ fn test_weird_formatting() { foo(); } -mod test_super_imports { +mod super_imports { fn foofoo() {} - mod use_super_should_be_replaced { + mod should_be_replaced { use super::*; fn with_super() { @@ -168,7 +168,7 @@ mod test_super_imports { } } - mod use_super_in_test_should_pass { + mod test_should_pass { use super::*; fn with_super() { @@ -176,8 +176,15 @@ mod test_super_imports { } } + mod inner { + fn test_should_pass() { + use super::*; + let _ = foofoo(); + } + } + mod use_explicit_should_be_replaced { - use test_super_imports::*; + use super_imports::*; fn with_explicit() { let _ = foofoo(); @@ -195,7 +202,7 @@ mod test_super_imports { } mod use_super_explicit_should_be_replaced { - use super::super::test_super_imports::*; + use super::super::super_imports::*; fn with_super_explicit() { let _ = foofoo(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 649d550a88d..bd000ce8161 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -99,22 +99,22 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:180:13 + --> $DIR/wildcard_imports.rs:187:13 | -LL | use test_super_imports::*; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` +LL | use super_imports::*; + | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:189:17 + --> $DIR/wildcard_imports.rs:196:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:198:13 + --> $DIR/wildcard_imports.rs:205:13 | -LL | use super::super::test_super_imports::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From a42a2bdac2a6c881f85ebdbce66e84d977c74cfa Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 14:48:27 -0700 Subject: Also have flag disable macro check --- clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 43d0d1b9e96..843ddda0356 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -102,7 +102,7 @@ impl LateLintPass<'_, '_> for WildcardImports { self.test_modules_deep += 1; } if_chain! { - if !in_macro(item.span); + if self.warn_on_all || !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if self.warn_on_all || !self.check_exceptions(use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); -- cgit 1.4.1-3-g733a5 From 152cdcb45be7a8f0f24dbcd4177e0858d94516b6 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Fri, 8 May 2020 18:22:27 -0700 Subject: Remove unnecessary field, check for Mod/Fn ItemKind --- clippy_lints/src/wildcard_imports.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 843ddda0356..48405a00d55 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -76,7 +76,6 @@ declare_clippy_lint! { #[derive(Default)] pub struct WildcardImports { warn_on_all: bool, - is_test_module: bool, test_modules_deep: u32, } @@ -84,7 +83,6 @@ impl WildcardImports { pub fn new(warn_on_all: bool) -> Self { Self { warn_on_all, - is_test_module: false, test_modules_deep: 0, } } @@ -97,8 +95,7 @@ impl LateLintPass<'_, '_> for WildcardImports { if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { return; } - if is_test_module(item) { - self.is_test_module = true; + if is_test_module_or_function(item) { self.test_modules_deep += 1; } if_chain! { @@ -173,9 +170,8 @@ impl LateLintPass<'_, '_> for WildcardImports { } } - fn check_item_post(&mut self, _: &LateContext<'_, '_>, _: &Item<'_>) { - if self.is_test_module { - self.is_test_module = false; + fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) { + if is_test_module_or_function(item) { self.test_modules_deep -= 1; } } @@ -201,6 +197,6 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.as_str() == "super" } -fn is_test_module(item: &Item<'_>) -> bool { - item.ident.name.as_str().contains("test") +fn is_test_module_or_function(item: &Item<'_>) -> bool { + matches!(item.kind, ItemKind::Fn(..) | ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") } -- cgit 1.4.1-3-g733a5 From 4db6abcd50eb07a7fa8349a059f80792b7b19a2e Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 08:04:07 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 48405a00d55..e12a6659ab5 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -43,9 +43,14 @@ declare_clippy_lint! { /// /// This can lead to confusing error messages at best and to unexpected behavior at worst. /// - /// Note that this will not warn about wildcard imports from modules named `prelude`; many - /// crates (including the standard library) provide modules named "prelude" specifically - /// designed for wildcard import. + /// **Exceptions:** + /// + /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) + /// provide modules named "prelude" specifically designed for wildcard import. + /// + /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. + /// + /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. /// /// **Known problems:** If macros are imported through the wildcard, this macro is not included /// by the suggestion and has to be added by hand. @@ -198,5 +203,5 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { } fn is_test_module_or_function(item: &Item<'_>) -> bool { - matches!(item.kind, ItemKind::Fn(..) | ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 98bf6acfe55..b47c8f23e24 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -175,13 +175,34 @@ mod super_imports { } } - mod inner { - fn test_should_pass() { + mod test_should_pass_inside_function { + fn with_super_inside_function() { use super::*; let _ = foofoo(); } } + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_futher_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { use super_imports::foofoo; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 9275c5a0928..3ad1a29aeba 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -176,13 +176,33 @@ mod super_imports { } } - mod inner { - fn test_should_pass() { + mod test_should_pass_inside_function { + fn with_super_inside_function() { use super::*; let _ = foofoo(); } } + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_futher_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + mod use_explicit_should_be_replaced { use super_imports::*; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index bd000ce8161..de07bd1d69b 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -99,22 +99,28 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:187:13 + --> $DIR/wildcard_imports.rs:199:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:208:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:196:17 + --> $DIR/wildcard_imports.rs:217:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:205:13 + --> $DIR/wildcard_imports.rs:226:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors -- cgit 1.4.1-3-g733a5 From 0ba61c612ee873314d252ca1f747c14a2f0161ba Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 10:14:29 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e12a6659ab5..2c4e24780e7 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -101,12 +101,11 @@ impl LateLintPass<'_, '_> for WildcardImports { return; } if is_test_module_or_function(item) { - self.test_modules_deep += 1; + self.test_modules_deep = self.test_modules_deep.saturating_add(1); } if_chain! { - if self.warn_on_all || !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if self.warn_on_all || !self.check_exceptions(use_path.segments); + if self.warn_on_all || !self.check_exceptions(item, use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -177,14 +176,16 @@ impl LateLintPass<'_, '_> for WildcardImports { fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) { if is_test_module_or_function(item) { - self.test_modules_deep -= 1; + self.test_modules_deep = self.test_modules_deep.saturating_sub(1); } } } impl WildcardImports { - fn check_exceptions(&self, segments: &[PathSegment<'_>]) -> bool { - is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { + in_macro(item.span) + || is_prelude_import(segments) + || (is_super_only_import(segments) && self.test_modules_deep > 0) } } diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index de07bd1d69b..fab43b738eb 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -105,19 +105,19 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:208:13 + --> $DIR/wildcard_imports.rs:207:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:217:17 + --> $DIR/wildcard_imports.rs:216:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:226:13 + --> $DIR/wildcard_imports.rs:225:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -- cgit 1.4.1-3-g733a5 From b69200b8468434bc3f5b9ef8468733e5d40f4e01 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 10:16:47 -0700 Subject: Move is_test_module check to top of function --- clippy_lints/src/wildcard_imports.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2c4e24780e7..32d9a45c37d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -97,12 +97,12 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_, '_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { - if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { - return; - } if is_test_module_or_function(item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } + if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { + return; + } if_chain! { if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if self.warn_on_all || !self.check_exceptions(item, use_path.segments); -- cgit 1.4.1-3-g733a5 From 8ab3224b3b273f4911943800c56dc4aa925bc4c5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 May 2020 13:57:01 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 91d3e47d787..278d043732f 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -3,12 +3,13 @@ use crate::utils::{ span_lint_and_sugg, walk_ptrs_ty, }; use if_chain::if_chain; -use rustc_ast::ast::{Name, UintTy}; +use rustc_ast::ast::{UintTy}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Symbol; declare_clippy_lint! { /// **What it does:** Checks for naive byte counts @@ -95,11 +96,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount { } } -fn check_arg(name: Name, arg: Name, needle: &Expr<'_>) -> bool { +fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { name == arg && !contains_name(name, needle) } -fn get_path_name(expr: &Expr<'_>) -> Option { +fn get_path_name(expr: &Expr<'_>) -> Option { match expr.kind { ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => { get_path_name(e) diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 1ebfb3c8162..475610dda47 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -2,11 +2,12 @@ use crate::utils::span_lint_and_then; use crate::utils::sugg::DiagnosticBuilderExt; -use rustc_ast::ast::{Attribute, Name}; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Symbol; declare_clippy_lint! { /// **What it does:** Checks for `#[inline]` on trait methods without bodies @@ -38,7 +39,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InlineFnWithoutBody { } } -fn check_attrs(cx: &LateContext<'_, '_>, name: Name, attrs: &[Attribute]) { +fn check_attrs(cx: &LateContext<'_, '_>, name: Symbol, attrs: &[Attribute]) { for attr in attrs { if !attr.check_name(sym!(inline)) { continue; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 1d86ca9696f..2ec0b5a8d6f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,5 +1,5 @@ use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; -use rustc_ast::ast::{LitKind, Name}; +use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -7,7 +7,7 @@ use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, Ite use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::source_map::{Span, Spanned, Symbol}; declare_clippy_lint! { /// **What it does:** Checks for getting the length of something via `.len()` @@ -226,7 +226,7 @@ fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr fn check_len( cx: &LateContext<'_, '_>, span: Span, - method_name: Name, + method_name: Symbol, args: &[Expr<'_>], lit: &LitKind, op: &str, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4b67c84e38e..51b5401da7d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -335,7 +335,7 @@ mod zero_div_zero; pub use crate::utils::conf::Conf; mod reexport { - pub use rustc_ast::ast::Name; + pub use rustc_span::Symbol as Name; } /// Register all pre expansion lints diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 0b346393ac3..0163b3f8dbc 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -3,14 +3,14 @@ use crate::utils::{ is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg, }; use if_chain::if_chain; -use rustc_ast::ast::Ident; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 45809b35986..2b51b732075 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,13 +1,13 @@ use crate::utils::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ - Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Ident, Item, ItemKind, Local, MacCall, Pat, PatKind, + Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind, }; use rustc_ast::attr; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::{Ident, SymbolStr}; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 86c469a4dcc..735800e7e74 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -1,9 +1,9 @@ use crate::utils::span_lint; -use rustc_ast::ast::{Ident, Item, ItemKind, UseTree, UseTreeKind}; +use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::{Ident, SymbolStr}; declare_clippy_lint! { /// **What it does:** Checks for imports that remove "unsafe" from an item's diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 02b721fd378..bd7da57c665 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -1,6 +1,5 @@ use crate::consts::{constant_context, constant_simple}; use crate::utils::differing_macro_contexts; -use rustc_ast::ast::Name; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::{ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg, @@ -10,6 +9,7 @@ use rustc_hir::{ use rustc_lint::LateContext; use rustc_middle::ich::StableHashingContextProvider; use rustc_middle::ty::TypeckTables; +use rustc_span::Symbol; use std::hash::Hash; /// Type used to check whether two ast are the same. This is different from the @@ -544,7 +544,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } - pub fn hash_name(&mut self, n: Name) { + pub fn hash_name(&mut self, n: Symbol) { n.as_str().hash(&mut self.s); } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 5bf9acdc5f7..8e1b047f6f8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -4,7 +4,7 @@ use crate::utils::{ span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, }; use if_chain::if_chain; -use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, Name, NodeId}; +use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -17,7 +17,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::{Symbol, SymbolStr}; use std::borrow::{Borrow, Cow}; @@ -245,8 +245,8 @@ impl EarlyLintPass for ClippyLintsInternal { #[derive(Clone, Debug, Default)] pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, + declared_lints: FxHashMap, + registered_lints: FxHashSet, } impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]); @@ -357,7 +357,7 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool { } struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, + output: &'a mut FxHashSet, cx: &'a LateContext<'a, 'tcx>, } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 1c7b40fa908..3b8ef18bfab 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1077,7 +1077,7 @@ pub fn is_allowed(cx: &LateContext<'_, '_>, lint: &'static Lint, id: HirId) -> b cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow } -pub fn get_arg_name(pat: &Pat<'_>) -> Option { +pub fn get_arg_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ident, None) => Some(ident.name), PatKind::Ref(ref subpat, _) => get_arg_name(subpat), diff --git a/clippy_lints/src/utils/ptr.rs b/clippy_lints/src/utils/ptr.rs index 240bf2449cb..fb6bd5e8158 100644 --- a/clippy_lints/src/utils/ptr.rs +++ b/clippy_lints/src/utils/ptr.rs @@ -1,10 +1,9 @@ use crate::utils::{get_pat_name, match_var, snippet}; -use rustc_ast::ast::Name; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use rustc_span::source_map::Span; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; pub fn get_spans( @@ -25,7 +24,7 @@ pub fn get_spans( fn extract_clone_suggestions<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, - name: Name, + name: Symbol, replace: &[(&'static str, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { @@ -46,7 +45,7 @@ fn extract_clone_suggestions<'a, 'tcx>( struct PtrCloneVisitor<'a, 'tcx> { cx: &'a LateContext<'a, 'tcx>, - name: Name, + name: Symbol, replace: &'a [(&'static str, &'static str)], spans: Vec<(Span, Cow<'static, str>)>, abort: bool, @@ -83,6 +82,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { } } -fn get_binding_name(arg: &Param<'_>) -> Option { +fn get_binding_name(arg: &Param<'_>) -> Option { get_pat_name(&arg.pat) } diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index c14da6aacea..e8535677987 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,5 +1,4 @@ use crate::utils::match_var; -use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -8,7 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_middle::ty; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. @@ -78,7 +77,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { } pub struct UsedVisitor { - pub var: ast::Name, // var to look for + pub var: Symbol, // var to look for pub used: bool, // has the var been used otherwise? } -- cgit 1.4.1-3-g733a5 From 505280b108f777ae941d4056a87f77fb7f513a7e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 11 May 2020 21:31:01 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 278d043732f..90c00ad098f 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -3,7 +3,7 @@ use crate::utils::{ span_lint_and_sugg, walk_ptrs_ty, }; use if_chain::if_chain; -use rustc_ast::ast::{UintTy}; +use rustc_ast::ast::UintTy; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 0163b3f8dbc..d5adf6b0f0d 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -9,8 +9,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index e8535677987..904d948ad29 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -77,8 +77,8 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { } pub struct UsedVisitor { - pub var: Symbol, // var to look for - pub used: bool, // has the var been used otherwise? + pub var: Symbol, // var to look for + pub used: bool, // has the var been used otherwise? } impl<'tcx> Visitor<'tcx> for UsedVisitor { -- cgit 1.4.1-3-g733a5 From f20b96277397db2c9021d06cf8647014ccdc0a39 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 01:04:16 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 5c9117d5b81..35464f629c3 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -248,28 +248,7 @@ impl EarlyLintPass for Return { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { - let (rspan, appl) = if let Ok(fn_source) = - cx.sess().source_map() - .span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable) - } else { - (ty.span, Applicability::MaybeIncorrect) - } - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - rspan, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + lint_unneeded_unit_return(cx, ty, span); } } } @@ -313,6 +292,22 @@ impl EarlyLintPass for Return { _ => (), } } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } } fn attr_is_cfg(attr: &ast::Attribute) -> bool { @@ -337,3 +332,28 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { false } } + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + if let Some(rpos) = fn_source.rfind("->") { + #[allow(clippy::cast_possible_truncation)] + ( + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + } else { + (ty.span, Applicability::MaybeIncorrect) + } + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 3f63624720f..07f2791786d 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -14,11 +14,10 @@ struct Unitter; impl Unitter { - // try to disorient the lint with multiple unit returns and newlines #[allow(clippy::no_effect)] - pub fn get_unit (), G>(&self, f: F, _g: G) - where G: Fn() -> () { - let _y: &dyn Fn() -> () = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } @@ -30,6 +29,20 @@ impl Into<()> for Unitter { } } +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() ; +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() {} +} + fn return_unit() { } #[allow(clippy::needless_return)] diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 8fc072ebd69..e2c6afb020f 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -14,10 +14,8 @@ struct Unitter; impl Unitter { - // try to disorient the lint with multiple unit returns and newlines #[allow(clippy::no_effect)] - pub fn get_unit (), G>(&self, f: F, _g: G) -> - () + pub fn get_unit (), G>(&self, f: F, _g: G) -> () where G: Fn() -> () { let _y: &dyn Fn() -> () = &f; (); // this should not lint, as it's not in return type position @@ -31,6 +29,20 @@ impl Into<()> for Unitter { } } +trait Trait { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> (); +} + +impl Trait for Unitter { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> () {} +} + fn return_unit() -> () { () } #[allow(clippy::needless_return)] diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index a013d2b3495..81e6738e6bf 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,10 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:59 + --> $DIR/unused_unit.rs:18:29 | -LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> - | ___________________________________________________________^ -LL | | () - | |__________^ help: remove the `-> ()` +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -13,40 +11,94 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:29:19 + --> $DIR/unused_unit.rs:19:19 + | +LL | where G: Fn() -> () { + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:18:59 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:20:27 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:27:19 | LL | fn into(self) -> () { | ^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:30:9 + --> $DIR/unused_unit.rs:28:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:18 + --> $DIR/unused_unit.rs:33:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:35:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:36:17 + | +LL | H: Fn() -> (); + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:40:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:42:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:43:17 + | +LL | H: Fn() -> () {} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:46:18 | LL | fn return_unit() -> () { () } | ^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:34:26 + --> $DIR/unused_unit.rs:46:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:44:14 + --> $DIR/unused_unit.rs:56:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:46:11 + --> $DIR/unused_unit.rs:58:11 | LL | return(); | ^^ help: remove the `()` -error: aborting due to 7 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 8ffa0bfaa2452eb9c80bf0f1909b039efc8dd0c3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 00:52:33 +0200 Subject: 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 ++++ tests/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 8457d6ad05c..33b277fbd31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1546,6 +1546,7 @@ Released 2018-09-13 [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51b5401da7d..e1cb10a4651 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -770,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_MINUS_ONE, &ranges::RANGE_PLUS_ONE, &ranges::RANGE_ZIP_WITH_LEN, + &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, @@ -1384,6 +1385,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&question_mark::QUESTION_MARK), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), @@ -1675,6 +1677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index d7ce2e66d69..86d55ccabb6 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,14 +1,17 @@ +use crate::consts::{constant, Constant}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use std::cmp::Ordering; use crate::utils::sugg::Sugg; +use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; use crate::utils::{higher, SpanlessEq}; -use crate::utils::{is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for zipping a collection with the range of @@ -84,10 +87,44 @@ declare_clippy_lint! { "`x..=(y-1)` reads better as `x..y`" } +declare_clippy_lint! { + /// **What it does:** Checks for range expressions `x..y` where both `x` and `y` + /// are constant and `x` is greater or equal to `y`. + /// + /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op. + /// Moreover, trying to use a reversed range to index a slice will panic at run-time. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// (10..=0).for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[3..1]; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// (0..=10).rev().for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[1..3]; + /// } + /// ``` + pub REVERSED_EMPTY_RANGES, + correctness, + "reversing the limits of range expressions, resulting in empty ranges" +} + declare_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, - RANGE_MINUS_ONE + RANGE_MINUS_ONE, + REVERSED_EMPTY_RANGES, ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { @@ -124,6 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { check_exclusive_range_plus_one(cx, expr); check_inclusive_range_minus_one(cx, expr); + check_reversed_empty_range(cx, expr); } } @@ -202,6 +240,76 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } } +fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::Index(..), + .. + }) + ) + } + + fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { + match limits { + RangeLimits::HalfOpen => ordering != Ordering::Less, + RangeLimits::Closed => ordering == Ordering::Greater, + } + } + + if_chain! { + if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr); + let ty = cx.tables.expr_ty(start); + if let ty::Int(_) | ty::Uint(_) = ty.kind; + if let Some((start_idx, _)) = constant(cx, cx.tables, start); + if let Some((end_idx, _)) = constant(cx, cx.tables, end); + if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); + if is_empty_range(limits, ordering); + then { + if inside_indexing_expr(cx, expr) { + let (reason, outcome) = if ordering == Ordering::Equal { + ("empty", "always yield an empty slice") + } else { + ("reversed", "panic at run-time") + }; + + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + &format!("this range is {} and using it to index a slice will {}", reason, outcome), + ); + } else { + span_lint_and_then( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is empty so it will yield no values", + |diag| { + if ordering != Ordering::Equal { + let start_snippet = snippet(cx, start.span, "_"); + let end_snippet = snippet(cx, end.span, "_"); + let dots = match limits { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=" + }; + + diag.span_suggestion( + expr.span, + "consider using the following if you are attempting to iterate over this \ + range in reverse", + format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + Applicability::MaybeIncorrect, + ); + } + }, + ); + } + } + } +} + fn y_plus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { match expr.kind { ExprKind::Binary( diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed new file mode 100644 index 00000000000..ee2cbc3cf54 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (21..=42).rev().for_each(|x| println!("{}", x)); + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in (-42..=-21).rev() {} + for _ in (21u32..42u32).rev() {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs new file mode 100644 index 00000000000..6ed5ca6daa0 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (42..=21).for_each(|x| println!("{}", x)); + let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in -21..=-42 {} + for _ in 42u32..21u32 {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr new file mode 100644 index 00000000000..97933b8ff85 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -0,0 +1,47 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + | +LL | (42..=21).for_each(|x| println!("{}", x)); + | ^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | (21..=42).rev().for_each(|x| println!("{}", x)); + | ^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + | +LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:10:14 + | +LL | for _ in -21..=-42 {} + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (-42..=-21).rev() {} + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + | +LL | for _ in 42u32..21u32 {} + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (21u32..42u32).rev() {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs new file mode 100644 index 00000000000..c9ca4c47668 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -0,0 +1,15 @@ +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; +const SOME_NUM: usize = 3; + +fn main() { + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[3usize..=1usize]; + let _ = &arr[SOME_NUM..1]; + let _ = &arr[3..3]; + + for _ in ANSWER..ANSWER {} +} diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr new file mode 100644 index 00000000000..12e5483ecdf --- /dev/null +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -0,0 +1,34 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 + | +LL | let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:10:18 + | +LL | let _ = &arr[3usize..=1usize]; + | ^^^^^^^^^^^^^^^ + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:11:18 + | +LL | let _ = &arr[SOME_NUM..1]; + | ^^^^^^^^^^^ + +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 + | +LL | let _ = &arr[3..3]; + | ^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + | +LL | for _ in ANSWER..ANSWER {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 0f2b1193f9501ffd06f9bf2ea8ab85a4db92f47b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 00:53:31 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 33b277fbd31..b25ef049356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1545,7 +1545,6 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used -[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e1cb10a4651..0c4daeb731f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -624,7 +624,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_COLLECT, &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, - &loops::REVERSE_RANGE_LOOP, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, @@ -1284,7 +1283,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_COLLECT), LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1658,7 +1656,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::FOR_LOOP_OVER_RESULT), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), @@ -1788,6 +1785,10 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { "unsafe_vector_initialization", "the replacement suggested by this lint had substantially different behavior", ); + store.register_removed( + "reverse_range_loop", + "this lint is now included in reversed_empty_ranges", + ); } /// Register renamed lints. diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2bbf4dba614..0bc6b70855b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use crate::consts::constant; use crate::reexport::Name; use crate::utils::paths; use crate::utils::usage::{is_unused, mutated_variables}; @@ -8,7 +8,7 @@ use crate::utils::{ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg}; +use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -270,30 +270,6 @@ declare_clippy_lint! { "collecting an iterator when collect is not needed" } -declare_clippy_lint! { - /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y` - /// are constant and `x` is greater or equal to `y`, unless the range is - /// reversed or has a negative `.step_by(_)`. - /// - /// **Why is it bad?** Such loops will either be skipped or loop until - /// wrap-around (in debug code, this may `panic!()`). Both options are probably - /// not intended. - /// - /// **Known problems:** The lint cannot catch loops over dynamically defined - /// ranges. Doing this would require simulating all possible inputs and code - /// paths through the program, which would be complex and error-prone. - /// - /// **Example:** - /// ```ignore - /// for x in 5..10 - 5 { - /// .. - /// } // oops, stray `-` - /// ``` - pub REVERSE_RANGE_LOOP, - correctness, - "iteration over an empty range, such as `10..0` or `5..5`" -} - declare_clippy_lint! { /// **What it does:** Checks `for` loops over slices with an explicit counter /// and suggests the use of `.enumerate()`. @@ -463,7 +439,6 @@ declare_lint_pass!(Loops => [ FOR_LOOP_OVER_OPTION, WHILE_LET_LOOP, NEEDLESS_COLLECT, - REVERSE_RANGE_LOOP, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -761,7 +736,6 @@ fn check_for_loop<'a, 'tcx>( expr: &'tcx Expr<'_>, ) { check_for_loop_range(cx, pat, arg, body, expr); - check_for_loop_reverse_range(cx, arg, expr); check_for_loop_arg(cx, pat, arg, expr); check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); @@ -1248,78 +1222,6 @@ fn is_end_eq_array_len<'tcx>( false } -fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - // if this for loop is iterating over a two-sided range... - if let Some(higher::Range { - start: Some(start), - end: Some(end), - limits, - }) = higher::range(cx, arg) - { - // ...and both sides are compile-time constant integers... - if let Some((start_idx, _)) = constant(cx, cx.tables, start) { - if let Some((end_idx, _)) = constant(cx, cx.tables, end) { - // ...and the start index is greater than the end index, - // this loop will never run. This is often confusing for developers - // who think that this will iterate from the larger value to the - // smaller value. - let ty = cx.tables.expr_ty(start); - let (sup, eq) = match (start_idx, end_idx) { - (Constant::Int(start_idx), Constant::Int(end_idx)) => ( - match ty.kind { - ty::Int(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity), - ty::Uint(_) => start_idx > end_idx, - _ => false, - }, - start_idx == end_idx, - ), - _ => (false, false), - }; - - if sup { - let start_snippet = snippet(cx, start.span, "_"); - let end_snippet = snippet(cx, end.span, "_"); - let dots = if limits == ast::RangeLimits::Closed { - "..=" - } else { - ".." - }; - - span_lint_and_then( - cx, - REVERSE_RANGE_LOOP, - expr.span, - "this range is empty so this for loop will never run", - |diag| { - diag.span_suggestion( - arg.span, - "consider using the following if you are attempting to iterate over this \ - range in reverse", - format!( - "({end}{dots}{start}).rev()", - end = end_snippet, - dots = dots, - start = start_snippet - ), - Applicability::MaybeIncorrect, - ); - }, - ); - } else if eq && limits != ast::RangeLimits::Closed { - // if they are equal, it's also problematic - this loop - // will never run. - span_lint( - cx, - REVERSE_RANGE_LOOP, - expr.span, - "this range is empty so this for loop will never run", - ); - } - } - } - } -} - fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { let mut applicability = Applicability::MachineApplicable; let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 51d1cb2216a..e1a6d4bdd31 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1922,11 +1922,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "reverse_range_loop", + name: "reversed_empty_ranges", group: "correctness", - desc: "iteration over an empty range, such as `10..0` or `5..5`", + desc: "reversing the limits of range expressions, resulting in empty ranges", deprecation: None, - module: "loops", + module: "ranges", }, Lint { name: "same_functions_in_if_condition", diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index 5fc84ada9ef..249a88a0b39 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -21,7 +21,6 @@ impl Unrelated { clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -32,61 +31,8 @@ impl Unrelated { )] #[allow(clippy::many_single_char_names, unused_variables)] fn main() { - const MAX_LEN: usize = 42; let mut vec = vec![1, 2, 3, 4]; - for i in (0..10).rev() { - println!("{}", i); - } - - for i in (0..=10).rev() { - println!("{}", i); - } - - for i in (0..MAX_LEN).rev() { - println!("{}", i); - } - - for i in 5..=5 { - // not an error, this is the range with only one element “5” - println!("{}", i); - } - - for i in 0..10 { - // not an error, the start index is less than the end index - println!("{}", i); - } - - for i in -10..0 { - // not an error - println!("{}", i); - } - - for i in (10..0).map(|x| x * 2) { - // not an error, it can't be known what arbitrary methods do to a range - println!("{}", i); - } - - // testing that the empty range lint folds constants - for i in (5 + 4..10).rev() { - println!("{}", i); - } - - for i in ((3 - 1)..(5 + 2)).rev() { - println!("{}", i); - } - - for i in (2 * 2)..(2 * 3) { - // no error, 4..6 is fine - println!("{}", i); - } - - let x = 42; - for i in x..10 { - // no error, not constant-foldable - println!("{}", i); - } - // See #601 for i in 0..10 { // no error, id_col does not exist outside the loop diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 4165b0dc004..306d85a6351 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -21,7 +21,6 @@ impl Unrelated { clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -32,61 +31,8 @@ impl Unrelated { )] #[allow(clippy::many_single_char_names, unused_variables)] fn main() { - const MAX_LEN: usize = 42; let mut vec = vec![1, 2, 3, 4]; - for i in 10..0 { - println!("{}", i); - } - - for i in 10..=0 { - println!("{}", i); - } - - for i in MAX_LEN..0 { - println!("{}", i); - } - - for i in 5..=5 { - // not an error, this is the range with only one element “5” - println!("{}", i); - } - - for i in 0..10 { - // not an error, the start index is less than the end index - println!("{}", i); - } - - for i in -10..0 { - // not an error - println!("{}", i); - } - - for i in (10..0).map(|x| x * 2) { - // not an error, it can't be known what arbitrary methods do to a range - println!("{}", i); - } - - // testing that the empty range lint folds constants - for i in 10..5 + 4 { - println!("{}", i); - } - - for i in (5 + 2)..(3 - 1) { - println!("{}", i); - } - - for i in (2 * 2)..(2 * 3) { - // no error, 4..6 is fine - println!("{}", i); - } - - let x = 42; - for i in x..10 { - // no error, not constant-foldable - println!("{}", i); - } - // See #601 for i in 0..10 { // no error, id_col does not exist outside the loop diff --git a/tests/ui/for_loop_fixable.stderr b/tests/ui/for_loop_fixable.stderr index cffb4b9f0a9..ddfe66d675f 100644 --- a/tests/ui/for_loop_fixable.stderr +++ b/tests/ui/for_loop_fixable.stderr @@ -1,61 +1,5 @@ -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:38:14 - | -LL | for i in 10..0 { - | ^^^^^ - | - = note: `-D clippy::reverse-range-loop` implied by `-D warnings` -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..10).rev() { - | ^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:42:14 - | -LL | for i in 10..=0 { - | ^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..=10).rev() { - | ^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:46:14 - | -LL | for i in MAX_LEN..0 { - | ^^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..MAX_LEN).rev() { - | ^^^^^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:71:14 - | -LL | for i in 10..5 + 4 { - | ^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (5 + 4..10).rev() { - | ^^^^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:75:14 - | -LL | for i in (5 + 2)..(3 - 1) { - | ^^^^^^^^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in ((3 - 1)..(5 + 2)).rev() { - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:97:15 + --> $DIR/for_loop_fixable.rs:43:15 | LL | for _v in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -63,13 +7,13 @@ LL | for _v in vec.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:99:15 + --> $DIR/for_loop_fixable.rs:45:15 | LL | for _v in vec.iter_mut() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:102:15 + --> $DIR/for_loop_fixable.rs:48:15 | LL | for _v in out_vec.into_iter() {} | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` @@ -77,76 +21,76 @@ LL | for _v in out_vec.into_iter() {} = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:107:15 + --> $DIR/for_loop_fixable.rs:53:15 | LL | for _v in [1, 2, 3].iter() {} | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:111:15 + --> $DIR/for_loop_fixable.rs:57:15 | LL | for _v in [0; 32].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:116:15 + --> $DIR/for_loop_fixable.rs:62:15 | LL | for _v in ll.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&ll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:119:15 + --> $DIR/for_loop_fixable.rs:65:15 | LL | for _v in vd.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&vd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:122:15 + --> $DIR/for_loop_fixable.rs:68:15 | LL | for _v in bh.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bh` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:125:15 + --> $DIR/for_loop_fixable.rs:71:15 | LL | for _v in hm.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hm` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:128:15 + --> $DIR/for_loop_fixable.rs:74:15 | LL | for _v in bt.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bt` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:131:15 + --> $DIR/for_loop_fixable.rs:77:15 | LL | for _v in hs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:134:15 + --> $DIR/for_loop_fixable.rs:80:15 | LL | for _v in bs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bs` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:309:18 + --> $DIR/for_loop_fixable.rs:255:18 | LL | for i in iterator.into_iter() { | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:329:18 + --> $DIR/for_loop_fixable.rs:275:18 | LL | for _ in t.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:331:18 + --> $DIR/for_loop_fixable.rs:277:18 | LL | for _ in r.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 20 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index 179b255e08c..e73536052f0 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -5,7 +5,6 @@ clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -16,25 +15,8 @@ unused, dead_code )] -#[allow(clippy::many_single_char_names, unused_variables)] fn main() { - for i in 5..5 { - println!("{}", i); - } - let vec = vec![1, 2, 3, 4]; for _v in vec.iter().next() {} - - for i in (5 + 2)..(8 - 1) { - println!("{}", i); - } - - const ZERO: usize = 0; - - for i in ZERO..vec.len() { - if f(&vec[i], &vec[i]) { - panic!("at the disco"); - } - } } diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index 1da8e0f3588..1c9287b6acb 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,9 +1,10 @@ -error[E0425]: cannot find function `f` in this scope - --> $DIR/for_loop_unfixable.rs:36:12 +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loop_unfixable.rs:21:15 | -LL | if f(&vec[i], &vec[i]) { - | ^ help: a local variable with a similar name exists: `i` +LL | for _v in vec.iter().next() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-next-loop` implied by `-D warnings` error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 9c24d6d4db1..0083f94798f 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -104,7 +104,7 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { dst[i - 0] = src[i]; } - #[allow(clippy::reverse_range_loop)] + #[allow(clippy::reversed_empty_ranges)] for i in 0..0 { dst[i] = src[i]; } -- cgit 1.4.1-3-g733a5 From e4cd8e7961b553cac44671d63bc6dfc2223ea66b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 12 May 2020 22:05:56 +0200 Subject: Fix ICE caused in unwrap module --- clippy_lints/src/unwrap.rs | 12 ++++++++++-- tests/ui/checked_unwrap/simple_conditionals.rs | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index f3844c7d3b6..8b971e7064b 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnO use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -90,6 +91,14 @@ fn collect_unwrap_info<'a, 'tcx>( branch: &'tcx Expr<'_>, invert: bool, ) -> Vec> { + fn is_relevant_option_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name) + } + + fn is_relevant_result_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name) + } + if let ExprKind::Binary(op, left, right) = &expr.kind { match (invert, op.node) { (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => { @@ -106,9 +115,8 @@ fn collect_unwrap_info<'a, 'tcx>( if let ExprKind::MethodCall(method_name, _, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind; let ty = cx.tables.expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) || is_type_diagnostic_item(cx, ty, sym!(result_type)); let name = method_name.ident.as_str(); - if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name); + if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name); then { assert!(args.len() == 1); let unwrappable = match name.as_ref() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 3e7b4b390ba..49794e0c241 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -78,3 +78,24 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern } + +mod issue_5579 { + trait IsErr { + fn is_err(&self, err: &str) -> bool; + } + + impl IsErr for Option { + fn is_err(&self, _err: &str) -> bool { + true + } + } + + #[allow(unused)] + fn boom() { + let t = Some(1); + + if t.is_err("") { + t.unwrap(); + } + } +} -- cgit 1.4.1-3-g733a5 From 671c1e34cc11767aa4ea257b9f5c40dcee1441fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 21:07:13 +0200 Subject: Avoid running doctest that is expected to panic --- clippy_lints/src/ranges.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 86d55ccabb6..83c6faac041 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -98,7 +98,7 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust + /// ```rust,no_run /// fn main() { /// (10..=0).for_each(|x| println!("{}", x)); /// -- cgit 1.4.1-3-g733a5 From 9217675c7f6ccd2efa18047ebb0c86841683b6a5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 14 May 2020 00:26:09 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 96df3ffe3ce..93e29edcaa5 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -81,12 +81,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain { // Check that both sets of operands are equal let mut spanless_eq = SpanlessEq::new(cx); - if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2)) - && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2)) - { + let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2); + let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2); + + if !same_fixed_operands && !same_transposed_operands { return; } + // Check that if the operation is the same, either it's not `==` or the operands are transposed + if kind1.node == kind2.node { + if kind1.node == BinOpKind::Eq { + return; + } + if !same_transposed_operands { + return; + } + } + // Check that the type being compared implements `core::cmp::Ord` let ty = cx.tables.expr_ty(lhs1); let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 9c2128469de..3b03f8c7dfe 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -137,4 +137,70 @@ fn h(x: T, y: T, z: T) { } } +// The following uses should be ignored +mod issue_5212 { + use super::{a, b, c}; + fn foo() -> u8 { + 21 + } + + fn same_operation_equals() { + // operands are fixed + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } else { + c() + } + + // operands are transposed + + if foo() == 42 { + a() + } else if 42 == foo() { + b() + } + } + + fn same_operation_not_equals() { + // operands are fixed + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } else { + c() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } else { + c() + } + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 945c9447093a2ca944e70bae125f2af69f8eac16 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 11:20:51 +0200 Subject: 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 | 51 +++++++++++++-------------- 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, 41 insertions(+), 60 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b25ef049356..0b270e6acd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1274,8 +1274,7 @@ Released 2018-09-13 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name -[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr -[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt +[`block_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/block_in_if_condition.rs index 9e533eaa32c..8a5e595749f 100644 --- a/clippy_lints/src/block_in_if_condition.rs +++ b/clippy_lints/src/block_in_if_condition.rs @@ -8,43 +8,40 @@ use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks to contain an - /// expression. + /// **What it does:** Checks for `if` conditions that use blocks containing an + /// expression, statements or conditions that use closures with blocks. /// - /// **Why is this bad?** It isn't really Rust style, same as using parentheses - /// to contain expressions. + /// **Why is this bad?** Style, using blocks in the condition makes it hard to read. /// /// **Known problems:** None. /// - /// **Example:** + /// **Examples:** /// ```rust + /// // Bad /// if { true } { /* ... */ } + /// + /// // Good + /// if true { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION_EXPR, - style, - "braces that can be eliminated in conditions, e.g., `if { true } ...`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks containing - /// statements, or conditions that use closures with blocks. /// - /// **Why is this bad?** Using blocks in the condition makes it hard to read. + /// // or /// - /// **Known problems:** None. + /// ```rust + /// # fn somefunc() -> bool { true }; /// - /// **Example:** - /// ```rust,ignore - /// if { let x = somefunc(); x } {} - /// // or - /// if somefunc(|x| { x == 47 }) {} + /// // Bad + /// if { let x = somefunc(); x } { /* ... */ } + /// + /// // Good + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION_STMT, + pub BLOCK_IN_IF_CONDITION, style, - "complex blocks in conditions, e.g., `if { let x = true; x } ...`" + "useless or complex blocks that can be eliminated in conditions" } -declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT]); +declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION]); struct ExVisitor<'a, 'tcx> { found_block: Option<&'tcx Expr<'tcx>>, @@ -72,7 +69,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ - instead, move the block or closure higher and bind it with a `let`"; + instead, move the block or closure higher and bind it with a `let`"; impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { @@ -92,7 +89,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION_EXPR, + BLOCK_IN_IF_CONDITION, cond.span, BRACED_EXPR_MESSAGE, "try", @@ -118,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION_STMT, + BLOCK_IN_IF_CONDITION, expr.span.with_hi(cond.span.hi()), COMPLEX_BLOCK_MESSAGE, "try", @@ -140,7 +137,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut visitor = ExVisitor { found_block: None, cx }; walk_expr(&mut visitor, cond); if let Some(block) = visitor.found_block { - span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE); + span_lint(cx, BLOCK_IN_IF_CONDITION, block.span, COMPLEX_BLOCK_MESSAGE); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..98b696533d8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -507,8 +507,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, &blacklisted_name::BLACKLISTED_NAME, - &block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR, - &block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT, + &block_in_if_condition::BLOCK_IN_IF_CONDITION, &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, @@ -1209,8 +1208,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT), + LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), @@ -1456,8 +1454,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT), + LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..4ae60f7d808 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -74,16 +74,9 @@ pub static ref ALL_LINTS: Vec = vec![ module: "blacklisted_name", }, Lint { - name: "block_in_if_condition_expr", + name: "block_in_if_condition", group: "style", - desc: "braces that can be eliminated in conditions, e.g., `if { true } ...`", - deprecation: None, - module: "block_in_if_condition", - }, - Lint { - name: "block_in_if_condition_stmt", - group: "style", - desc: "complex blocks in conditions, e.g., `if { let x = true; x } ...`", + desc: "useless or complex blocks that can be eliminated in conditions", deprecation: None, module: "block_in_if_condition", }, diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/block_in_if_condition.fixed index 955801e40f9..ae01c6d3042 100644 --- a/tests/ui/block_in_if_condition.fixed +++ b/tests/ui/block_in_if_condition.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/block_in_if_condition.rs index a6ea01d5fc5..88555dc47c2 100644 --- a/tests/ui/block_in_if_condition.rs +++ b/tests/ui/block_in_if_condition.rs @@ -1,6 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/block_in_if_condition.stderr index b0a0a276c89..89e9ad26f49 100644 --- a/tests/ui/block_in_if_condition.stderr +++ b/tests/ui/block_in_if_condition.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition.rs:27:5 + --> $DIR/block_in_if_condition.rs:26:5 | LL | / if { LL | | let x = 3; @@ -7,7 +7,7 @@ LL | | x == 3 LL | | } { | |_____^ | - = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + = note: `-D clippy::block-in-if-condition` implied by `-D warnings` help: try | LL | let res = { @@ -17,15 +17,13 @@ LL | }; if res { | error: omit braces around single expression condition - --> $DIR/block_in_if_condition.rs:38:8 + --> $DIR/block_in_if_condition.rs:37:8 | LL | if { true } { | ^^^^^^^^ help: try: `true` - | - = note: `-D clippy::block-in-if-condition-expr` implied by `-D warnings` error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:47:8 + --> $DIR/block_in_if_condition.rs:46:8 | LL | if true && x == 3 { | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/block_in_if_condition_closure.rs index bac3eda5e7f..87b3fb94daf 100644 --- a/tests/ui/block_in_if_condition_closure.rs +++ b/tests/ui/block_in_if_condition_closure.rs @@ -1,5 +1,4 @@ -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] fn predicate bool, T>(pfn: F, val: T) -> bool { @@ -10,7 +9,7 @@ fn pred_test() { let v = 3; let sky = "blue"; // This is a sneaky case, where the block isn't directly in the condition, - // but is actually nside a closure that the condition is using. + // but is actually inside a closure that the condition is using. // The same principle applies -- add some extra expressions to make sure // linter isn't confused by them. if v == 3 diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/block_in_if_condition_closure.stderr index 86cd24fe763..3df25691c3c 100644 --- a/tests/ui/block_in_if_condition_closure.stderr +++ b/tests/ui/block_in_if_condition_closure.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:19:17 + --> $DIR/block_in_if_condition_closure.rs:18:17 | LL | |x| { | _________________^ @@ -8,10 +8,10 @@ LL | | x == target LL | | }, | |_____________^ | - = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + = note: `-D clippy::block-in-if-condition` implied by `-D warnings` error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:28:13 + --> $DIR/block_in_if_condition_closure.rs:27:13 | LL | |x| { | _____________^ -- cgit 1.4.1-3-g733a5 From 6cbdd1e49dbb2355ac1036946a5a635e22023c6f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 12:28:40 +0200 Subject: 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 | 92 ++++--------- clippy_lints/src/methods/option_map_unwrap_or.rs | 6 +- src/lintlist/mod.rs | 28 +--- tests/ui/map_unwrap.rs | 99 ++++++++++++++ tests/ui/map_unwrap.stderr | 161 +++++++++++++++++++++++ tests/ui/option_map_unwrap_or.rs | 88 ------------- tests/ui/option_map_unwrap_or.stderr | 138 ------------------- tests/ui/result_map_unwrap_or_else.rs | 23 ---- tests/ui/result_map_unwrap_or_else.stderr | 27 ---- 11 files changed, 301 insertions(+), 373 deletions(-) create mode 100644 tests/ui/map_unwrap.rs create mode 100644 tests/ui/map_unwrap.stderr delete mode 100644 tests/ui/option_map_unwrap_or.rs delete mode 100644 tests/ui/option_map_unwrap_or.stderr delete mode 100644 tests/ui/result_map_unwrap_or_else.rs delete mode 100644 tests/ui/result_map_unwrap_or_else.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b270e6acd2..28b05044db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1430,6 +1430,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten +[`map_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items @@ -1499,8 +1500,6 @@ Released 2018-09-13 [`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn -[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or -[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option [`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call @@ -1542,7 +1541,6 @@ Released 2018-09-13 [`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn -[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 98b696533d8..c9a2ef49907 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -673,19 +673,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_FLATTEN, + &methods::MAP_UNWRAP, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, - &methods::OPTION_MAP_UNWRAP_OR, - &methods::OPTION_MAP_UNWRAP_OR_ELSE, &methods::OPTION_UNWRAP_USED, &methods::OR_FUN_CALL, &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::RESULT_MAP_UNWRAP_OR_ELSE, &methods::RESULT_UNWRAP_USED, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, @@ -1152,9 +1150,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::OPTION_MAP_UNWRAP_OR), - LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE), - LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE), + LintId::of(&methods::MAP_UNWRAP), LintId::of(&misc::USED_UNDERSCORE_BINDING), LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3676dc5b09d..401298b2d51 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -257,59 +257,40 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)`. + /// **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or + /// `result.map(_).unwrap_or_else(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map_or(_, _)`. + /// **Why is this bad?** Readability, these can be written more concisely (resp.) as + /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. /// /// **Known problems:** The order of the arguments is not in execution order /// - /// **Example:** + /// **Examples:** /// ```rust /// # let x = Some(1); - /// x.map(|a| a + 1).unwrap_or(0); - /// ``` - 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(_, _)`. /// - /// **Known problems:** The order of the arguments is not in execution order. + /// // Bad + /// x.map(|a| a + 1).unwrap_or(0); /// - /// **Example:** - /// ```rust - /// # let x = Some(1); - /// # fn some_function() -> usize { 1 } - /// x.map(|a| a + 1).unwrap_or_else(some_function); + /// // Good + /// x.map_or(0, |a| a + 1); /// ``` - 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. + /// // or /// - /// **Example:** /// ```rust /// # let x: Result = Ok(1); /// # fn some_function(foo: ()) -> usize { 1 } + /// + /// // Bad /// x.map(|a| a + 1).unwrap_or_else(some_function); + /// + /// // Good + /// x.map_or_else(some_function, |a| a + 1); /// ``` - pub RESULT_MAP_UNWRAP_OR_ELSE, + pub MAP_UNWRAP, pedantic, - "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`" + "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" } declare_clippy_lint! { @@ -1294,9 +1275,7 @@ declare_lint_pass!(Methods => [ WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, OK_EXPECT, - OPTION_MAP_UNWRAP_OR, - OPTION_MAP_UNWRAP_OR_ELSE, - RESULT_MAP_UNWRAP_OR_ELSE, + MAP_UNWRAP, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, OPTION_AND_THEN_SOME, @@ -1503,9 +1482,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { cx, lint, first_arg.pat.span, - &format!( - "methods called `{}` usually take {}; consider choosing a less \ - ambiguous name", + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", conv, &self_kinds .iter() @@ -1678,7 +1655,7 @@ fn lint_or_fun_call<'a, 'tcx>( let self_ty = cx.tables.expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); + know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); if poss.contains(&name); @@ -1931,7 +1908,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir: CLONE_DOUBLE_REF, expr.span, "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + this will copy the reference instead of cloning the inner type", |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -2121,7 +2098,7 @@ fn lint_iter_cloned_collect<'a, 'tcx>( ITER_CLONED_COLLECT, to_replace, "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", + more readable", "try", ".to_vec()".to_string(), Applicability::MachineApplicable, @@ -2436,7 +2413,7 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hi None, &format!( "if you don't want to handle the `{}` case gracefully, consider \ - using `expect()` to provide a better panic message", + using `expect()` to provide a better panic message", none_value, ), ); @@ -2494,7 +2471,7 @@ fn lint_map_flatten<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr< // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; + This is more succinctly expressed by calling `.flat_map(..)`"; let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); @@ -2555,10 +2532,10 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( // lint message let msg = if is_option { "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(g, f)` instead" + `map_or_else(g, f)` instead" } else { "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(g, f)` instead" + `.map_or_else(g, f)` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2570,11 +2547,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( if same_span && !multiline { span_lint_and_note( cx, - if is_option { - OPTION_MAP_UNWRAP_OR_ELSE - } else { - RESULT_MAP_UNWRAP_OR_ELSE - }, + MAP_UNWRAP, expr.span, msg, None, @@ -2584,16 +2557,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( ), ); } else if same_span && multiline { - span_lint( - cx, - if is_option { - OPTION_MAP_UNWRAP_OR_ELSE - } else { - RESULT_MAP_UNWRAP_OR_ELSE - }, - expr.span, - msg, - ); + span_lint(cx, MAP_UNWRAP, expr.span, msg); }; } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index bf9dd3c9369..fcaa9b47e64 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use super::OPTION_MAP_UNWRAP_OR; +use super::MAP_UNWRAP; /// lint use of `map().unwrap_or()` for `Option`s pub(super) fn lint<'a, 'tcx>( @@ -62,11 +62,11 @@ pub(super) fn lint<'a, 'tcx>( }; let msg = &format!( "called `map(f).unwrap_or({})` on an `Option` value. \ - This can be done more directly by calling `{}` instead", + This can be done more directly by calling `{}` instead", arg, suggest ); - span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |diag| { + span_lint_and_then(cx, MAP_UNWRAP, expr.span, msg, |diag| { let map_arg_span = map_args[1].span; let mut suggestion = vec![ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4ae60f7d808..d3bd9f66e38 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1137,6 +1137,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "map_unwrap", + group: "pedantic", + desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", + deprecation: None, + module: "methods", + }, Lint { name: "match_as_ref", group: "complexity", @@ -1613,20 +1620,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "option_map_unwrap_or", - group: "pedantic", - desc: "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`", - deprecation: None, - module: "methods", - }, - Lint { - name: "option_map_unwrap_or_else", - group: "pedantic", - desc: "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`", - deprecation: None, - module: "methods", - }, Lint { name: "option_option", group: "pedantic", @@ -1900,13 +1893,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "result_map_unwrap_or_else", - group: "pedantic", - desc: "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`", - deprecation: None, - module: "methods", - }, Lint { name: "result_unwrap_used", group: "restriction", diff --git a/tests/ui/map_unwrap.rs b/tests/ui/map_unwrap.rs new file mode 100644 index 00000000000..53e50368231 --- /dev/null +++ b/tests/ui/map_unwrap.rs @@ -0,0 +1,99 @@ +// FIXME: Add "run-rustfix" once it's supported for multipart suggestions +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap)] + +#[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(_)` 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 + } + ).unwrap_or(0); + let _ = opt.map(|x| x + 1) + .unwrap_or({ + 0 + }); + // Single line `map(f).unwrap_or(None)` case. + let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + // Multi-line `map(f).unwrap_or(None)` cases. + let _ = opt.map(|x| { + Some(x + 1) + } + ).unwrap_or(None); + let _ = opt + .map(|x| Some(x + 1)) + .unwrap_or(None); + // macro case + let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint + + // Should not lint if not copyable + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); + // ...but DO lint if the `unwrap_or` argument is not used in the `map` + let id: String = "identifier".to_string(); + 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 + } + ).unwrap_or_else(|| 0); + let _ = opt.map(|x| x + 1) + .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.stderr b/tests/ui/map_unwrap.stderr new file mode 100644 index 00000000000..2610923275d --- /dev/null +++ b/tests/ui/map_unwrap.stderr @@ -0,0 +1,161 @@ +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 + | +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` 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 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or(0); + | |__________________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| { +LL | x + 1 +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 + --> $DIR/map_unwrap.rs:25:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or({ +LL | | 0 +LL | | }); + | |__________^ + | +help: use `map_or(a, f)` 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 + --> $DIR/map_unwrap.rs:30:13 + | +LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and_then(f)` 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 + --> $DIR/map_unwrap.rs:32:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | Some(x + 1) +LL | | } +LL | | ).unwrap_or(None); + | |_____________________^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +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 + --> $DIR/map_unwrap.rs:36:13 + | +LL | let _ = opt + | _____________^ +LL | | .map(|x| Some(x + 1)) +LL | | .unwrap_or(None); + | |________________________^ + | +help: use `and_then(f)` 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 + --> $DIR/map_unwrap.rs:47:13 + | +LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `map_or(a, f)` 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 + --> $DIR/map_unwrap.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); + | |_____________________________^ + | + = 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 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +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 + --> $DIR/map_unwrap.rs:59:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|| +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 + --> $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/option_map_unwrap_or.rs b/tests/ui/option_map_unwrap_or.rs deleted file mode 100644 index 0364d83663a..00000000000 --- a/tests/ui/option_map_unwrap_or.rs +++ /dev/null @@ -1,88 +0,0 @@ -// 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)] - -#[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`. - // 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 - } - ).unwrap_or(0); - let _ = opt.map(|x| x + 1) - .unwrap_or({ - 0 - }); - // Single line `map(f).unwrap_or(None)` case. - let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - // Multi-line `map(f).unwrap_or(None)` cases. - let _ = opt.map(|x| { - Some(x + 1) - } - ).unwrap_or(None); - let _ = opt - .map(|x| Some(x + 1)) - .unwrap_or(None); - // macro case - let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint - - // Should not lint if not copyable - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); - // ...but DO lint if the `unwrap_or` argument is not used in the `map` - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - - // Check OPTION_MAP_UNWRAP_OR_ELSE - // 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 - } - ).unwrap_or_else(|| 0); - let _ = opt.map(|x| x + 1) - .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 main() { - option_methods(); -} diff --git a/tests/ui/option_map_unwrap_or.stderr b/tests/ui/option_map_unwrap_or.stderr deleted file mode 100644 index f05f2893de2..00000000000 --- a/tests/ui/option_map_unwrap_or.stderr +++ /dev/null @@ -1,138 +0,0 @@ -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 - | -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::option-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/option_map_unwrap_or.rs:24:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -LL | | } -LL | | ).unwrap_or(0); - | |__________________^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or(0, |x| { -LL | x + 1 -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 - --> $DIR/option_map_unwrap_or.rs:28:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or({ -LL | | 0 -LL | | }); - | |__________^ - | -help: use `map_or(a, f)` 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 - --> $DIR/option_map_unwrap_or.rs:33:13 - | -LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `and_then(f)` 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 - --> $DIR/option_map_unwrap_or.rs:35:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | Some(x + 1) -LL | | } -LL | | ).unwrap_or(None); - | |_____________________^ - | -help: use `and_then(f)` instead - | -LL | let _ = opt.and_then(|x| { -LL | Some(x + 1) -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 - --> $DIR/option_map_unwrap_or.rs:39:13 - | -LL | let _ = opt - | _____________^ -LL | | .map(|x| Some(x + 1)) -LL | | .unwrap_or(None); - | |________________________^ - | -help: use `and_then(f)` 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 - --> $DIR/option_map_unwrap_or.rs:50:13 - | -LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `map_or(a, f)` 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 - --> $DIR/option_map_unwrap_or.rs:54: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); - | |_____________________________^ - | - = 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 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -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 - --> $DIR/option_map_unwrap_or.rs:62:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or_else(|| -LL | | 0 -LL | | ); - | |_________^ - -error: aborting due to 10 previous errors - diff --git a/tests/ui/result_map_unwrap_or_else.rs b/tests/ui/result_map_unwrap_or_else.rs deleted file mode 100644 index 40751bfebe6..00000000000 --- a/tests/ui/result_map_unwrap_or_else.rs +++ /dev/null @@ -1,23 +0,0 @@ -// aux-build:option_helpers.rs - -//! Checks implementation of `RESULT_MAP_UNWRAP_OR_ELSE` - -#![warn(clippy::result_map_unwrap_or_else)] - -#[macro_use] -extern crate option_helpers; - -fn result_methods() { - let res: Result = Ok(1); - - // Check RESULT_MAP_UNWRAP_OR_ELSE - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint -} - -fn main() {} diff --git a/tests/ui/result_map_unwrap_or_else.stderr b/tests/ui/result_map_unwrap_or_else.stderr deleted file mode 100644 index ec7bc8f1241..00000000000 --- a/tests/ui/result_map_unwrap_or_else.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:15:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::result-map-unwrap-or-else` implied by `-D warnings` - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:17:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:18:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From bcf61666bd903c0d13c081cf222b423e45cd854e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 13:11:18 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b05044db6..78f98bba2b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1501,7 +1501,6 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option -[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional @@ -1622,6 +1621,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9a2ef49907..bb3bc0b4545 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -680,11 +680,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OPTION_AS_REF_DEREF, &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, - &methods::OPTION_UNWRAP_USED, &methods::OR_FUN_CALL, &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::RESULT_UNWRAP_USED, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, @@ -695,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, &methods::WRONG_SELF_CONVENTION, @@ -1090,9 +1089,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), LintId::of(&methods::OPTION_EXPECT_USED), - LintId::of(&methods::OPTION_UNWRAP_USED), LintId::of(&methods::RESULT_EXPECT_USED), - LintId::of(&methods::RESULT_UNWRAP_USED), + LintId::of(&methods::UNWRAP_USED), LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), LintId::of(&misc::FLOAT_CMP_CONST), LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 401298b2d51..1af4d03c7a2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -33,40 +33,15 @@ use crate::utils::{ }; declare_clippy_lint! { - /// **What it does:** Checks for `.unwrap()` calls on `Option`s. + /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// - /// **Why is this bad?** Usually it is better to handle the `None` case, or to - /// at least call `.expect(_)` with a more helpful message. Still, for a lot of + /// **Why is this bad?** It is better to handle the `None` or `Err` case, + /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is /// `Allow` by default. /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// Using unwrap on an `Option`: - /// - /// ```rust - /// let opt = Some(1); - /// opt.unwrap(); - /// ``` - /// - /// Better: - /// - /// ```rust - /// let opt = Some(1); - /// opt.expect("more helpful message"); - /// ``` - pub OPTION_UNWRAP_USED, - restriction, - "using `Option.unwrap()`, which should at least get a better message using `expect()`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `.unwrap()` calls on `Result`s. - /// - /// **Why is this bad?** `result.unwrap()` will let the thread panic on `Err` - /// values. Normally, you want to implement more sophisticated error handling, + /// `result.unwrap()` will let the thread panic on `Err` values. + /// Normally, you want to implement more sophisticated error handling, /// and propagate errors upwards with `?` operator. /// /// Even if you want to panic on errors, not all `Error`s implement good @@ -75,23 +50,31 @@ declare_clippy_lint! { /// /// **Known problems:** None. /// - /// **Example:** - /// Using unwrap on an `Result`: - /// + /// **Examples:** /// ```rust - /// let res: Result = Ok(1); - /// res.unwrap(); + /// # let opt = Some(1); + /// + /// // Bad + /// opt.unwrap(); + /// + /// // Good + /// opt.expect("more helpful message"); /// ``` /// - /// Better: + /// // or /// /// ```rust - /// let res: Result = Ok(1); + /// # let res: Result = Ok(1); + /// + /// // Bad + /// res.unwrap(); + /// + /// // Good /// res.expect("more helpful message"); /// ``` - pub RESULT_UNWRAP_USED, + pub UNWRAP_USED, restriction, - "using `Result.unwrap()`, which might be better handled" + "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`" } declare_clippy_lint! { @@ -1267,8 +1250,7 @@ declare_clippy_lint! { } declare_lint_pass!(Methods => [ - OPTION_UNWRAP_USED, - RESULT_UNWRAP_USED, + UNWRAP_USED, OPTION_EXPECT_USED, RESULT_EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, @@ -2397,9 +2379,9 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hi let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&unwrap_args[0])); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { - Some((OPTION_UNWRAP_USED, "an Option", "None")) + Some((UNWRAP_USED, "an Option", "None")) } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { - Some((RESULT_UNWRAP_USED, "a Result", "Err")) + Some((UNWRAP_USED, "a Result", "Err")) } else { None }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d3bd9f66e38..5cbf3ef028c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1627,13 +1627,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, - Lint { - name: "option_unwrap_used", - group: "restriction", - desc: "using `Option.unwrap()`, which should at least get a better message using `expect()`", - deprecation: None, - module: "methods", - }, Lint { name: "or_fun_call", group: "perf", @@ -2404,6 +2397,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "returns", }, + Lint { + name: "unwrap_used", + group: "restriction", + desc: "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`", + deprecation: None, + module: "methods", + }, Lint { name: "use_debug", group: "restriction", diff --git a/tests/ui/unwrap.rs b/tests/ui/unwrap.rs index fcd1fcd14d4..a4a3cd1d379 100644 --- a/tests/ui/unwrap.rs +++ b/tests/ui/unwrap.rs @@ -1,4 +1,4 @@ -#![warn(clippy::option_unwrap_used, clippy::result_unwrap_used)] +#![warn(clippy::unwrap_used)] fn unwrap_option() { let opt = Some(0); diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index b90ce68fa97..4f0858005f6 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -4,7 +4,7 @@ error: used `unwrap()` on `an Option` value LL | let _ = opt.unwrap(); | ^^^^^^^^^^^^ | - = note: `-D clippy::option-unwrap-used` implied by `-D warnings` + = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: used `unwrap()` on `a Result` value @@ -13,7 +13,6 @@ error: used `unwrap()` on `a Result` value LL | let _ = res.unwrap(); | ^^^^^^^^^^^^ | - = note: `-D clippy::result-unwrap-used` implied by `-D warnings` = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 0e8be599cd04a8566224c63eeb07f5fa04605702 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 13:32:17 +0200 Subject: 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 | 63 +++++++++++++++-------------------------- src/lintlist/mod.rs | 21 +++++--------- tests/ui/expect.rs | 2 +- tests/ui/expect.stderr | 3 +- 6 files changed, 35 insertions(+), 63 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 78f98bba2b4..4eeb71fa5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1337,6 +1337,7 @@ Released 2018-09-13 [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used [`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy [`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop [`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods @@ -1497,7 +1498,6 @@ Released 2018-09-13 [`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap -[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option @@ -1537,7 +1537,6 @@ Released 2018-09-13 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs -[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bb3bc0b4545..eaef1f543d3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -657,6 +657,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, &methods::EXPECT_FUN_CALL, + &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, &methods::FILTER_MAP, &methods::FILTER_MAP_NEXT, @@ -678,10 +679,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, - &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, - &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, @@ -1086,10 +1085,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(&mem_forget::MEM_FORGET), LintId::of(&methods::CLONE_ON_REF_PTR), + LintId::of(&methods::EXPECT_USED), LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), - LintId::of(&methods::OPTION_EXPECT_USED), - LintId::of(&methods::RESULT_EXPECT_USED), LintId::of(&methods::UNWRAP_USED), LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), LintId::of(&misc::FLOAT_CMP_CONST), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1af4d03c7a2..2e75de019b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -78,61 +78,45 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `.expect()` calls on `Option`s. + /// **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s. /// - /// **Why is this bad?** Usually it is better to handle the `None` case. Still, - /// for a lot of quick-and-dirty code, `expect` is a good choice, which is why - /// this lint is `Allow` by default. + /// **Why is this bad?** Usually it is better to handle the `None` or `Err` case. + /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why + /// this lint is `Allow` by default. /// - /// **Known problems:** None. + /// `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. /// - /// **Example:** + /// **Known problems:** None. /// - /// Using expect on an `Option`: + /// **Examples:** + /// ```rust,ignore + /// # let opt = Some(1); /// - /// ```rust - /// let opt = Some(1); + /// // Bad /// opt.expect("one"); - /// ``` - /// - /// Better: /// - /// ```rust,ignore + /// // Good /// 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` - /// 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`: + /// // or /// /// ```rust - /// let res: Result = Ok(1); - /// res.expect("one"); - /// ``` + /// # let res: Result = Ok(1); /// - /// Better: + /// // Bad + /// res.expect("one"); /// - /// ```rust - /// let res: Result = Ok(1); + /// // Good /// res?; /// # Ok::<(), ()>(()) /// ``` - pub RESULT_EXPECT_USED, + pub EXPECT_USED, restriction, - "using `Result.expect()`, which might be better handled" + "using `.expect()` on `Result` or `Option`, which might be better handled" } declare_clippy_lint! { @@ -1251,8 +1235,7 @@ declare_clippy_lint! { declare_lint_pass!(Methods => [ UNWRAP_USED, - OPTION_EXPECT_USED, - RESULT_EXPECT_USED, + EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, @@ -2407,9 +2390,9 @@ fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, expect_args: &[hi let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0])); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { - Some((OPTION_EXPECT_USED, "an Option", "None")) + Some((EXPECT_USED, "an Option", "None")) } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { - Some((RESULT_EXPECT_USED, "a Result", "Err")) + Some((EXPECT_USED, "a Result", "Err")) } else { None }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5cbf3ef028c..4e79ce96bb5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -514,6 +514,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "expect_used", + group: "restriction", + desc: "using `.expect()` on `Result` or `Option`, which might be better handled", + deprecation: None, + module: "methods", + }, Lint { name: "expl_impl_clone_on_copy", group: "pedantic", @@ -1599,13 +1606,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "option_env_unwrap", }, - Lint { - name: "option_expect_used", - group: "restriction", - desc: "using `Option.expect()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "option_map_or_none", group: "style", @@ -1865,13 +1865,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, - Lint { - name: "result_expect_used", - group: "restriction", - desc: "using `Result.expect()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "result_map_or_into_option", group: "style", diff --git a/tests/ui/expect.rs b/tests/ui/expect.rs index 0bd4252c49a..1073acf6f0c 100644 --- a/tests/ui/expect.rs +++ b/tests/ui/expect.rs @@ -1,4 +1,4 @@ -#![warn(clippy::option_expect_used, clippy::result_expect_used)] +#![warn(clippy::expect_used)] fn expect_option() { let opt = Some(0); diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index adf9f4f1921..9d3fc7df15c 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -4,7 +4,7 @@ error: used `expect()` on `an Option` value LL | let _ = opt.expect(""); | ^^^^^^^^^^^^^^ | - = note: `-D clippy::option-expect-used` implied by `-D warnings` + = note: `-D clippy::expect-used` implied by `-D warnings` = help: if this value is an `None`, it will panic error: used `expect()` on `a Result` value @@ -13,7 +13,6 @@ error: used `expect()` on `a Result` value LL | let _ = res.expect(""); | ^^^^^^^^^^^^^^ | - = note: `-D clippy::result-expect-used` implied by `-D warnings` = help: if this value is an `Err`, it will panic error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From adbdf7549c6b24c37629eabdc4be0346e0c8fd56 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 15:16:00 +0200 Subject: 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 | 64 +++++++++++-------------- src/lintlist/mod.rs | 11 +---- tests/ui/for_loop_over_fallible.rs | 57 +++++++++++++++++++++++ tests/ui/for_loop_over_fallible.stderr | 71 ++++++++++++++++++++++++++++ tests/ui/for_loop_over_option_result.rs | 59 ----------------------- tests/ui/for_loop_over_option_result.stderr | 72 ----------------------------- 8 files changed, 161 insertions(+), 185 deletions(-) create mode 100644 tests/ui/for_loop_over_fallible.rs create mode 100644 tests/ui/for_loop_over_fallible.stderr delete mode 100644 tests/ui/for_loop_over_option_result.rs delete mode 100644 tests/ui/for_loop_over_option_result.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eeb71fa5c5..3f9486e0972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1361,8 +1361,7 @@ Released 2018-09-13 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map -[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option -[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result +[`for_loop_over_fallible`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_fallible [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index eaef1f543d3..8de94d19d31 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -615,8 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_INTO_ITER_LOOP, &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, - &loops::FOR_LOOP_OVER_OPTION, - &loops::FOR_LOOP_OVER_RESULT, + &loops::FOR_LOOP_OVER_FALLIBLE, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -1265,8 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOP_OVER_OPTION), - LintId::of(&loops::FOR_LOOP_OVER_RESULT), + LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1641,8 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&loops::FOR_LOOP_OVER_OPTION), - LintId::of(&loops::FOR_LOOP_OVER_RESULT), + LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0bc6b70855b..da6793a69d6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -168,7 +168,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `for` loops over `Option` values. + /// **What it does:** Checks for `for` loops over `Option` or `Result` values. /// /// **Why is this bad?** Readability. This is more clearly expressed as an `if /// let`. @@ -176,47 +176,38 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```ignore - /// for x in option { - /// .. + /// ```rust + /// # let opt = Some(1); + /// + /// // Bad + /// for x in opt { + /// // .. /// } - /// ``` /// - /// This should be - /// ```ignore - /// if let Some(x) = option { - /// .. + /// // Good + /// if let Some(x) = opt { + /// // .. /// } /// ``` - pub FOR_LOOP_OVER_OPTION, - 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`. + /// // or /// - /// **Known problems:** None. + /// ```rust + /// # let res: Result = Ok(1); /// - /// **Example:** - /// ```ignore - /// for x in result { - /// .. + /// // Bad + /// for x in &res { + /// // .. /// } - /// ``` /// - /// This should be - /// ```ignore - /// if let Ok(x) = result { - /// .. + /// // Good + /// if let Ok(x) = res { + /// // .. /// } /// ``` - pub FOR_LOOP_OVER_RESULT, + pub FOR_LOOP_OVER_FALLIBLE, correctness, - "for-looping over a `Result`, which is more clearly expressed as an `if let`" + "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } declare_clippy_lint! { @@ -435,8 +426,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOP_OVER_RESULT, - FOR_LOOP_OVER_OPTION, + FOR_LOOP_OVER_FALLIBLE, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -1283,7 +1273,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e ITER_NEXT_LOOP, expr.span, "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ - probably not what you want", + probably not what you want", ); next_loop_linted = true; } @@ -1300,11 +1290,11 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { if is_type_diagnostic_item(cx, ty, sym!(option_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_OPTION, + FOR_LOOP_OVER_FALLIBLE, arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement.", + `if let` statement.", snippet(cx, arg.span, "_") ), None, @@ -1317,11 +1307,11 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_RESULT, + FOR_LOOP_OVER_FALLIBLE, arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement.", + `if let` statement.", snippet(cx, arg.span, "_") ), None, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4e79ce96bb5..0ea0f55a381 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -676,16 +676,9 @@ pub static ref ALL_LINTS: Vec = vec![ module: "loops", }, Lint { - name: "for_loop_over_option", + name: "for_loop_over_fallible", group: "correctness", - desc: "for-looping over an `Option`, which is more clearly expressed as an `if let`", - deprecation: None, - module: "loops", - }, - Lint { - name: "for_loop_over_result", - group: "correctness", - desc: "for-looping over a `Result`, which is more clearly expressed as an `if let`", + desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", deprecation: None, module: "loops", }, diff --git a/tests/ui/for_loop_over_fallible.rs b/tests/ui/for_loop_over_fallible.rs new file mode 100644 index 00000000000..e52468cdd4b --- /dev/null +++ b/tests/ui/for_loop_over_fallible.rs @@ -0,0 +1,57 @@ +#![warn(clippy::for_loop_over_fallible)] + +fn for_loop_over_fallible() { + let option = Some(1); + let result = option.ok_or("x not found"); + let v = vec![0, 1, 2]; + + // check over an `Option` + for x in option { + println!("{}", x); + } + + // check over a `Result` + for x in result { + println!("{}", x); + } + + for x in option.ok_or("x not found") { + println!("{}", x); + } + + // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call + // in the chain + for x in v.iter().next() { + println!("{}", x); + } + + // make sure we lint when next() is not the last call in the chain + for x in v.iter().next().and(Some(0)) { + println!("{}", x); + } + + for x in v.iter().next().ok_or("x not found") { + println!("{}", x); + } + + // check for false positives + + // for loop false positive + for x in v { + println!("{}", x); + } + + // while let false positive for Option + while let Some(x) = option { + println!("{}", x); + break; + } + + // while let false positive for Result + while let Ok(x) = result { + println!("{}", x); + break; + } +} + +fn main() {} diff --git a/tests/ui/for_loop_over_fallible.stderr b/tests/ui/for_loop_over_fallible.stderr new file mode 100644 index 00000000000..4ce9a144ad8 --- /dev/null +++ b/tests/ui/for_loop_over_fallible.stderr @@ -0,0 +1,71 @@ +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 + | +LL | for x in option { + | ^^^^^^ + | + = 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_fallible.rs:14:14 + | +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 + | +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 + | +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 + | +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 + | +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 + | +LL | / while let Some(x) = option { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/for_loop_over_fallible.rs:51:5 + | +LL | / while let Ok(x) = result { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/for_loop_over_option_result.rs b/tests/ui/for_loop_over_option_result.rs deleted file mode 100644 index 6b207b26b6b..00000000000 --- a/tests/ui/for_loop_over_option_result.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![warn(clippy::for_loop_over_option, clippy::for_loop_over_result)] - -/// Tests for_loop_over_result and for_loop_over_option - -fn for_loop_over_option_and_result() { - let option = Some(1); - let result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check FOR_LOOP_OVER_OPTION lint - for x in option { - println!("{}", x); - } - - // check FOR_LOOP_OVER_RESULT lint - for x in result { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loop_over_option_result.stderr b/tests/ui/for_loop_over_option_result.stderr deleted file mode 100644 index 194a0bfec5b..00000000000 --- a/tests/ui/for_loop_over_option_result.stderr +++ /dev/null @@ -1,72 +0,0 @@ -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 - | -LL | for x in option { - | ^^^^^^ - | - = note: `-D clippy::for-loop-over-option` 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 - | -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 - | -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 - | -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 - | -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 - | -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 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loop_over_option_result.rs:53:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 8 previous errors - -- cgit 1.4.1-3-g733a5 From 95399f8f941b89785c6e9d94e0bc32ff5d43ba06 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 14 May 2020 09:57:36 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 398a3103a03..d7bf8a14768 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// }; /// ``` pub USELESS_LET_IF_SEQ, - style, + nursery, "unidiomatic `let mut` declaration followed by initialization in `if`" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..b241ac5559c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1266,7 +1266,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1476,7 +1475,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1728,6 +1726,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), LintId::of(&future_not_send::FUTURE_NOT_SEND), + LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(&mutex_atomic::MUTEX_INTEGER), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..e1c68b58b86 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2469,7 +2469,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "useless_let_if_seq", - group: "style", + group: "nursery", desc: "unidiomatic `let mut` declaration followed by initialization in `if`", deprecation: None, module: "let_if_seq", -- cgit 1.4.1-3-g733a5 From 94e4b5ec316993200d75276b4e7c16a059bf3a57 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 00:08:41 +0300 Subject: 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 ++++ tests/ui/match_wildcard_for_single_variants.fixed | 18 ++++++++ tests/ui/match_wildcard_for_single_variants.rs | 18 ++++++++ tests/ui/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b25ef049356..d6298fec65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1439,6 +1439,7 @@ Released 2018-09-13 [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter [`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..41046c18ed2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -641,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, &matches::MATCH_SINGLE_BINDING, + &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, &matches::SINGLE_MATCH, @@ -1147,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(¯o_use::MACRO_USE_IMPORTS), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), + LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f86535ef1e..42a6c416619 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -229,6 +229,40 @@ declare_clippy_lint! { "a wildcard enum match arm using `_`" } +declare_clippy_lint! { + /// **What it does:** Checks for wildcard enum matches for a single variant. + /// + /// **Why is this bad?** New enum variants added by library updates can be missed. + /// + /// **Known problems:** Suggested replacements may not use correct path to enum + /// if it's not present in the current scope. + /// + /// **Example:** + /// + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// Foo::C => {}, + /// } + /// ``` + pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + pedantic, + "a wildcard enum match for a single variant" +} + declare_clippy_lint! { /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm. /// @@ -356,6 +390,7 @@ impl_lint_pass!(Matches => [ MATCH_WILD_ERR_ARM, MATCH_AS_REF, WILDCARD_ENUM_MATCH_ARM, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, INFALLIBLE_DESTRUCTURING_MATCH, @@ -766,6 +801,19 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ } } + if suggestion.len() == 1 { + // No need to check for non-exhaustive enum as in that case len would be greater than 1 + span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + message, + "try this", + suggestion[0].clone(), + Applicability::MachineApplicable, + ) + }; + span_lint_and_sugg( cx, WILDCARD_ENUM_MATCH_ARM, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..250a7c09f78 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1200,6 +1200,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, + Lint { + name: "match_wildcard_for_single_variants", + group: "pedantic", + desc: "a wildcard enum match for a single variant", + deprecation: None, + module: "matches", + }, Lint { name: "maybe_infinite_iter", group: "pedantic", diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed new file mode 100644 index 00000000000..5f1a559f591 --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +fn main() { + match Foo::A { + Foo::A => {}, + Foo::B => {}, + Foo::C => {}, + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs new file mode 100644 index 00000000000..1159f9e722d --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +fn main() { + match Foo::A { + Foo::A => {}, + Foo::B => {}, + _ => {}, + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr new file mode 100644 index 00000000000..128dd4808bf --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -0,0 +1,10 @@ +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:16:9 + | +LL | _ => {}, + | ^ help: try this: `Foo::C` + | + = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 0ad9f7d651b52de4be6384c9b6dc893b389fd557 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 18:33:12 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 3a52b1d3fc2..4c604cd0107 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -77,7 +77,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral { let type_suffix = match lit_float_ty { LitFloatType::Suffixed(FloatTy::F32) => Some("f32"), LitFloatType::Suffixed(FloatTy::F64) => Some("f64"), - _ => None + LitFloatType::Unsuffixed => None }; let (is_whole, mut float_str) = match fty { FloatTy::F32 => { diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 62ee051624b..552222eba2e 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -379,7 +379,7 @@ impl EarlyLintPass for MiscEarlyLints { let left_binding = match left { BindingMode::ByRef(Mutability::Mut) => "ref mut ", BindingMode::ByRef(Mutability::Not) => "ref ", - _ => "", + BindingMode::ByValue(..) => "", }; if let PatKind::Wild = right.kind { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 4301157e164..9cfc8d19134 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { return; } }, - _ => return, + FnKind::Closure(..) => return, } let mir = cx.tcx.optimized_mir(def_id); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index a21818701da..c099c553333 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { } }, FnKind::Method(..) => (), - _ => return, + FnKind::Closure(..) => return, } // Exclude non-inherent impls diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 2c101220c5d..8e0cb94317a 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef { } }, FnKind::Method(..) => (), - _ => return, + FnKind::Closure(..) => return, } // Exclude non-inherent impls diff --git a/tests/compile-test.rs b/tests/compile-test.rs index de2cf6d7873..a3df9d5ccbd 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -44,7 +44,7 @@ fn third_party_crates() -> String { for entry in fs::read_dir(dep_dir).unwrap() { let path = match entry { Ok(entry) => entry.path(), - _ => continue, + Err(_) => continue, }; if let Some(name) = path.file_name().and_then(OsStr::to_str) { for dep in CRATES { -- cgit 1.4.1-3-g733a5 From 494830797744c09d6de3b2b2452ab185d2204005 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 18:34:29 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 81ddc8c0067..efb424bcb7b 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -139,6 +139,7 @@ impl Constant { .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + #[allow(clippy::match_wildcard_for_single_variants)] match Self::partial_cmp(tcx, cmp_type, lv, rv) { Some(Equal) => Some(ls.cmp(rs)), x => x, @@ -354,14 +355,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - _ => None, + Some(_) | None => None, }, (Some(Constant::Vec(vec)), _) => { if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { match vec.get(0) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - _ => None, + Some(_) | None => None, } } else { None @@ -532,7 +533,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - _ => None, + Some(_) | None => None, }, ty::Float(FloatTy::F64) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc @@ -546,7 +547,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - _ => None, + Some(_) | None => None, }, // FIXME: implement other array type conversions. _ => None, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 1ec60a0e6e6..615afee33ef 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -95,12 +95,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { match map.find(id) { Some(Node::Binding(_)) => (), - _ => return false, + Some(_) | None => return false, } match map.find(map.get_parent_node(id)) { Some(Node::Param(_)) => true, - _ => false, + Some(_) | None => false, } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 86317fb8bd5..8c61b2f8664 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -410,7 +410,7 @@ fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { Some(Constant::Int(i)) => i == 0, Some(Constant::F32(f)) => f == 0.0, Some(Constant::F64(f)) => f == 0.0, - _ => false, + Some(_) | None => false, } } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0bc6b70855b..39908bff5ed 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2154,7 +2154,7 @@ fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Ex } }, Some(Node::Stmt(_)) => (), - _ => { + Some(_) | None => { return false; }, } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index e1d524c2231..38c2645d36e 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -509,7 +509,7 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> boo Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), _ => false, }), - _ => false, + Some(_) | None => false, } } diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 4ca90455bc4..3bb3eb15d9c 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -37,6 +37,7 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + #[allow(clippy::match_wildcard_for_single_variants)] match constant(cx, cx.tables, operand) { Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind { ty::Int(ity) => { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3b8ef18bfab..7bc8be492e8 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -370,6 +370,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O /// Checks whether this type implements `Drop`. pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + #[allow(clippy::match_wildcard_for_single_variants)] match ty.ty_adt_def() { Some(def) => def.has_dtor(cx.tcx), _ => false, @@ -444,6 +445,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); + #[allow(clippy::match_wildcard_for_single_variants)] match cx.tcx.hir().find(parent_id) { Some( Node::Item(Item { ident, .. }) -- cgit 1.4.1-3-g733a5 From 749619cfe34be1ee591f3af748fbdd4d2f54d3f0 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Thu, 14 May 2020 22:40:33 +0300 Subject: Apply suggestions from PR review --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 42a6c416619..444f5bb0db6 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -810,7 +810,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ message, "try this", suggestion[0].clone(), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) }; @@ -821,7 +821,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ message, "try this", suggestion.join(" | "), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) } } -- cgit 1.4.1-3-g733a5 From 1c59cd5f2110ff90a256f0948f05716403e84b85 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Thu, 14 May 2020 22:41:05 +0300 Subject: Fix example code of wildcard_enum_match_arm lint --- clippy_lints/src/matches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 444f5bb0db6..6fdb4cf9cd7 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -220,7 +220,7 @@ declare_clippy_lint! { /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); /// match x { - /// A => {}, + /// Foo::A(_) => {}, /// _ => {}, /// } /// ``` -- cgit 1.4.1-3-g733a5 From 93386563f66823ac7d10641c007b0bbc23ab09e6 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 19:58:27 +0200 Subject: 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 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 4 +- src/lintlist/mod.rs | 9 +- tests/ui/map_unwrap.rs | 99 -------------- tests/ui/map_unwrap.stderr | 161 ----------------------- tests/ui/map_unwrap_or.rs | 99 ++++++++++++++ tests/ui/map_unwrap_or.stderr | 161 +++++++++++++++++++++++ 9 files changed, 281 insertions(+), 278 deletions(-) delete mode 100644 tests/ui/map_unwrap.rs delete mode 100644 tests/ui/map_unwrap.stderr create mode 100644 tests/ui/map_unwrap_or.rs create mode 100644 tests/ui/map_unwrap_or.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f9486e0972..77272f4f78b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1430,7 +1430,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten -[`map_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items @@ -1538,7 +1538,6 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn -[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8de94d19d31..ff67ccae794 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -673,7 +673,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_FLATTEN, - &methods::MAP_UNWRAP, + &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, @@ -1145,7 +1145,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::MAP_UNWRAP), + LintId::of(&methods::MAP_UNWRAP_OR), LintId::of(&misc::USED_UNDERSCORE_BINDING), LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), @@ -1785,6 +1785,17 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loop_over_fallible"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loop_over_fallible"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2e75de019b6..e6094edc5d7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -255,7 +255,7 @@ declare_clippy_lint! { /// // Good /// x.map_or_else(some_function, |a| a + 1); /// ``` - pub MAP_UNWRAP, + pub MAP_UNWRAP_OR, pedantic, "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" } @@ -1240,7 +1240,7 @@ declare_lint_pass!(Methods => [ WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, OK_EXPECT, - MAP_UNWRAP, + MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, OPTION_AND_THEN_SOME, @@ -2512,7 +2512,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( if same_span && !multiline { span_lint_and_note( cx, - MAP_UNWRAP, + MAP_UNWRAP_OR, expr.span, msg, None, @@ -2522,7 +2522,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( ), ); } else if same_span && multiline { - span_lint(cx, MAP_UNWRAP, expr.span, msg); + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); }; } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index fcaa9b47e64..20c60ef3318 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use super::MAP_UNWRAP; +use super::MAP_UNWRAP_OR; /// lint use of `map().unwrap_or()` for `Option`s pub(super) fn lint<'a, 'tcx>( @@ -66,7 +66,7 @@ pub(super) fn lint<'a, 'tcx>( arg, suggest ); - span_lint_and_then(cx, MAP_UNWRAP, expr.span, msg, |diag| { + span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { let map_arg_span = map_args[1].span; let mut suggestion = vec![ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0ea0f55a381..e90b9c15747 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1138,7 +1138,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "map_unwrap", + name: "map_unwrap_or", group: "pedantic", desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", deprecation: None, @@ -1872,13 +1872,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "result_unwrap_used", - group: "restriction", - desc: "using `Result.unwrap()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/map_unwrap.rs b/tests/ui/map_unwrap.rs deleted file mode 100644 index 53e50368231..00000000000 --- a/tests/ui/map_unwrap.rs +++ /dev/null @@ -1,99 +0,0 @@ -// FIXME: Add "run-rustfix" once it's supported for multipart suggestions -// aux-build:option_helpers.rs - -#![warn(clippy::map_unwrap)] - -#[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(_)` 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 - } - ).unwrap_or(0); - let _ = opt.map(|x| x + 1) - .unwrap_or({ - 0 - }); - // Single line `map(f).unwrap_or(None)` case. - let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - // Multi-line `map(f).unwrap_or(None)` cases. - let _ = opt.map(|x| { - Some(x + 1) - } - ).unwrap_or(None); - let _ = opt - .map(|x| Some(x + 1)) - .unwrap_or(None); - // macro case - let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint - - // Should not lint if not copyable - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); - // ...but DO lint if the `unwrap_or` argument is not used in the `map` - let id: String = "identifier".to_string(); - 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 - } - ).unwrap_or_else(|| 0); - let _ = opt.map(|x| x + 1) - .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.stderr b/tests/ui/map_unwrap.stderr deleted file mode 100644 index 2610923275d..00000000000 --- a/tests/ui/map_unwrap.stderr +++ /dev/null @@ -1,161 +0,0 @@ -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 - | -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` 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 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -LL | | } -LL | | ).unwrap_or(0); - | |__________________^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or(0, |x| { -LL | x + 1 -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 - --> $DIR/map_unwrap.rs:25:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or({ -LL | | 0 -LL | | }); - | |__________^ - | -help: use `map_or(a, f)` 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 - --> $DIR/map_unwrap.rs:30:13 - | -LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `and_then(f)` 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 - --> $DIR/map_unwrap.rs:32:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | Some(x + 1) -LL | | } -LL | | ).unwrap_or(None); - | |_____________________^ - | -help: use `and_then(f)` instead - | -LL | let _ = opt.and_then(|x| { -LL | Some(x + 1) -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 - --> $DIR/map_unwrap.rs:36:13 - | -LL | let _ = opt - | _____________^ -LL | | .map(|x| Some(x + 1)) -LL | | .unwrap_or(None); - | |________________________^ - | -help: use `and_then(f)` 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 - --> $DIR/map_unwrap.rs:47:13 - | -LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `map_or(a, f)` 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 - --> $DIR/map_unwrap.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); - | |_____________________________^ - | - = 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 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -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 - --> $DIR/map_unwrap.rs:59:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or_else(|| -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 - --> $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/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs new file mode 100644 index 00000000000..585944032e7 --- /dev/null +++ b/tests/ui/map_unwrap_or.rs @@ -0,0 +1,99 @@ +// FIXME: Add "run-rustfix" once it's supported for multipart suggestions +// 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(_)` 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 + } + ).unwrap_or(0); + let _ = opt.map(|x| x + 1) + .unwrap_or({ + 0 + }); + // Single line `map(f).unwrap_or(None)` case. + let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + // Multi-line `map(f).unwrap_or(None)` cases. + let _ = opt.map(|x| { + Some(x + 1) + } + ).unwrap_or(None); + let _ = opt + .map(|x| Some(x + 1)) + .unwrap_or(None); + // macro case + let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint + + // Should not lint if not copyable + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); + // ...but DO lint if the `unwrap_or` argument is not used in the `map` + let id: String = "identifier".to_string(); + 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 + } + ).unwrap_or_else(|| 0); + let _ = opt.map(|x| x + 1) + .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 new file mode 100644 index 00000000000..b62080a073f --- /dev/null +++ b/tests/ui/map_unwrap_or.stderr @@ -0,0 +1,161 @@ +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_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(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_or.rs:21:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or(0); + | |__________________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| { +LL | x + 1 +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 + --> $DIR/map_unwrap_or.rs:25:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or({ +LL | | 0 +LL | | }); + | |__________^ + | +help: use `map_or(a, f)` 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 + --> $DIR/map_unwrap_or.rs:30:13 + | +LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and_then(f)` 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 + --> $DIR/map_unwrap_or.rs:32:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | Some(x + 1) +LL | | } +LL | | ).unwrap_or(None); + | |_____________________^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +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 + --> $DIR/map_unwrap_or.rs:36:13 + | +LL | let _ = opt + | _____________^ +LL | | .map(|x| Some(x + 1)) +LL | | .unwrap_or(None); + | |________________________^ + | +help: use `and_then(f)` 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 + --> $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 + | +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_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); + | |_____________________________^ + | + = 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_or.rs:55:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +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 + --> $DIR/map_unwrap_or.rs:59:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|| +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 + --> $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)` + +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_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)` + +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_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)` + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From ab87f87ba03518da23ca510249aa3f5908a42368 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 15 May 2020 18:20:07 +0200 Subject: Fix CHANGELOG.md and lint names plural --- CHANGELOG.md | 22 ++-- clippy_lints/src/block_in_if_condition.rs | 145 ------------------------ clippy_lints/src/blocks_in_if_conditions.rs | 145 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 24 ++-- clippy_lints/src/loops.rs | 8 +- src/lintlist/mod.rs | 6 +- tests/ui/block_in_if_condition.fixed | 74 ------------ tests/ui/block_in_if_condition.rs | 74 ------------ tests/ui/block_in_if_condition.stderr | 34 ------ tests/ui/block_in_if_condition_closure.rs | 47 -------- tests/ui/block_in_if_condition_closure.stderr | 24 ---- tests/ui/blocks_in_if_conditions.fixed | 74 ++++++++++++ tests/ui/blocks_in_if_conditions.rs | 74 ++++++++++++ tests/ui/blocks_in_if_conditions.stderr | 34 ++++++ tests/ui/blocks_in_if_conditions_closure.rs | 47 ++++++++ tests/ui/blocks_in_if_conditions_closure.stderr | 24 ++++ tests/ui/for_loop_over_fallible.rs | 57 ---------- tests/ui/for_loop_over_fallible.stderr | 71 ------------ tests/ui/for_loops_over_fallibles.rs | 57 ++++++++++ tests/ui/for_loops_over_fallibles.stderr | 71 ++++++++++++ 20 files changed, 556 insertions(+), 556 deletions(-) delete mode 100644 clippy_lints/src/block_in_if_condition.rs create mode 100644 clippy_lints/src/blocks_in_if_conditions.rs delete mode 100644 tests/ui/block_in_if_condition.fixed delete mode 100644 tests/ui/block_in_if_condition.rs delete mode 100644 tests/ui/block_in_if_condition.stderr delete mode 100644 tests/ui/block_in_if_condition_closure.rs delete mode 100644 tests/ui/block_in_if_condition_closure.stderr create mode 100644 tests/ui/blocks_in_if_conditions.fixed create mode 100644 tests/ui/blocks_in_if_conditions.rs create mode 100644 tests/ui/blocks_in_if_conditions.stderr create mode 100644 tests/ui/blocks_in_if_conditions_closure.rs create mode 100644 tests/ui/blocks_in_if_conditions_closure.stderr delete mode 100644 tests/ui/for_loop_over_fallible.rs delete mode 100644 tests/ui/for_loop_over_fallible.stderr create mode 100644 tests/ui/for_loops_over_fallibles.rs create mode 100644 tests/ui/for_loops_over_fallibles.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 77272f4f78b..d05819a973a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,7 +198,7 @@ Released 2020-03-12 ### Suggestion Improvements -* [`option_map_unwrap_or`] [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) * [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934) * [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935) * [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956) @@ -282,8 +282,8 @@ Released 2019-12-19 * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) - * [`option_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) - * [`result_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509) * Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736) * Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613) @@ -395,7 +395,7 @@ Released 2019-08-15 * Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107) * Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214) * Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136) -* Improve suggestions for [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) * Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119) * Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137) * Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071) @@ -448,7 +448,7 @@ Released 2019-05-20 * Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()` * Fix false positive in [`bool_comparison`] pertaining to non-bool types * Fix false positive in [`redundant_closure`] pertaining to differences in borrows -* Fix false positive in [`option_map_unwrap_or`] on non-copy types +* Fix false positive in `option_map_unwrap_or` on non-copy types * Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls * Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros * Fix false positive in [`needless_continue`] pertaining to loop labels @@ -794,7 +794,7 @@ Released 2018-09-13 ## 0.0.169 * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* -* New lints: [`just_underscores_and_digits`], [`result_map_unwrap_or_else`], [`transmute_bytes_to_str`] +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* @@ -1068,7 +1068,7 @@ Released 2018-09-13 ## 0.0.93 — 2016-10-03 * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* -* [`option_map_unwrap_or`] and [`option_map_unwrap_or_else`] are now +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] @@ -1087,8 +1087,8 @@ Released 2018-09-13 ## 0.0.88 — 2016-09-04 * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` - lint groups: [`filter_next`], [`for_loop_over_option`], - [`for_loop_over_result`] and [`match_overlapping_arm`]. You should now be + lint groups: [`filter_next`], `for_loop_over_option`, + `for_loop_over_result` and [`match_overlapping_arm`]. You should now be able to `#[allow/deny]` them individually and they are available directly through `cargo clippy`. @@ -1274,7 +1274,7 @@ Released 2018-09-13 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name -[`block_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box @@ -1361,7 +1361,7 @@ Released 2018-09-13 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map -[`for_loop_over_fallible`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_fallible +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/block_in_if_condition.rs deleted file mode 100644 index 8a5e595749f..00000000000 --- a/clippy_lints/src/block_in_if_condition.rs +++ /dev/null @@ -1,145 +0,0 @@ -use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BlockCheckMode, Expr, ExprKind}; -use rustc_lint::{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}; - -declare_clippy_lint! { - /// **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?** Style, using blocks in the condition makes it hard to read. - /// - /// **Known problems:** None. - /// - /// **Examples:** - /// ```rust - /// // Bad - /// if { true } { /* ... */ } - /// - /// // Good - /// if true { /* ... */ } - /// ``` - /// - /// // or - /// - /// ```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, - style, - "useless or complex blocks that can be eliminated in conditions" -} - -declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION]); - -struct ExVisitor<'a, 'tcx> { - found_block: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'a, 'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { - let body = self.cx.tcx.hir().body(eid); - let ex = &body.value; - if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { - self.found_block = Some(ex); - return; - } - } - walk_expr(self, expr); - } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - -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`"; - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - if let Some((cond, _, _)) = higher::if_block(&expr) { - if let ExprKind::Block(block, _) = &cond.kind { - if block.rules == BlockCheckMode::DefaultBlock { - if block.stmts.is_empty() { - if let Some(ex) = &block.expr { - // don't dig into the expression here, just suggest that they remove - // the block - if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BLOCK_IN_IF_CONDITION, - cond.span, - BRACED_EXPR_MESSAGE, - "try", - format!( - "{}", - snippet_block_with_applicability( - cx, - ex.span, - "..", - Some(expr.span), - &mut applicability - ) - ), - applicability, - ); - } - } else { - let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); - if span.from_expansion() || differing_macro_contexts(expr.span, span) { - return; - } - // move block higher - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BLOCK_IN_IF_CONDITION, - expr.span.with_hi(cond.span.hi()), - COMPLEX_BLOCK_MESSAGE, - "try", - format!( - "let res = {}; if res", - snippet_block_with_applicability( - cx, - block.span, - "..", - Some(expr.span), - &mut applicability - ), - ), - applicability, - ); - } - } - } else { - 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); - } - } - } - } -} diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs new file mode 100644 index 00000000000..8fa9b05ca32 --- /dev/null +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -0,0 +1,145 @@ +use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind}; +use rustc_lint::{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}; + +declare_clippy_lint! { + /// **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?** Style, using blocks in the condition makes it hard to read. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// // Bad + /// if { true } { /* ... */ } + /// + /// // Good + /// if true { /* ... */ } + /// ``` + /// + /// // or + /// + /// ```rust + /// # fn somefunc() -> bool { true }; + /// + /// // Bad + /// if { let x = somefunc(); x } { /* ... */ } + /// + /// // Good + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } + /// ``` + pub BLOCKS_IN_IF_CONDITIONS, + style, + "useless or complex blocks that can be eliminated in conditions" +} + +declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); + +struct ExVisitor<'a, 'tcx> { + found_block: Option<&'tcx Expr<'tcx>>, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { + let body = self.cx.tcx.hir().body(eid); + let ex = &body.value; + if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { + self.found_block = Some(ex); + return; + } + } + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +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`"; + +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; + } + if let Some((cond, _, _)) = higher::if_block(&expr) { + if let ExprKind::Block(block, _) = &cond.kind { + if block.rules == BlockCheckMode::DefaultBlock { + if block.stmts.is_empty() { + if let Some(ex) = &block.expr { + // don't dig into the expression here, just suggest that they remove + // the block + if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_IF_CONDITIONS, + cond.span, + BRACED_EXPR_MESSAGE, + "try", + format!( + "{}", + snippet_block_with_applicability( + cx, + ex.span, + "..", + Some(expr.span), + &mut applicability + ) + ), + applicability, + ); + } + } else { + let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); + if span.from_expansion() || differing_macro_contexts(expr.span, span) { + return; + } + // move block higher + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_IF_CONDITIONS, + expr.span.with_hi(cond.span.hi()), + COMPLEX_BLOCK_MESSAGE, + "try", + format!( + "let res = {}; if res", + snippet_block_with_applicability( + cx, + block.span, + "..", + Some(expr.span), + &mut applicability + ), + ), + applicability, + ); + } + } + } else { + let mut visitor = ExVisitor { found_block: None, cx }; + walk_expr(&mut visitor, cond); + if let Some(block) = visitor.found_block { + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ff67ccae794..eba4ab5056b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -180,7 +180,7 @@ mod attrs; mod await_holding_lock; mod bit_mask; mod blacklisted_name; -mod block_in_if_condition; +mod blocks_in_if_conditions; mod booleans; mod bytecount; mod cargo_common_metadata; @@ -507,7 +507,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, &blacklisted_name::BLACKLISTED_NAME, - &block_in_if_condition::BLOCK_IN_IF_CONDITION, + &blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, @@ -615,7 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_INTO_ITER_LOOP, &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, - &loops::FOR_LOOP_OVER_FALLIBLE, + &loops::FOR_LOOPS_OVER_FALLIBLES, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -894,7 +894,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed); store.register_late_pass(|| box len_zero::LenZero); store.register_late_pass(|| box attrs::Attributes); - store.register_late_pass(|| box block_in_if_condition::BlockInIfCondition); + store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); @@ -1199,7 +1199,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), @@ -1264,7 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1444,7 +1444,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), @@ -1639,7 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), @@ -1785,8 +1785,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::block_in_if_condition"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); @@ -1794,8 +1794,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loop_over_fallible"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loop_over_fallible"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index da6793a69d6..9c9d1a84003 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -205,7 +205,7 @@ declare_clippy_lint! { /// // .. /// } /// ``` - pub FOR_LOOP_OVER_FALLIBLE, + pub FOR_LOOPS_OVER_FALLIBLES, correctness, "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } @@ -426,7 +426,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -1290,7 +1290,7 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { if is_type_diagnostic_item(cx, ty, sym!(option_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ @@ -1307,7 +1307,7 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e90b9c15747..feada261a4c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -74,11 +74,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "blacklisted_name", }, Lint { - name: "block_in_if_condition", + name: "blocks_in_if_conditions", group: "style", desc: "useless or complex blocks that can be eliminated in conditions", deprecation: None, - module: "block_in_if_condition", + module: "blocks_in_if_conditions", }, Lint { name: "bool_comparison", @@ -676,7 +676,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "loops", }, Lint { - name: "for_loop_over_fallible", + name: "for_loops_over_fallibles", group: "correctness", desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", deprecation: None, diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/block_in_if_condition.fixed deleted file mode 100644 index ae01c6d3042..00000000000 --- a/tests/ui/block_in_if_condition.fixed +++ /dev/null @@ -1,74 +0,0 @@ -// run-rustfix -#![warn(clippy::block_in_if_condition)] -#![allow(unused, clippy::let_and_return)] -#![warn(clippy::nonminimal_bool)] - -macro_rules! blocky { - () => {{ - true - }}; -} - -macro_rules! blocky_too { - () => {{ - let r = true; - r - }}; -} - -fn macro_if() { - if blocky!() {} - - if blocky_too!() {} -} - -fn condition_has_block() -> i32 { - let res = { - let x = 3; - x == 3 - }; if res { - 6 - } else { - 10 - } -} - -fn condition_has_block_with_single_expression() -> i32 { - if true { - 6 - } else { - 10 - } -} - -fn condition_is_normal() -> i32 { - let x = 3; - if x == 3 { - 6 - } else { - 10 - } -} - -fn condition_is_unsafe_block() { - let a: i32 = 1; - - // this should not warn because the condition is an unsafe block - if unsafe { 1u32 == std::mem::transmute(a) } { - println!("1u32 == a"); - } -} - -fn block_in_assert() { - let opt = Some(42); - assert!(opt - .as_ref() - .and_then(|val| { - let mut v = val * 2; - v -= 1; - Some(v * 3) - }) - .is_some()); -} - -fn main() {} diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/block_in_if_condition.rs deleted file mode 100644 index 88555dc47c2..00000000000 --- a/tests/ui/block_in_if_condition.rs +++ /dev/null @@ -1,74 +0,0 @@ -// run-rustfix -#![warn(clippy::block_in_if_condition)] -#![allow(unused, clippy::let_and_return)] -#![warn(clippy::nonminimal_bool)] - -macro_rules! blocky { - () => {{ - true - }}; -} - -macro_rules! blocky_too { - () => {{ - let r = true; - r - }}; -} - -fn macro_if() { - if blocky!() {} - - if blocky_too!() {} -} - -fn condition_has_block() -> i32 { - if { - let x = 3; - x == 3 - } { - 6 - } else { - 10 - } -} - -fn condition_has_block_with_single_expression() -> i32 { - if { true } { - 6 - } else { - 10 - } -} - -fn condition_is_normal() -> i32 { - let x = 3; - if true && x == 3 { - 6 - } else { - 10 - } -} - -fn condition_is_unsafe_block() { - let a: i32 = 1; - - // this should not warn because the condition is an unsafe block - if unsafe { 1u32 == std::mem::transmute(a) } { - println!("1u32 == a"); - } -} - -fn block_in_assert() { - let opt = Some(42); - assert!(opt - .as_ref() - .and_then(|val| { - let mut v = val * 2; - v -= 1; - Some(v * 3) - }) - .is_some()); -} - -fn main() {} diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/block_in_if_condition.stderr deleted file mode 100644 index 89e9ad26f49..00000000000 --- a/tests/ui/block_in_if_condition.stderr +++ /dev/null @@ -1,34 +0,0 @@ -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 - | -LL | / if { -LL | | let x = 3; -LL | | x == 3 -LL | | } { - | |_____^ - | - = note: `-D clippy::block-in-if-condition` implied by `-D warnings` -help: try - | -LL | let res = { -LL | let x = 3; -LL | x == 3 -LL | }; if res { - | - -error: omit braces around single expression condition - --> $DIR/block_in_if_condition.rs:37:8 - | -LL | if { true } { - | ^^^^^^^^ help: try: `true` - -error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:46:8 - | -LL | if true && x == 3 { - | ^^^^^^^^^^^^^^ help: try: `x == 3` - | - = note: `-D clippy::nonminimal-bool` implied by `-D warnings` - -error: aborting due to 3 previous errors - diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/block_in_if_condition_closure.rs deleted file mode 100644 index 87b3fb94daf..00000000000 --- a/tests/ui/block_in_if_condition_closure.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![warn(clippy::block_in_if_condition)] -#![allow(unused, clippy::let_and_return)] - -fn predicate bool, T>(pfn: F, val: T) -> bool { - pfn(val) -} - -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 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 - && sky == "blue" - && predicate( - |x| { - let target = 3; - x == target - }, - v, - ) - {} - - if predicate( - |x| { - let target = 3; - x == target - }, - v, - ) {} -} - -fn closure_without_block() { - if predicate(|x| x == 3, 6) {} -} - -fn macro_in_closure() { - let option = Some(true); - - if option.unwrap_or_else(|| unimplemented!()) { - unimplemented!() - } -} - -fn main() {} diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/block_in_if_condition_closure.stderr deleted file mode 100644 index 3df25691c3c..00000000000 --- a/tests/ui/block_in_if_condition_closure.stderr +++ /dev/null @@ -1,24 +0,0 @@ -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 - | -LL | |x| { - | _________________^ -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_____________^ - | - = 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:27:13 - | -LL | |x| { - | _____________^ -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/blocks_in_if_conditions.fixed b/tests/ui/blocks_in_if_conditions.fixed new file mode 100644 index 00000000000..9040552cefc --- /dev/null +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -0,0 +1,74 @@ +// run-rustfix +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + let res = { + let x = 3; + x == 3 + }; if res { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if true { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .and_then(|val| { + let mut v = val * 2; + v -= 1; + Some(v * 3) + }) + .is_some()); +} + +fn main() {} diff --git a/tests/ui/blocks_in_if_conditions.rs b/tests/ui/blocks_in_if_conditions.rs new file mode 100644 index 00000000000..2fe409b22d3 --- /dev/null +++ b/tests/ui/blocks_in_if_conditions.rs @@ -0,0 +1,74 @@ +// run-rustfix +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + if { + let x = 3; + x == 3 + } { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if { true } { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if true && x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .and_then(|val| { + let mut v = val * 2; + v -= 1; + Some(v * 3) + }) + .is_some()); +} + +fn main() {} diff --git a/tests/ui/blocks_in_if_conditions.stderr b/tests/ui/blocks_in_if_conditions.stderr new file mode 100644 index 00000000000..9bdddc8e152 --- /dev/null +++ b/tests/ui/blocks_in_if_conditions.stderr @@ -0,0 +1,34 @@ +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/blocks_in_if_conditions.rs:26:5 + | +LL | / if { +LL | | let x = 3; +LL | | x == 3 +LL | | } { + | |_____^ + | + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` +help: try + | +LL | let res = { +LL | let x = 3; +LL | x == 3 +LL | }; if res { + | + +error: omit braces around single expression condition + --> $DIR/blocks_in_if_conditions.rs:37:8 + | +LL | if { true } { + | ^^^^^^^^ help: try: `true` + +error: this boolean expression can be simplified + --> $DIR/blocks_in_if_conditions.rs:46:8 + | +LL | if true && x == 3 { + | ^^^^^^^^^^^^^^ help: try: `x == 3` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/blocks_in_if_conditions_closure.rs b/tests/ui/blocks_in_if_conditions_closure.rs new file mode 100644 index 00000000000..acbabfa20d7 --- /dev/null +++ b/tests/ui/blocks_in_if_conditions_closure.rs @@ -0,0 +1,47 @@ +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] + +fn predicate bool, T>(pfn: F, val: T) -> bool { + pfn(val) +} + +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 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 + && sky == "blue" + && predicate( + |x| { + let target = 3; + x == target + }, + v, + ) + {} + + if predicate( + |x| { + let target = 3; + x == target + }, + v, + ) {} +} + +fn closure_without_block() { + if predicate(|x| x == 3, 6) {} +} + +fn macro_in_closure() { + let option = Some(true); + + if option.unwrap_or_else(|| unimplemented!()) { + unimplemented!() + } +} + +fn main() {} diff --git a/tests/ui/blocks_in_if_conditions_closure.stderr b/tests/ui/blocks_in_if_conditions_closure.stderr new file mode 100644 index 00000000000..941d604dd5f --- /dev/null +++ b/tests/ui/blocks_in_if_conditions_closure.stderr @@ -0,0 +1,24 @@ +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/blocks_in_if_conditions_closure.rs:18:17 + | +LL | |x| { + | _________________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_____________^ + | + = 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/blocks_in_if_conditions_closure.rs:27:13 + | +LL | |x| { + | _____________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_________^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/for_loop_over_fallible.rs b/tests/ui/for_loop_over_fallible.rs deleted file mode 100644 index e52468cdd4b..00000000000 --- a/tests/ui/for_loop_over_fallible.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![warn(clippy::for_loop_over_fallible)] - -fn for_loop_over_fallible() { - let option = Some(1); - let result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check over an `Option` - for x in option { - println!("{}", x); - } - - // check over a `Result` - for x in result { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loop_over_fallible.stderr b/tests/ui/for_loop_over_fallible.stderr deleted file mode 100644 index 4ce9a144ad8..00000000000 --- a/tests/ui/for_loop_over_fallible.stderr +++ /dev/null @@ -1,71 +0,0 @@ -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 - | -LL | for x in option { - | ^^^^^^ - | - = 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_fallible.rs:14:14 - | -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 - | -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 - | -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 - | -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 - | -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 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loop_over_fallible.rs:51:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs new file mode 100644 index 00000000000..1b9dde87cd5 --- /dev/null +++ b/tests/ui/for_loops_over_fallibles.rs @@ -0,0 +1,57 @@ +#![warn(clippy::for_loops_over_fallibles)] + +fn for_loops_over_fallibles() { + let option = Some(1); + let result = option.ok_or("x not found"); + let v = vec![0, 1, 2]; + + // check over an `Option` + for x in option { + println!("{}", x); + } + + // check over a `Result` + for x in result { + println!("{}", x); + } + + for x in option.ok_or("x not found") { + println!("{}", x); + } + + // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call + // in the chain + for x in v.iter().next() { + println!("{}", x); + } + + // make sure we lint when next() is not the last call in the chain + for x in v.iter().next().and(Some(0)) { + println!("{}", x); + } + + for x in v.iter().next().ok_or("x not found") { + println!("{}", x); + } + + // check for false positives + + // for loop false positive + for x in v { + println!("{}", x); + } + + // while let false positive for Option + while let Some(x) = option { + println!("{}", x); + break; + } + + // while let false positive for Result + while let Ok(x) = result { + println!("{}", x); + break; + } +} + +fn main() {} diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr new file mode 100644 index 00000000000..bef228d4b93 --- /dev/null +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -0,0 +1,71 @@ +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:9:14 + | +LL | for x in option { + | ^^^^^^ + | + = 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_loops_over_fallibles.rs:14:14 + | +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_loops_over_fallibles.rs:18:14 + | +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_loops_over_fallibles.rs:24:14 + | +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_loops_over_fallibles.rs:29:14 + | +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_loops_over_fallibles.rs:33:14 + | +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_loops_over_fallibles.rs:45:5 + | +LL | / while let Some(x) = option { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/for_loops_over_fallibles.rs:51:5 + | +LL | / while let Ok(x) = result { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From fc8ab099c38952b91e38608c386314bde6dd2629 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 15 May 2020 21:17:37 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 088e4ab1921..78e07d25f67 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,4 +1,5 @@ -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use if_chain::if_chain; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -32,7 +33,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if is_allowed(cx, cmp, left, right) { + return; + } match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { check(cx, left, 0, e.span, right.span); @@ -54,6 +58,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp { } } +fn is_allowed(cx: &LateContext<'_, '_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { + // `1 << 0` is a common pattern in bit manipulation code + if_chain! { + if let BinOpKind::Shl = cmp.node; + if let Some(Constant::Int(0)) = constant_simple(cx, cx.tables, right); + if let Some(Constant::Int(1)) = constant_simple(cx, cx.tables, left); + then { + return true; + } + } + + false +} + #[allow(clippy::cast_possible_wrap)] fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.tables, e) { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index ae2815d345a..ceaacaaf6bd 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -33,4 +33,9 @@ fn main() { let u: u8 = 0; u & 255; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42 << 0; + 1 >> 0; + 42 >> 0; } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 4742877706a..d8d44a74f9a 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -48,5 +48,23 @@ error: the operation is ineffective. Consider reducing it to `u` LL | u & 255; | ^^^^^^^ -error: aborting due to 8 previous errors +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:38:5 + | +LL | 42 << 0; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:39:5 + | +LL | 1 >> 0; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:40:5 + | +LL | 42 >> 0; + | ^^^^^^^ + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 10313a2631efa6a01dc86199d554ce5a7c1bb51a Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Fri, 15 May 2020 22:33:37 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index efb424bcb7b..81ddc8c0067 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -139,7 +139,6 @@ impl Constant { .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { - #[allow(clippy::match_wildcard_for_single_variants)] match Self::partial_cmp(tcx, cmp_type, lv, rv) { Some(Equal) => Some(ls.cmp(rs)), x => x, @@ -355,14 +354,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - Some(_) | None => None, + _ => None, }, (Some(Constant::Vec(vec)), _) => { if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { match vec.get(0) { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), - Some(_) | None => None, + _ => None, } } else { None @@ -533,7 +532,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - Some(_) | None => None, + _ => None, }, ty::Float(FloatTy::F64) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc @@ -547,7 +546,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }) .collect::>>() .map(Constant::Vec), - Some(_) | None => None, + _ => None, }, // FIXME: implement other array type conversions. _ => None, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 615afee33ef..1ec60a0e6e6 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -95,12 +95,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { match map.find(id) { Some(Node::Binding(_)) => (), - Some(_) | None => return false, + _ => return false, } match map.find(map.get_parent_node(id)) { Some(Node::Param(_)) => true, - Some(_) | None => false, + _ => false, } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 8c61b2f8664..86317fb8bd5 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -410,7 +410,7 @@ fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { Some(Constant::Int(i)) => i == 0, Some(Constant::F32(f)) => f == 0.0, Some(Constant::F64(f)) => f == 0.0, - Some(_) | None => false, + _ => false, } } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 39908bff5ed..0bc6b70855b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2154,7 +2154,7 @@ fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Ex } }, Some(Node::Stmt(_)) => (), - Some(_) | None => { + _ => { return false; }, } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 38c2645d36e..e1d524c2231 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -509,7 +509,7 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> boo Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), _ => false, }), - Some(_) | None => false, + _ => false, } } diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 3bb3eb15d9c..4ca90455bc4 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -37,7 +37,6 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { - #[allow(clippy::match_wildcard_for_single_variants)] match constant(cx, cx.tables, operand) { Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind { ty::Int(ity) => { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 7bc8be492e8..3b8ef18bfab 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -370,7 +370,6 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O /// Checks whether this type implements `Drop`. pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - #[allow(clippy::match_wildcard_for_single_variants)] match ty.ty_adt_def() { Some(def) => def.has_dtor(cx.tcx), _ => false, @@ -445,7 +444,6 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); - #[allow(clippy::match_wildcard_for_single_variants)] match cx.tcx.hir().find(parent_id) { Some( Node::Item(Item { ident, .. }) -- cgit 1.4.1-3-g733a5 From 2620d2449da851171773f7bec1396af11babe278 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sat, 16 May 2020 00:06:52 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 6fdb4cf9cd7..7d722820800 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -764,9 +764,21 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ if let QPath::Resolved(_, p) = path { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } - } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind { + } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind { if let QPath::Resolved(_, p) = path { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + // Some simple checks for exhaustive patterns. + // There is a room for improvements to detect more cases, + // but it can be more expensive to do so. + let is_pattern_exhaustive = |pat: &&Pat<'_>| { + if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { + true + } else { + false + } + }; + if patterns.iter().all(is_pattern_exhaustive) { + missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + } } } } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3b8ef18bfab..7545235e646 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -372,7 +372,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.ty_adt_def() { Some(def) => def.has_dtor(cx.tcx), - _ => false, + None => false, } } -- cgit 1.4.1-3-g733a5 From e55b920970fdc33f5ddaf7757738fbacdadf15ab Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 4 May 2020 17:09:02 +0200 Subject: Rename lint `identity_conversion` to `useless_conversion` --- CHANGELOG.md | 4 +- clippy_lints/src/identity_conversion.rs | 124 ------------------------------ clippy_lints/src/lib.rs | 11 +-- clippy_lints/src/useless_conversion.rs | 130 ++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 14 ++-- tests/ui/identity_conversion.fixed | 58 -------------- tests/ui/identity_conversion.rs | 58 -------------- tests/ui/identity_conversion.stderr | 68 ----------------- tests/ui/useless_conversion.fixed | 58 ++++++++++++++ tests/ui/useless_conversion.rs | 58 ++++++++++++++ tests/ui/useless_conversion.stderr | 68 +++++++++++++++++ 11 files changed, 329 insertions(+), 322 deletions(-) delete mode 100644 clippy_lints/src/identity_conversion.rs create mode 100644 clippy_lints/src/useless_conversion.rs delete mode 100644 tests/ui/identity_conversion.fixed delete mode 100644 tests/ui/identity_conversion.rs delete mode 100644 tests/ui/identity_conversion.stderr create mode 100644 tests/ui/useless_conversion.fixed create mode 100644 tests/ui/useless_conversion.rs create mode 100644 tests/ui/useless_conversion.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d05819a973a..9e85e6da3b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -805,7 +805,7 @@ Released 2018-09-13 ## 0.0.166 * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* -* New lints: [`explicit_write`], [`identity_conversion`], [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] @@ -1367,7 +1367,6 @@ Released 2018-09-13 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap -[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion [`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op [`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex [`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching @@ -1624,6 +1623,7 @@ Released 2018-09-13 [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute diff --git a/clippy_lints/src/identity_conversion.rs b/clippy_lints/src/identity_conversion.rs deleted file mode 100644 index 33a9478f058..00000000000 --- a/clippy_lints/src/identity_conversion.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::utils::{ - match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, -}; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; -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. - /// - /// **Why is this bad?** Redundant code. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // format!() returns a `String` - /// let s: String = format!("hello").into(); - /// ``` - pub IDENTITY_CONVERSION, - complexity, - "using always-identical `Into`/`From`/`IntoIter` conversions" -} - -#[derive(Default)] -pub struct IdentityConversion { - try_desugar_arm: Vec, -} - -impl_lint_pass!(IdentityConversion => [IDENTITY_CONVERSION]); - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { - return; - } - - if Some(&e.hir_id) == self.try_desugar_arm.last() { - return; - } - - match e.kind { - ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { - let e = match arms[0].body.kind { - ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, - _ => return, - }; - if let ExprKind::Call(_, ref args) = e.kind { - self.try_desugar_arm.push(args[0].hir_id); - } - }, - - ExprKind::MethodCall(ref name, .., ref args) => { - 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) { - let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); - - span_lint_and_sugg( - cx, - IDENTITY_CONVERSION, - e.span, - "identical conversion", - "consider removing `.into()`", - sugg, - Applicability::MachineApplicable, // snippet - ); - } - } - 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) { - let sugg = snippet(cx, args[0].span, "").into_owned(); - span_lint_and_sugg( - cx, - IDENTITY_CONVERSION, - e.span, - "identical conversion", - "consider removing `.into_iter()`", - sugg, - Applicability::MachineApplicable, // snippet - ); - } - } - }, - - 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 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 = - format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); - span_lint_and_sugg( - cx, - IDENTITY_CONVERSION, - e.span, - "identical conversion", - &sugg_msg, - sugg, - Applicability::MachineApplicable, // snippet - ); - } - } - } - } - }, - - _ => {}, - } - } - - fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { - if Some(&e.hir_id) == self.try_desugar_arm.last() { - self.try_desugar_arm.pop(); - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bda0d5c0458..4dda373738b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -221,7 +221,6 @@ mod formatting; mod functions; mod future_not_send; mod get_last_with_len; -mod identity_conversion; mod identity_op; mod if_let_mutex; mod if_let_some_result; @@ -324,6 +323,7 @@ mod unused_io_amount; mod unused_self; mod unwrap; mod use_self; +mod useless_conversion; mod vec; mod verbose_file_reads; mod wildcard_dependencies; @@ -577,7 +577,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &functions::TOO_MANY_LINES, &future_not_send::FUTURE_NOT_SEND, &get_last_with_len::GET_LAST_WITH_LEN, - &identity_conversion::IDENTITY_CONVERSION, &identity_op::IDENTITY_OP, &if_let_mutex::IF_LET_MUTEX, &if_let_some_result::IF_LET_SOME_RESULT, @@ -843,6 +842,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, &use_self::USE_SELF, + &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, @@ -980,7 +980,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); - store.register_late_pass(|| box identity_conversion::IdentityConversion::default()); + store.register_late_pass(|| box useless_conversion::UselessConversion::default()); store.register_late_pass(|| box types::ImplicitHasher); store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); store.register_late_pass(|| box types::UnitArg); @@ -1241,7 +1241,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_conversion::IDENTITY_CONVERSION), LintId::of(&identity_op::IDENTITY_OP), LintId::of(&if_let_mutex::IF_LET_MUTEX), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), @@ -1427,6 +1426,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), @@ -1546,7 +1546,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&format::USELESS_FORMAT), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_conversion::IDENTITY_CONVERSION), LintId::of(&identity_op::IDENTITY_OP), LintId::of(&int_plus_one::INT_PLUS_ONE), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -1605,6 +1604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); @@ -1795,6 +1795,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs new file mode 100644 index 00000000000..95921518986 --- /dev/null +++ b/clippy_lints/src/useless_conversion.rs @@ -0,0 +1,130 @@ +use crate::utils::{ + match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, +}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **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 USELESS_CONVERSION, + complexity, + "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" +} + +#[derive(Default)] +pub struct UselessConversion { + try_desugar_arm: Vec, +} + +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); + +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; + } + + if Some(&e.hir_id) == self.try_desugar_arm.last() { + return; + } + + match e.kind { + ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { + let e = match arms[0].body.kind { + ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, + _ => return, + }; + if let ExprKind::Call(_, ref args) = e.kind { + self.try_desugar_arm.push(args[0].hir_id); + } + }, + + ExprKind::MethodCall(ref name, .., ref args) => { + 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) { + let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); + + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + "consider removing `.into()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + 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) { + let sugg = snippet(cx, args[0].span, "").into_owned(); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + "consider removing `.into_iter()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + }, + + 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 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 = + format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + &sugg_msg, + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + } + } + }, + + _ => {}, + } + } + + fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if Some(&e.hir_id) == self.try_desugar_arm.last() { + self.try_desugar_arm.pop(); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e5e3bf453a0..e411e60782a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -717,13 +717,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, - Lint { - name: "identity_conversion", - group: "complexity", - desc: "using always-identical `Into`/`From`/`IntoIter` conversions", - deprecation: None, - module: "identity_conversion", - }, Lint { name: "identity_op", group: "complexity", @@ -2418,6 +2411,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "useless_conversion", + group: "complexity", + desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + deprecation: None, + module: "useless_conversion", + }, Lint { name: "useless_format", group: "complexity", diff --git a/tests/ui/identity_conversion.fixed b/tests/ui/identity_conversion.fixed deleted file mode 100644 index dd3fc56e98b..00000000000 --- a/tests/ui/identity_conversion.fixed +++ /dev/null @@ -1,58 +0,0 @@ -// run-rustfix - -#![deny(clippy::identity_conversion)] - -fn test_generic(val: T) -> T { - let _ = val; - val -} - -fn test_generic2 + Into, U: From>(val: T) { - // ok - let _: i32 = val.into(); - let _: U = val.into(); - let _ = U::from(val); -} - -fn test_questionmark() -> Result<(), ()> { - { - let _: i32 = 0i32; - Ok(Ok(())) - }??; - Ok(()) -} - -fn test_issue_3913() -> Result<(), std::io::Error> { - use std::fs; - use std::path::Path; - - let path = Path::new("."); - for _ in fs::read_dir(path)? {} - - Ok(()) -} - -fn main() { - test_generic(10i32); - test_generic2::(10i32); - test_questionmark().unwrap(); - test_issue_3913().unwrap(); - - let _: String = "foo".into(); - let _: String = From::from("foo"); - let _ = String::from("foo"); - #[allow(clippy::identity_conversion)] - { - let _: String = "foo".into(); - let _ = String::from("foo"); - let _ = "".lines().into_iter(); - } - - let _: String = "foo".to_string(); - let _: String = "foo".to_string(); - let _ = "foo".to_string(); - let _ = format!("A: {:04}", 123); - let _ = "".lines(); - let _ = vec![1, 2, 3].into_iter(); - let _: String = format!("Hello {}", "world"); -} diff --git a/tests/ui/identity_conversion.rs b/tests/ui/identity_conversion.rs deleted file mode 100644 index 875ed7db373..00000000000 --- a/tests/ui/identity_conversion.rs +++ /dev/null @@ -1,58 +0,0 @@ -// run-rustfix - -#![deny(clippy::identity_conversion)] - -fn test_generic(val: T) -> T { - let _ = T::from(val); - val.into() -} - -fn test_generic2 + Into, U: From>(val: T) { - // ok - let _: i32 = val.into(); - let _: U = val.into(); - let _ = U::from(val); -} - -fn test_questionmark() -> Result<(), ()> { - { - let _: i32 = 0i32.into(); - Ok(Ok(())) - }??; - Ok(()) -} - -fn test_issue_3913() -> Result<(), std::io::Error> { - use std::fs; - use std::path::Path; - - let path = Path::new("."); - for _ in fs::read_dir(path)? {} - - Ok(()) -} - -fn main() { - test_generic(10i32); - test_generic2::(10i32); - test_questionmark().unwrap(); - test_issue_3913().unwrap(); - - let _: String = "foo".into(); - let _: String = From::from("foo"); - let _ = String::from("foo"); - #[allow(clippy::identity_conversion)] - { - let _: String = "foo".into(); - let _ = String::from("foo"); - let _ = "".lines().into_iter(); - } - - let _: String = "foo".to_string().into(); - let _: String = From::from("foo".to_string()); - let _ = String::from("foo".to_string()); - let _ = String::from(format!("A: {:04}", 123)); - let _ = "".lines().into_iter(); - let _ = vec![1, 2, 3].into_iter().into_iter(); - let _: String = format!("Hello {}", "world").into(); -} diff --git a/tests/ui/identity_conversion.stderr b/tests/ui/identity_conversion.stderr deleted file mode 100644 index 57626b23795..00000000000 --- a/tests/ui/identity_conversion.stderr +++ /dev/null @@ -1,68 +0,0 @@ -error: identical conversion - --> $DIR/identity_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 - | -LL | #![deny(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: identical conversion - --> $DIR/identity_conversion.rs:7:5 - | -LL | val.into() - | ^^^^^^^^^^ help: consider removing `.into()`: `val` - -error: identical conversion - --> $DIR/identity_conversion.rs:19:22 - | -LL | let _: i32 = 0i32.into(); - | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` - -error: identical conversion - --> $DIR/identity_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 - | -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 - | -LL | let _ = String::from("foo".to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` - -error: identical conversion - --> $DIR/identity_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 - | -LL | let _ = "".lines().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` - -error: identical conversion - --> $DIR/identity_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 - | -LL | let _: String = format!("Hello {}", "world").into(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` - -error: aborting due to 10 previous errors - diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed new file mode 100644 index 00000000000..fdd4bc581f3 --- /dev/null +++ b/tests/ui/useless_conversion.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::useless_conversion)] + +fn test_generic(val: T) -> T { + let _ = val; + val +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32; + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::useless_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string(); + let _: String = "foo".to_string(); + let _ = "foo".to_string(); + let _ = format!("A: {:04}", 123); + let _ = "".lines(); + let _ = vec![1, 2, 3].into_iter(); + let _: String = format!("Hello {}", "world"); +} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs new file mode 100644 index 00000000000..4cae745e7c0 --- /dev/null +++ b/tests/ui/useless_conversion.rs @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::useless_conversion)] + +fn test_generic(val: T) -> T { + let _ = T::from(val); + val.into() +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32.into(); + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::useless_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string().into(); + let _: String = From::from("foo".to_string()); + let _ = String::from("foo".to_string()); + let _ = String::from(format!("A: {:04}", 123)); + let _ = "".lines().into_iter(); + let _ = vec![1, 2, 3].into_iter().into_iter(); + let _: String = format!("Hello {}", "world").into(); +} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr new file mode 100644 index 00000000000..7df3507edfd --- /dev/null +++ b/tests/ui/useless_conversion.stderr @@ -0,0 +1,68 @@ +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/useless_conversion.rs:3:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: useless conversion + --> $DIR/useless_conversion.rs:7:5 + | +LL | val.into() + | ^^^^^^^^^^ help: consider removing `.into()`: `val` + +error: useless conversion + --> $DIR/useless_conversion.rs:19:22 + | +LL | let _: i32 = 0i32.into(); + | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` + +error: useless conversion + --> $DIR/useless_conversion.rs:51:21 + | +LL | let _: String = "foo".to_string().into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` + +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: useless conversion + --> $DIR/useless_conversion.rs:53:13 + | +LL | let _ = String::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` + +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: useless conversion + --> $DIR/useless_conversion.rs:55:13 + | +LL | let _ = "".lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` + +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: useless conversion + --> $DIR/useless_conversion.rs:57:21 + | +LL | let _: String = format!("Hello {}", "world").into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` + +error: aborting due to 10 previous errors + -- cgit 1.4.1-3-g733a5 From cb7f9679a63075b3fce2fdc69f7b02fe0f482464 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 25 Apr 2020 20:52:00 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 098d47bdd40..4e1c1f13140 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -115,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp { let rsnip = snippet(cx, r.span, "...").to_string(); multispan_sugg( diag, - "use the values directly".to_string(), + "use the values directly", vec![(left.span, lsnip), (right.span, rsnip)], ); }, diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9c9d1a84003..4a9c411d7c8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1134,7 +1134,7 @@ fn check_for_loop_range<'a, 'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator".to_string(), + "consider using an iterator", vec![ (pat.span, format!("({}, )", ident.name)), ( @@ -1163,7 +1163,7 @@ fn check_for_loop_range<'a, 'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator".to_string(), + "consider using an iterator", vec![(pat.span, "".to_string()), (arg.span, repl)], ); }, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f86535ef1e..bbf14374a1f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -820,7 +820,7 @@ fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_> span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { if !expr.span.from_expansion() { - multispan_sugg(diag, msg.to_owned(), suggs); + multispan_sugg(diag, msg, suggs); } }); } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index a21818701da..ed48ab54897 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -293,7 +293,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { ); spans.sort_by_key(|&(span, _)| span); } - multispan_sugg(diag, "consider taking a reference instead".to_string(), spans); + multispan_sugg(diag, "consider taking a reference instead", spans); }; span_lint_and_then( diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6d49f50d550..f50adbc48ab 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2206,7 +2206,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher { multispan_sugg( diag, - "consider adding a type parameter".to_string(), + "consider adding a type parameter", vec![ ( generics_suggestion_span, diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 24a1bdf1883..f6d87c8532e 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -1,6 +1,6 @@ //! Clippy wrappers around rustc's diagnostic functions. -use rustc_errors::{Applicability, CodeSuggestion, DiagnosticBuilder, Substitution, SubstitutionPart, SuggestionStyle}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::source_map::{MultiSpan, Span}; @@ -198,20 +198,20 @@ pub fn span_lint_and_sugg<'a, T: LintContext>( /// appear once per /// replacement. In human-readable format though, it only appears once before /// the whole suggestion. -pub fn multispan_sugg(diag: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I) +pub fn multispan_sugg(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I) where I: IntoIterator, { - let sugg = CodeSuggestion { - substitutions: vec![Substitution { - parts: sugg - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }], - msg: help_msg, - style: SuggestionStyle::ShowCode, - applicability: Applicability::Unspecified, - }; - diag.suggestions.push(sugg); + multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg) +} + +pub fn multispan_sugg_with_applicability( + diag: &mut DiagnosticBuilder<'_>, + help_msg: &str, + applicability: Applicability, + sugg: I, +) where + I: IntoIterator, +{ + diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability); } -- cgit 1.4.1-3-g733a5 From 07f1edf2d43efa0ef53e5b6c56c895bc9738ab94 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 25 Apr 2020 23:33:11 +0300 Subject: 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 +- clippy_lints/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 +- tests/ui/bind_instead_of_map.fixed | 25 ++ tests/ui/bind_instead_of_map.rs | 25 ++ tests/ui/bind_instead_of_map.stderr | 26 ++ 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_and_then_some.fixed | 25 -- tests/ui/option_and_then_some.rs | 25 -- tests/ui/option_and_then_some.stderr | 20 -- tests/ui/option_map_or_none.fixed | 2 +- tests/ui/option_map_or_none.rs | 2 +- 19 files changed, 564 insertions(+), 166 deletions(-) create mode 100644 clippy_lints/src/methods/bind_instead_of_map.rs create mode 100644 tests/ui/bind_instead_of_map.fixed create mode 100644 tests/ui/bind_instead_of_map.rs create mode 100644 tests/ui/bind_instead_of_map.stderr create mode 100644 tests/ui/bind_instead_of_map_multipart.rs create mode 100644 tests/ui/bind_instead_of_map_multipart.stderr delete mode 100644 tests/ui/option_and_then_some.fixed delete mode 100644 tests/ui/option_and_then_some.rs delete mode 100644 tests/ui/option_and_then_some.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d05819a973a..7abefe65424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -315,7 +315,7 @@ Released 2019-11-07 * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535) * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511) * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394) - * [`option_and_then_some`] [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) + * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498) * Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348) * Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403) @@ -1273,6 +1273,7 @@ Released 2018-09-13 [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison @@ -1494,7 +1495,6 @@ Released 2018-09-13 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref -[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bda0d5c0458..ec198b684b6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -650,6 +650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &mem_replace::MEM_REPLACE_OPTION_WITH_NONE, &mem_replace::MEM_REPLACE_WITH_DEFAULT, &mem_replace::MEM_REPLACE_WITH_UNINIT, + &methods::BIND_INSTEAD_OF_MAP, &methods::CHARS_LAST_CMP, &methods::CHARS_NEXT_CMP, &methods::CLONE_DOUBLE_REF, @@ -676,7 +677,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, - &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, @@ -1291,6 +1291,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(&methods::BIND_INSTEAD_OF_MAP), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_DOUBLE_REF), @@ -1307,7 +1308,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), - LintId::of(&methods::OPTION_AND_THEN_SOME), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::OR_FUN_CALL), @@ -1559,10 +1559,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_SINGLE_BINDING), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(&methods::BIND_INSTEAD_OF_MAP), LintId::of(&methods::CLONE_ON_COPY), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::OPTION_AND_THEN_SOME), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), @@ -1784,6 +1784,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4a9c411d7c8..84e8a010738 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1462,7 +1462,7 @@ fn check_for_loop_over_map_kv<'a, 'tcx>( let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( diag, - "use the corresponding method".into(), + "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs new file mode 100644 index 00000000000..32e86637569 --- /dev/null +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -0,0 +1,309 @@ +use super::{contains_return, BIND_INSTEAD_OF_MAP}; +use crate::utils::{ + in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::Span; + +pub(crate) struct OptionAndThenSome; +impl BindInsteadOfMap for OptionAndThenSome { + const TYPE_NAME: &'static str = "Option"; + const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Some"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::OPTION_SOME; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultAndThenOk; +impl BindInsteadOfMap for ResultAndThenOk { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Ok"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_OK; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultOrElseErrInfo; +impl BindInsteadOfMap for ResultOrElseErrInfo { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "or_else"; + const BAD_VARIANT_NAME: &'static str = "Err"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_ERR; + + const GOOD_METHOD_NAME: &'static str = "map_err"; +} + +pub(crate) trait BindInsteadOfMap { + const TYPE_NAME: &'static str; + const TYPE_QPATH: &'static [&'static str]; + + const BAD_METHOD_NAME: &'static str; + const BAD_VARIANT_NAME: &'static str; + const BAD_VARIANT_QPATH: &'static [&'static str]; + + const GOOD_METHOD_NAME: &'static str; + + fn no_op_msg() -> String { + format!( + "using `{}.{}({})`, which is a no-op", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME + ) + } + + fn lint_msg() -> String { + format!( + "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME, + Self::GOOD_METHOD_NAME + ) + } + + fn lint_closure_autofixable( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + args: &[hir::Expr<'_>], + closure_expr: &hir::Expr<'_>, + closure_args_span: Span, + ) -> bool { + if_chain! { + if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; + if let hir::ExprKind::Path(ref qpath) = some_expr.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if some_args.len() == 1; + then { + let inner_expr = &some_args[0]; + + if contains_return(inner_expr) { + return false; + } + + let some_inner_snip = if inner_expr.span.from_expansion() { + snippet_with_macro_callsite(cx, inner_expr.span, "_") + } else { + snippet(cx, inner_expr.span, "_") + }; + + let closure_args_snip = snippet(cx, closure_args_span, ".."); + let option_snip = snippet(cx, args[0].span, ".."); + let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::lint_msg().as_ref(), + "try this", + note, + Applicability::MachineApplicable, + ); + true + } else { + false + } + } + } + + fn lint_closure(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + if_chain! { + if !in_macro(ret_expr.span); + if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; + if let hir::ExprKind::Path(ref qpath) = func_path.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if args.len() == 1; + if !contains_return(&args[0]); + then { + suggs.push((ret_expr.span, args[0].span.source_callsite())); + true + } else { + false + } + } + }); + + if can_sugg { + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, Self::lint_msg().as_ref(), |diag| { + multispan_sugg_with_applicability( + diag, + "try this", + Applicability::MachineApplicable, + std::iter::once((*method_calls(expr, 1).2.get(0).unwrap(), Self::GOOD_METHOD_NAME.into())).chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ), + ) + }); + } + } + + /// Lint use of `_.and_then(|x| Some(y))` for `Option`s + fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if !match_type(cx, cx.tables.expr_ty(&args[0]), Self::TYPE_QPATH) { + return; + } + + match args[1].kind { + hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + Self::lint_closure(cx, expr, closure_expr); + } + }, + // `_.and_then(Some)` case, which is no-op. + hir::ExprKind::Path(ref qpath) if match_qpath(qpath, Self::BAD_VARIANT_QPATH) => { + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::no_op_msg().as_ref(), + "use the expression directly", + snippet(cx, args[0].span, "..").into(), + Applicability::MachineApplicable, + ); + }, + _ => {}, + } + } +} + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &hir::Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } + } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_, '_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir hir::Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + hir::ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), + hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e6094edc5d7..626427c15ec 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,3 +1,4 @@ +mod bind_instead_of_map; mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; @@ -7,6 +8,7 @@ use std::borrow::Cow; use std::fmt; use std::iter; +use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -306,27 +308,34 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`. + /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or + /// `_.or_else(|x| Err(y))`. /// /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map(|x| y)`. + /// `_.map(|x| y)` or `_.map_err(|x| y)`. /// /// **Known problems:** None /// /// **Example:** /// /// ```rust - /// let x = Some("foo"); - /// let _ = x.and_then(|s| Some(s.len())); + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().and_then(|s| Some(s.len())); + /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); + /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); /// ``` /// /// The correct use would be: /// /// ```rust - /// let x = Some("foo"); - /// let _ = x.map(|s| s.len()); + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().map(|s| s.len()); + /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); + /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); /// ``` - pub OPTION_AND_THEN_SOME, + pub BIND_INSTEAD_OF_MAP, complexity, "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } @@ -1243,7 +1252,7 @@ declare_lint_pass!(Methods => [ MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, - OPTION_AND_THEN_SOME, + BIND_INSTEAD_OF_MAP, OR_FUN_CALL, EXPECT_FUN_CALL, CHARS_NEXT_CMP, @@ -1302,7 +1311,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), - ["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]), + ["and_then", ..] => { + bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + }, + ["or_else", ..] => { + bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2601,73 +2616,6 @@ fn lint_map_or_none<'a, 'tcx>( ); } -/// Lint use of `_.and_then(|x| Some(y))` for `Option`s -fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"; - const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op"; - - let ty = cx.tables.expr_ty(&args[0]); - if !is_type_diagnostic_item(cx, ty, sym!(option_type)) { - return; - } - - match args[1].kind { - hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); - if_chain! { - if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; - if let hir::ExprKind::Path(ref qpath) = some_expr.kind; - if match_qpath(qpath, &paths::OPTION_SOME); - if some_args.len() == 1; - then { - let inner_expr = &some_args[0]; - - if contains_return(inner_expr) { - return; - } - - let some_inner_snip = if inner_expr.span.from_expansion() { - snippet_with_macro_callsite(cx, inner_expr.span, "_") - } else { - snippet(cx, inner_expr.span, "_") - }; - - let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, args[0].span, ".."); - let note = format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip); - span_lint_and_sugg( - cx, - OPTION_AND_THEN_SOME, - expr.span, - LINT_MSG, - "try this", - note, - Applicability::MachineApplicable, - ); - } - } - }, - // `_.and_then(Some)` case, which is no-op. - hir::ExprKind::Path(ref qpath) => { - if match_qpath(qpath, &paths::OPTION_SOME) { - let option_snip = snippet(cx, args[0].span, ".."); - let note = format!("{}", option_snip); - span_lint_and_sugg( - cx, - OPTION_AND_THEN_SOME, - expr.span, - NO_OP_MSG, - "use the expression directly", - note, - Applicability::MachineApplicable, - ); - } - }, - _ => {}, - } -} - /// lint use of `filter().next()` for `Iterators` fn lint_filter_next<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f50adbc48ab..6ed9ff22e46 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2230,7 +2230,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher { ); if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor".into(), vis.suggestions); + multispan_sugg(diag, "...and use generic constructor", vis.suggestions); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e5e3bf453a0..5b4e2906b5f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -66,6 +66,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bit_mask", }, + Lint { + name: "bind_instead_of_map", + group: "complexity", + desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", + deprecation: None, + module: "methods", + }, Lint { name: "blacklisted_name", group: "style", @@ -1578,13 +1585,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "eq_op", }, - Lint { - name: "option_and_then_some", - group: "complexity", - desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", - deprecation: None, - module: "methods", - }, Lint { name: "option_as_ref_deref", group: "complexity", diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed new file mode 100644 index 00000000000..5815550d7a6 --- /dev/null +++ b/tests/ui/bind_instead_of_map.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x; + let _ = x.map(|o| o + 1); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result = Ok(1); + let _ = x; +} + +pub fn foo() -> Option { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs new file mode 100644 index 00000000000..623b100a4ce --- /dev/null +++ b/tests/ui/bind_instead_of_map.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x.and_then(Some); + let _ = x.and_then(|o| Some(o + 1)); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result = Ok(1); + let _ = x.and_then(Ok); +} + +pub fn foo() -> Option { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr new file mode 100644 index 00000000000..24c6b7f9ef3 --- /dev/null +++ b/tests/ui/bind_instead_of_map.stderr @@ -0,0 +1,26 @@ +error: using `Option.and_then(Some)`, which is a no-op + --> $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/bind_instead_of_map.rs:2:9 + | +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/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: using `Result.and_then(Ok)`, which is a no-op + --> $DIR/bind_instead_of_map.rs:15:13 + | +LL | let _ = x.and_then(Ok); + | ^^^^^^^^^^^^^^ help: use the expression directly: `x` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/bind_instead_of_map_multipart.rs b/tests/ui/bind_instead_of_map_multipart.rs new file mode 100644 index 00000000000..91d9d11e3c1 --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.rs @@ -0,0 +1,61 @@ +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").and_then(|s| { + if { + if s == "43" { + return Some(43); + } + s == "42" + } { + return Some(45); + } + match s.len() { + 10 => Some(2), + 20 => { + if foo() { + return { + if foo() { + return Some(20); + } + println!("foo"); + Some(3) + }; + } + Some(20) + }, + 40 => Some(30), + _ => Some(1), + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); +} diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr new file mode 100644 index 00000000000..50ce2f4051e --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -0,0 +1,73 @@ +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:5:13 + | +LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bind_instead_of_map_multipart.rs:1:9 + | +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try this + | +LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:8:13 + | +LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:11:13 + | +LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + | ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:19:5 + | +LL | / Some("42").and_then(|s| { +LL | | if { +LL | | if s == "43" { +LL | | return Some(43); +... | +LL | | } +LL | | }); + | |______^ + | +help: try this + | +LL | Some("42").map(|s| { +LL | if { +LL | if s == "43" { +LL | return 43; +LL | } +LL | s == "42" + ... + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:60:13 + | +LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); + | ^^^ ^^^^ ^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/blocks_in_if_conditions.fixed b/tests/ui/blocks_in_if_conditions.fixed index 9040552cefc..14562c4d32c 100644 --- a/tests/ui/blocks_in_if_conditions.fixed +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -63,10 +63,10 @@ fn block_in_assert() { let opt = Some(42); assert!(opt .as_ref() - .and_then(|val| { + .map(|val| { let mut v = val * 2; v -= 1; - Some(v * 3) + v * 3 }) .is_some()); } diff --git a/tests/ui/blocks_in_if_conditions.rs b/tests/ui/blocks_in_if_conditions.rs index 2fe409b22d3..bda87650f6d 100644 --- a/tests/ui/blocks_in_if_conditions.rs +++ b/tests/ui/blocks_in_if_conditions.rs @@ -63,10 +63,10 @@ fn block_in_assert() { let opt = Some(42); assert!(opt .as_ref() - .and_then(|val| { + .map(|val| { let mut v = val * 2; v -= 1; - Some(v * 3) + v * 3 }) .is_some()); } diff --git a/tests/ui/option_and_then_some.fixed b/tests/ui/option_and_then_some.fixed deleted file mode 100644 index 035bc1e5465..00000000000 --- a/tests/ui/option_and_then_some.fixed +++ /dev/null @@ -1,25 +0,0 @@ -// run-rustfix -#![deny(clippy::option_and_then_some)] - -// need a main anyway, use it get rid of unused warnings too -pub fn main() { - let x = Some(5); - // the easiest cases - let _ = x; - let _ = x.map(|o| o + 1); - // and an easy counter-example - let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); - - // Different type - let x: Result = Ok(1); - let _ = x.and_then(Ok); -} - -pub fn foo() -> Option { - let x = Some(String::from("hello")); - Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) -} - -pub fn example2(x: bool) -> Option<&'static str> { - Some("a").and_then(|s| Some(if x { s } else { return None })) -} diff --git a/tests/ui/option_and_then_some.rs b/tests/ui/option_and_then_some.rs deleted file mode 100644 index d49da7813c6..00000000000 --- a/tests/ui/option_and_then_some.rs +++ /dev/null @@ -1,25 +0,0 @@ -// run-rustfix -#![deny(clippy::option_and_then_some)] - -// need a main anyway, use it get rid of unused warnings too -pub fn main() { - let x = Some(5); - // the easiest cases - let _ = x.and_then(Some); - let _ = x.and_then(|o| Some(o + 1)); - // and an easy counter-example - let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); - - // Different type - let x: Result = Ok(1); - let _ = x.and_then(Ok); -} - -pub fn foo() -> Option { - let x = Some(String::from("hello")); - Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) -} - -pub fn example2(x: bool) -> Option<&'static str> { - Some("a").and_then(|s| Some(if x { s } else { return None })) -} diff --git a/tests/ui/option_and_then_some.stderr b/tests/ui/option_and_then_some.stderr deleted file mode 100644 index 47825491765..00000000000 --- a/tests/ui/option_and_then_some.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: using `Option.and_then(Some)`, which is a no-op - --> $DIR/option_and_then_some.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 - | -LL | #![deny(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 - | -LL | let _ = x.and_then(|o| Some(o + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/option_map_or_none.fixed b/tests/ui/option_map_or_none.fixed index decbae4e5af..d80c3c7c1b7 100644 --- a/tests/ui/option_map_or_none.fixed +++ b/tests/ui/option_map_or_none.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::option_and_then_some)] +#![allow(clippy::bind_instead_of_map)] fn main() { let opt = Some(1); diff --git a/tests/ui/option_map_or_none.rs b/tests/ui/option_map_or_none.rs index 0f1d2218d5d..629842419e5 100644 --- a/tests/ui/option_map_or_none.rs +++ b/tests/ui/option_map_or_none.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::option_and_then_some)] +#![allow(clippy::bind_instead_of_map)] fn main() { let opt = Some(1); -- cgit 1.4.1-3-g733a5 From 1b3dc5f79b98ba92c0b6fa98e62c8f331faa8ed6 Mon Sep 17 00:00:00 2001 From: Rahul Butani Date: Sun, 17 May 2020 22:21:02 -0500 Subject: Add to the list of words clippy::doc_markdown ignores --- clippy_lints/src/utils/conf.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 57b9eafd14d..9e8e0ff30ec 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -120,10 +120,12 @@ define_Conf! { "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", - "JavaScript", + "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", - "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap", + "OCaml", + "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", + "TensorFlow", "TrueType", "iOS", "macOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", -- cgit 1.4.1-3-g733a5 From f28f1f15da825bcf5cf78413f464dfea0bc553e5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 20 May 2020 13:32:53 +0200 Subject: Fix dogfood fallout --- clippy_lints/src/utils/inspector.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 748c11fac64..9b672b9ec22 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -289,21 +289,21 @@ fn print_expr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}operands:", ind); for op in asm.operands { match op { - hir::InlineAsmOperand::In { expr, .. } => print_expr(cx, expr, indent + 1), + hir::InlineAsmOperand::In { expr, .. } + | hir::InlineAsmOperand::InOut { expr, .. } + | hir::InlineAsmOperand::Const { expr } + | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { print_expr(cx, expr, indent + 1); } }, - hir::InlineAsmOperand::InOut { expr, .. } => print_expr(cx, expr, indent + 1), hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { print_expr(cx, in_expr, indent + 1); if let Some(out_expr) = out_expr { print_expr(cx, out_expr, indent + 1); } }, - hir::InlineAsmOperand::Const { expr } => print_expr(cx, expr, indent + 1), - hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), } } }, -- cgit 1.4.1-3-g733a5 From ecd0a67b01e13d7a80d2f64bbfa5da1e568367e5 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 20 May 2020 13:23:51 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4d4fff883b3..057d39d4c82 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1141,6 +1141,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), + LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), @@ -1285,7 +1286,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_SINGLE_BINDING), - LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), @@ -1476,7 +1476,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), - LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH), LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4106e5013b9..94380acfcfd 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -168,7 +168,7 @@ declare_clippy_lint! { /// **What it does:** Checks for arm which matches all errors with `Err(_)` /// and take drastic actions like `panic!`. /// - /// **Why is this bad?** It is generally a bad practice, just like + /// **Why is this bad?** It is generally a bad practice, similar to /// catching all exceptions in java with `catch(Exception)` /// /// **Known problems:** None. @@ -182,7 +182,7 @@ declare_clippy_lint! { /// } /// ``` pub MATCH_WILD_ERR_ARM, - style, + pedantic, "a `match` with `Err(_)` arm and take drastic actions" } @@ -711,7 +711,7 @@ fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) arm.pat.span, &format!("`Err({})` matches all errors", &ident_bind_name), None, - "match each error separately or use the error output", + "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable", ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0bf46491d31..8211a57b564 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1195,7 +1195,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "match_wild_err_arm", - group: "style", + group: "pedantic", desc: "a `match` with `Err(_)` arm and take drastic actions", deprecation: None, module: "matches", diff --git a/tests/ui/match_wild_err_arm.stderr b/tests/ui/match_wild_err_arm.stderr index 20d4c933418..6a2a02987de 100644 --- a/tests/ui/match_wild_err_arm.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -5,7 +5,7 @@ LL | Err(_) => panic!("err"), | ^^^^^^ | = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_)` matches all errors --> $DIR/match_wild_err_arm.rs:17:9 @@ -13,7 +13,7 @@ error: `Err(_)` matches all errors LL | Err(_) => panic!(), | ^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_)` matches all errors --> $DIR/match_wild_err_arm.rs:23:9 @@ -21,7 +21,7 @@ error: `Err(_)` matches all errors LL | Err(_) => { | ^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_e)` matches all errors --> $DIR/match_wild_err_arm.rs:31:9 @@ -29,7 +29,7 @@ error: `Err(_e)` matches all errors LL | Err(_e) => panic!(), | ^^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From bc93f7052e4a76d62e2aa5f11649e662cb46c7ce Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:56:33 +0200 Subject: Add test for cargo_common_metadata Fix missing `authors` entry in the provided example --- clippy_lints/src/cargo_common_metadata.rs | 1 + tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml | 3 +++ tests/ui-cargo/cargo_common_metadata/fail/src/main.rs | 3 +++ .../cargo_common_metadata/fail/src/main.stderr | 18 ++++++++++++++++++ tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 10 ++++++++++ tests/ui-cargo/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 782da249808..16b46423c8f 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -23,6 +23,7 @@ declare_clippy_lint! { /// [package] /// name = "clippy" /// version = "0.0.212" + /// authors = ["Someone "] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml new file mode 100644 index 00000000000..8346bf05778 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs new file mode 100644 index 00000000000..c67166fc4b0 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::cargo_common_metadata)] + +fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr new file mode 100644 index 00000000000..c8ae6c820df --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -0,0 +1,18 @@ +error: package `cargo_common_metadata` is missing `package.authors` metadata + | + = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + +error: package `cargo_common_metadata` is missing `package.description` metadata + +error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata + +error: package `cargo_common_metadata` is missing `package.repository` metadata + +error: package `cargo_common_metadata` is missing `package.readme` metadata + +error: package `cargo_common_metadata` is missing `package.keywords` metadata + +error: package `cargo_common_metadata` is missing `package.categories` metadata + +error: aborting due to 7 previous errors + diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml new file mode 100644 index 00000000000..f99c126fabf --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" +authors = ["Random person from the Internet "] +description = "A test package for the cargo_common_metadata lint" +repository = "https://github.com/someone/cargo_common_metadata" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["metadata", "lint", "clippy"] +categories = ["development-tools::testing"] diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs new file mode 100644 index 00000000000..c67166fc4b0 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::cargo_common_metadata)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From 7a0eccbd8a719af00b027b0ea85c576d9cbed750 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:58:02 +0200 Subject: 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 +++ tests/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index ed85d0315bd..c4decfc9401 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -54,7 +54,9 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { let group: Vec = group.collect(); if group.len() > 1 { - let versions = group.into_iter().map(|p| p.version).join(", "); + let mut versions: Vec<_> = group.into_iter().map(|p| p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); span_lint( cx, diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml new file mode 100644 index 00000000000..05ffde839dc --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "multiple_crate_versions" +version = "0.1.0" + +[dependencies] +ctrlc = "=3.1.0" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs new file mode 100644 index 00000000000..4bc61dd6299 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr new file mode 100644 index 00000000000..4f668599be9 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr @@ -0,0 +1,6 @@ +error: multiple versions for dependency `winapi`: 0.2.8, 0.3.8 + | + = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml new file mode 100644 index 00000000000..cad32b9233f --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" + +[dependencies] +regex = "1.3.7" +serde = "1.0.110" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs new file mode 100644 index 00000000000..4bc61dd6299 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From 6b3cf63bf568cab4f8e05ea483ad97d5ea0e2eec Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 22 May 2020 14:45:51 +0200 Subject: Fix dogfood fallout --- clippy_lints/src/methods/mod.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 810a226b50d..32b3b7f7947 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1496,17 +1496,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { if let ty::Opaque(def_id, _) = ret_ty.kind { // one of the associated types must be Self for predicate in cx.tcx.predicates_of(def_id).predicates { - match predicate.0.kind() { - ty::PredicateKind::Projection(poly_projection_predicate) => { - let binder = poly_projection_predicate.ty(); - let associated_type = binder.skip_binder(); - - // walk the associated type and check for Self - if contains_self_ty(associated_type) { - return; - } - }, - _ => {}, + 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; + } } } } -- cgit 1.4.1-3-g733a5 From a578bed69ac2a9b33fcb871f9ad7dbf02355cb82 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 18 May 2020 18:35:49 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index a599667b8d8..3b88e4c4cb1 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,27 +1,20 @@ use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then}; +use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::HirIdSet; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for types with a `fn new() -> Self` method and no /// implementation of /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html). /// - /// It detects both the case when a manual - /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) - /// implementation is required and also when it can be created with - /// `#[derive(Default)]` - /// /// **Why is this bad?** The user might expect to be able to use /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the /// type can be constructed without arguments. @@ -40,46 +33,17 @@ declare_clippy_lint! { /// } /// ``` /// - /// Instead, use: + /// To fix the lint, and a `Default` implementation that delegates to `new`: /// /// ```ignore /// struct Foo(Bar); /// /// impl Default for Foo { /// fn default() -> Self { - /// Foo(Bar::new()) + /// Foo::new() /// } /// } /// ``` - /// - /// Or, if - /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) - /// can be derived by `#[derive(Default)]`: - /// - /// ```rust - /// struct Foo; - /// - /// impl Foo { - /// fn new() -> Self { - /// Foo - /// } - /// } - /// ``` - /// - /// Instead, use: - /// - /// ```rust - /// #[derive(Default)] - /// struct Foo; - /// - /// impl Foo { - /// fn new() -> Self { - /// Foo - /// } - /// } - /// ``` - /// - /// You can also have `new()` call `Default::default()`. pub NEW_WITHOUT_DEFAULT, style, "`fn new() -> Self` method without `Default` implementation" @@ -158,46 +122,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { } } - if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) { - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider deriving a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_item_with_attr( - cx, - sp, - "try this", - "#[derive(Default)]", - Applicability::MaybeIncorrect, - ); - }); - } else { - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try this", - &create_new_without_default_suggest_msg(self_ty), - Applicability::MaybeIncorrect, - ); - }, - ); - } + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id, + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{}`", + self_ty + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try this", + &create_new_without_default_suggest_msg(self_ty), + Applicability::MaybeIncorrect, + ); + }, + ); } } } @@ -217,18 +160,3 @@ fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { }} }}", ty) } - -fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option { - match ty.kind { - ty::Adt(adt_def, substs) if adt_def.is_struct() => { - for field in adt_def.all_fields() { - let f_ty = field.ty(cx.tcx, substs); - if !implements_trait(cx, f_ty, default_trait_id, &[]) { - return None; - } - } - Some(cx.tcx.def_span(adt_def.did)) - }, - _ => None, - } -} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 781ea7bb152..3b6041823d8 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -148,4 +148,15 @@ impl AllowDerive { } } +pub struct NewNotEqualToDerive { + foo: i32, +} + +impl NewNotEqualToDerive { + // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving. + pub fn new() -> Self { + NewNotEqualToDerive { foo: 1 } + } +} + fn main() {} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 5e485d40663..e529e441eb7 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,4 +1,4 @@ -error: you should consider deriving a `Default` implementation for `Foo` +error: you should consider adding a `Default` implementation for `Foo` --> $DIR/new_without_default.rs:8:5 | LL | / pub fn new() -> Foo { @@ -9,10 +9,14 @@ LL | | } = note: `-D clippy::new-without-default` implied by `-D warnings` help: try this | -LL | #[derive(Default)] +LL | impl Default for Foo { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } | -error: you should consider deriving a `Default` implementation for `Bar` +error: you should consider adding a `Default` implementation for `Bar` --> $DIR/new_without_default.rs:16:5 | LL | / pub fn new() -> Self { @@ -22,7 +26,11 @@ LL | | } | help: try this | -LL | #[derive(Default)] +LL | impl Default for Bar { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } | error: you should consider adding a `Default` implementation for `LtKo<'c>` @@ -42,5 +50,22 @@ LL | } LL | } | -error: aborting due to 3 previous errors +error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` + --> $DIR/new_without_default.rs:157:5 + | +LL | / pub fn new() -> Self { +LL | | NewNotEqualToDerive { foo: 1 } +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for NewNotEqualToDerive { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From a709559705db19785b29ce4a9044d7aebaefec31 Mon Sep 17 00:00:00 2001 From: Nick Torres Date: Sat, 23 May 2020 16:14:38 -0700 Subject: Clarify the documentation of the `unnecessary_mut_passed` lint --- clippy_lints/src/mut_reference.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index e5680482e5b..67a1ac78a67 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Detects giving a mutable reference to a function that only + /// **What it does:** Detects passing a mutable reference to a function that only /// requires an immutable reference. /// /// **Why is this bad?** The immutable reference rules out all other references -- cgit 1.4.1-3-g733a5 From 8642fc97dd1a9b4f0291726c47ec97d15599d74d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 22 May 2020 22:39:19 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index c4decfc9401..b24ec897ef5 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -1,11 +1,14 @@ //! lint on multiple versions of a crate being used use crate::utils::{run_lints, span_lint}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{Crate, CRATE_HIR_ID}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; +use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId}; +use if_chain::if_chain; use itertools::Itertools; declare_clippy_lint! { @@ -34,37 +37,65 @@ declare_clippy_lint! { declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]); impl LateLintPass<'_, '_> for MultipleCrateVersions { + #[allow(clippy::find_map)] fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() { + let metadata = if let Ok(metadata) = MetadataCommand::new().exec() { metadata } else { span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata"); - return; }; + let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str(); let mut packages = metadata.packages; packages.sort_by(|a, b| a.name.cmp(&b.name)); - for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) { - let group: Vec = group.collect(); + if_chain! { + if let Some(resolve) = &metadata.resolve; + if let Some(local_id) = packages.iter().find(|p| p.name == *local_name).map(|p| &p.id); + then { + for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + let group: Vec<&Package> = group.collect(); + + if group.len() <= 1 { + continue; + } - 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.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), - ); + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{}`: {}", name, versions), + ); + } + } } } } } + +fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool { + fn depends_on(node: &Node, dep_id: &PackageId) -> bool { + node.deps.iter().any(|dep| { + dep.pkg == *dep_id + && dep + .dep_kinds + .iter() + .any(|info| matches!(info.kind, DependencyKind::Normal)) + }) + } + + nodes + .iter() + .filter(|node| depends_on(node, dep_id)) + .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id)) +} diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml new file mode 100644 index 00000000000..72731fbc75d --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml @@ -0,0 +1,17 @@ +# Should not lint for dev or build dependencies. See issue 5041. + +[package] +name = "multiple_crate_versions" +version = "0.1.0" +publish = false + +# One of the versions of winapi is only a dev dependency: allowed +[dependencies] +ctrlc = "=3.1.0" +[dev-dependencies] +ansi_term = "=0.11.0" + +# Both versions of winapi are a build dependency: allowed +[build-dependencies] +ctrlc = "=3.1.0" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs new file mode 100644 index 00000000000..1b2d3ec9459 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs @@ -0,0 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions +#![warn(clippy::multiple_crate_versions)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From ec0a00e53980619a6313ff4f01099a1aebcfd9e6 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 24 May 2020 21:17:54 +0200 Subject: Use find_map instead of find() + map() --- clippy_lints/src/multiple_crate_versions.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index b24ec897ef5..b6770804e18 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -37,7 +37,6 @@ declare_clippy_lint! { declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]); impl LateLintPass<'_, '_> for MultipleCrateVersions { - #[allow(clippy::find_map)] fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) { return; @@ -56,7 +55,9 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { if_chain! { if let Some(resolve) = &metadata.resolve; - if let Some(local_id) = packages.iter().find(|p| p.name == *local_name).map(|p| &p.id); + if let Some(local_id) = packages + .iter() + .find_map(|p| if p.name == *local_name { Some(&p.id) } else { None }); then { for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { let group: Vec<&Package> = group.collect(); -- cgit 1.4.1-3-g733a5 From 4f8909fad986dda68a9dcd172eaa362b6fce105b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 May 2020 21:28:31 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 95921518986..0b080d9be2c 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,13 +1,16 @@ use crate::utils::{ - match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, + is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, + span_lint_and_help, span_lint_and_sugg, }; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts + /// **What it does:** Checks for `Into`, `From`, `TryFrom`,`IntoIter` calls that useless converts /// to the same type as caller. /// /// **Why is this bad?** Redundant code. @@ -26,7 +29,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" } #[derive(Default)] @@ -68,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -84,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -94,11 +97,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { }, ExprKind::Call(ref path, ref args) => { - if let ExprKind::Path(ref qpath) = path.kind { - if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() { + if_chain! { + if args.len() == 1; + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id(); + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + + then { + if_chain! { + if match_def_path(cx, def_id, &paths::TRY_FROM); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().nth(0); + if same_tys(cx, a_type, b); + + then { + let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "Useless conversion to the same type", + None, + &hint, + ); + } + } + if match_def_path(cx, def_id, &paths::FROM_FROM) { - let a = cx.tables.expr_ty(e); - let b = cx.tables.expr_ty(&args[0]); if same_tys(cx, a, b) { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); let sugg_msg = @@ -107,7 +134,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index b3ad2ad9d99..e00d726282a 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -128,6 +128,7 @@ pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned" pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 7df3507edfd..0b2947f7d62 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,55 +10,55 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs new file mode 100644 index 00000000000..abf0c891b52 --- /dev/null +++ b/tests/ui/useless_conversion_try.rs @@ -0,0 +1,25 @@ +#![deny(clippy::useless_conversion)] + +use std::convert::TryFrom; + +fn test_generic(val: T) -> T { + T::try_from(val).unwrap() +} + +fn test_generic2 + Into, U: From>(val: T) { + let _ = U::try_from(val).unwrap(); +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + + let _: String = TryFrom::try_from("foo").unwrap(); + let _ = String::try_from("foo").unwrap(); + #[allow(clippy::useless_conversion)] + let _ = String::try_from("foo").unwrap(); + + let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + let _ = String::try_from("foo".to_string()).unwrap(); + let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); +} diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr new file mode 100644 index 00000000000..b3cb01fbe32 --- /dev/null +++ b/tests/ui/useless_conversion_try.stderr @@ -0,0 +1,39 @@ +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:6:5 + | +LL | T::try_from(val).unwrap() + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/useless_conversion_try.rs:1:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing `T::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:22:21 + | +LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `TryFrom::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:23:13 + | +LL | let _ = String::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:24:13 + | +LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 705bfdcc467c0ddd7eb61d3adb24809b27bae891 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 22 May 2020 11:46:17 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 0b080d9be2c..1645c5777b2 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -10,8 +10,8 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for `Into`, `From`, `TryFrom`,`IntoIter` calls that useless converts - /// to the same type as caller. + /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls + /// that useless converts to the same type as caller. /// /// **Why is this bad?** Redundant code. /// @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" } #[derive(Default)] @@ -39,6 +39,7 @@ pub struct UselessConversion { impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); +#[allow(clippy::too_many_lines)] impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { @@ -66,7 +67,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { let b = cx.tables.expr_ty(&args[0]); if same_tys(cx, a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); - span_lint_and_sugg( cx, USELESS_CONVERSION, @@ -94,6 +94,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { ); } } + if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { + if_chain! { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().next(); + if same_tys(cx, a_type, b); + + then { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "Useless conversion to the same type", + None, + "consider removing `.try_into()`", + ); + } + } + } }, ExprKind::Call(ref path, ref args) => { @@ -109,7 +130,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; - if let Some(a_type) = substs.types().nth(0); + if let Some(a_type) = substs.types().next(); if same_tys(cx, a_type, b); then { @@ -125,8 +146,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { } } - if match_def_path(cx, def_id, &paths::FROM_FROM) { - if same_tys(cx, a, b) { + if_chain! { + if match_def_path(cx, def_id, &paths::FROM_FROM); + if same_tys(cx, a, b); + + then { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index e00d726282a..779da7e6bf2 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -131,6 +131,7 @@ pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; +pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8211a57b564..f63301c7db0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2421,7 +2421,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "useless_conversion", group: "complexity", - desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type", deprecation: None, module: "useless_conversion", }, diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index abf0c891b52..ab4f960edb7 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,12 +1,16 @@ #![deny(clippy::useless_conversion)] -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; fn test_generic(val: T) -> T { - T::try_from(val).unwrap() + let _ = T::try_from(val).unwrap(); + val.try_into().unwrap() } fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.try_into().unwrap(); + let _: U = val.try_into().unwrap(); let _ = U::try_from(val).unwrap(); } @@ -14,12 +18,17 @@ fn main() { test_generic(10i32); test_generic2::(10i32); + let _: String = "foo".try_into().unwrap(); let _: String = TryFrom::try_from("foo").unwrap(); let _ = String::try_from("foo").unwrap(); #[allow(clippy::useless_conversion)] - let _ = String::try_from("foo").unwrap(); - + { + let _ = String::try_from("foo").unwrap(); + let _: String = "foo".try_into().unwrap(); + } + let _: String = "foo".to_string().try_into().unwrap(); let _: String = TryFrom::try_from("foo".to_string()).unwrap(); let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + let _: String = format!("Hello {}", "world").try_into().unwrap(); } diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index b3cb01fbe32..5afb5dc45d3 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,8 +1,8 @@ error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:6:5 + --> $DIR/useless_conversion_try.rs:6:13 | -LL | T::try_from(val).unwrap() - | ^^^^^^^^^^^^^^^^ +LL | let _ = T::try_from(val).unwrap(); + | ^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/useless_conversion_try.rs:1:9 @@ -12,7 +12,23 @@ LL | #![deny(clippy::useless_conversion)] = help: consider removing `T::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:22:21 + --> $DIR/useless_conversion_try.rs:7:5 + | +LL | val.try_into().unwrap() + | ^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:29:21 + | +LL | let _: String = "foo".to_string().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:23:13 + --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,12 +44,20 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:24:13 + --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider removing `String::try_from()` -error: aborting due to 4 previous errors +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:33:21 + | +LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 60d38ee1dde4344daa5fdf716eef78b45f483c7e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 23 May 2020 22:07:03 +0200 Subject: 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 - tests/ui/reversed_empty_ranges_unfixable.stderr | 10 ++------- 6 files changed, 47 insertions(+), 24 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 83c6faac041..1eb26d97ed4 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -241,14 +241,14 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { - matches!( - get_parent_expr(cx, expr), - Some(Expr { + fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> { + match get_parent_expr(cx, expr) { + parent_expr @ Some(Expr { kind: ExprKind::Index(..), .. - }) - ) + }) => parent_expr, + _ => None, + } } fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { @@ -267,18 +267,32 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { - if inside_indexing_expr(cx, expr) { + if let Some(parent_expr) = inside_indexing_expr(cx, expr) { let (reason, outcome) = if ordering == Ordering::Equal { ("empty", "always yield an empty slice") } else { ("reversed", "panic at run-time") }; - span_lint( + span_lint_and_then( cx, REVERSED_EMPTY_RANGES, expr.span, &format!("this range is {} and using it to index a slice will {}", reason, outcome), + |diag| { + if_chain! { + if ordering == Ordering::Equal; + if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind; + then { + diag.span_suggestion( + parent_expr.span, + "if you want an empty slice, use", + format!("[] as &[{}]", slice_ty), + Applicability::MaybeIncorrect + ); + } + } + } ); } else { span_lint_and_then( diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index ee2cbc3cf54..332c0427ef6 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -4,18 +4,23 @@ const ANSWER: i32 = 42; fn main() { + let arr = [1, 2, 3, 4, 5]; + + // These should be linted: + (21..=42).rev().for_each(|x| println!("{}", x)); let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); for _ in (-42..=-21).rev() {} for _ in (21u32..42u32).rev() {} + let _ = &[] as &[i32]; + // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index 6ed5ca6daa0..901ec8bcc09 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -4,18 +4,23 @@ const ANSWER: i32 = 42; fn main() { + let arr = [1, 2, 3, 4, 5]; + + // These should be linted: + (42..=21).for_each(|x| println!("{}", x)); let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); for _ in -21..=-42 {} for _ in 42u32..21u32 {} + let _ = &arr[3..3]; + // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 97933b8ff85..9a646fd9939 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + --> $DIR/reversed_empty_ranges_fixable.rs:11:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + --> $DIR/reversed_empty_ranges_fixable.rs:12:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:10:14 + --> $DIR/reversed_empty_ranges_fixable.rs:14:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ^^^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + --> $DIR/reversed_empty_ranges_fixable.rs:15:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ @@ -43,5 +43,11 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_fixable.rs:17:18 + | +LL | let _ = &arr[3..3]; + | ----^^^^- help: if you want an empty slice, use: `[] as &[i32]` + +error: aborting due to 5 previous errors diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs index c9ca4c47668..561a35625f0 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -9,7 +9,6 @@ fn main() { let arr = [1, 2, 3, 4, 5]; let _ = &arr[3usize..=1usize]; let _ = &arr[SOME_NUM..1]; - let _ = &arr[3..3]; for _ in ANSWER..ANSWER {} } diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr index 12e5483ecdf..240188cbb46 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -18,17 +18,11 @@ error: this range is reversed and using it to index a slice will panic at run-ti LL | let _ = &arr[SOME_NUM..1]; | ^^^^^^^^^^^ -error: this range is empty and using it to index a slice will always yield an empty slice - --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 - | -LL | let _ = &arr[3..3]; - | ^^^^ - error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + --> $DIR/reversed_empty_ranges_unfixable.rs:13:14 | LL | for _ in ANSWER..ANSWER {} | ^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 566377f6272b0a3b9fa65dabe1f39ee82be80d4e Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Wed, 21 Aug 2019 21:19:28 +0200 Subject: Ignore calls to 'len' --- clippy_lints/src/methods/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 32b3b7f7947..c82cf57a4b1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1614,6 +1614,21 @@ fn lint_or_fun_call<'a, 'tcx>( or_has_args: bool, span: Span, ) { + if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.node { + if path.ident.as_str() == "len" { + let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); + + match ty.sty { + ty::Slice(_) | ty::Array(_, _) => return, + _ => (), + } + + if match_type(cx, ty, &paths::VEC) { + return; + } + } + } + // (path, fn_has_argument, methods, suffix) let know_types: &[(&[_], _, &[_], _)] = &[ (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), -- cgit 1.4.1-3-g733a5 From bcfeb4de1589c19a7b21f04fec284e6045c0aa7a Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Mon, 25 May 2020 21:23:39 +0200 Subject: Fix build --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c82cf57a4b1..52ca962e7ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1614,11 +1614,11 @@ fn lint_or_fun_call<'a, 'tcx>( or_has_args: bool, span: Span, ) { - if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.node { + if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.kind { if path.ident.as_str() == "len" { let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); - match ty.sty { + match ty.kind { ty::Slice(_) | ty::Array(_, _) => return, _ => (), } -- cgit 1.4.1-3-g733a5 From a1824e187cb6d17e48e2ff039810551540a9b826 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 25 May 2020 23:09:06 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2cdf9671419..4eac571f966 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -2,7 +2,7 @@ use crate::utils::ptr::get_spans; use crate::utils::{ - is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, + is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then, walk_ptrs_hir_ty, }; use if_chain::if_chain; @@ -150,8 +150,16 @@ fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_ let fn_def_id = cx.tcx.hir().local_def_id(fn_id); let sig = cx.tcx.fn_sig(fn_def_id); let fn_ty = sig.skip_binder(); + let body = opt_body_id.map(|id| cx.tcx.hir().body(id)); for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() { + // Honor the allow attribute on parameters. See issue 5644. + if let Some(body) = &body { + if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) { + continue; + } + } + if let ty::Ref(_, ty, Mutability::Not) = ty.kind { if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { let mut ty_snippet = None; diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 4ebe2e2852f..73758b7eeb7 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -530,7 +530,7 @@ pub trait DiagnosticBuilderExt<'a, T: LintContext> { /// Suggest to add an item before another. /// - /// The item should not be indented (expect for inner indentation). + /// The item should not be indented (except for inner indentation). /// /// # Example /// diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 30f39e9b063..541225e6351 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -71,7 +71,6 @@ fn false_positive_capacity_too(x: &String) -> String { #[allow(dead_code)] fn test_cow_with_ref(c: &Cow<[i32]>) {} -#[allow(dead_code)] fn test_cow(c: Cow<[i32]>) { let _c = c; } @@ -84,3 +83,34 @@ trait Foo2 { impl Foo2 for String { fn do_string(&self) {} } + +// Check that the allow attribute on parameters is honored +mod issue_5644 { + use std::borrow::Cow; + + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + + struct S {} + impl S { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } + + trait T { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } +} -- cgit 1.4.1-3-g733a5 From 67167be1679c60eefa2c314c5e4a2b673d5eef11 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 17 May 2020 18:14:43 +0200 Subject: 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 +++++- tests/ui/empty_line_after_outer_attribute.stderr | 12 ++-- 6 files changed, 109 insertions(+), 40 deletions(-) create mode 100644 tests/ui/auxiliary/proc_macro_attr.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 1c0be727834..043a79f2001 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -33,5 +33,9 @@ semver = "0.9.0" # see url = { version = "2.1.0", features = ["serde"] } +[dev-dependencies] +quote = "*" +syn = { version = "*", features = ["full"] } + [features] deny-warnings = [] diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 64abc9fdc71..41f125d4839 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -248,7 +248,6 @@ declare_lint_pass!(Attributes => [ INLINE_ALWAYS, DEPRECATED_SEMVER, USELESS_ATTRIBUTE, - EMPTY_LINE_AFTER_OUTER_ATTR, UNKNOWN_CLIPPY_LINTS, ]); @@ -480,36 +479,6 @@ fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attrib } for attr in attrs { - let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { - attr - } else { - continue; - }; - - if attr.style == AttrStyle::Outer { - if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { - return; - } - - let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt()); - let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt()); - - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { - let lines = snippet.split('\n').collect::>(); - let lines = without_block_comments(lines); - - if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - span_lint( - cx, - EMPTY_LINE_AFTER_OUTER_ATTR, - begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - ); - } - } - } - if let Some(values) = attr.meta_item_list() { if values.len() != 1 || !attr.check_name(sym!(inline)) { continue; @@ -551,15 +520,57 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } } -declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]); +declare_lint_pass!(EarlyAttributes => [ + DEPRECATED_CFG_ATTR, + MISMATCHED_TARGET_OS, + EMPTY_LINE_AFTER_OUTER_ATTR, +]); impl EarlyLintPass for EarlyAttributes { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + check_empty_line_after_outer_attr(cx, item); + } + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { check_deprecated_cfg_attr(cx, attr); check_mismatched_target_os(cx, attr); } } +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + for attr in &item.attrs { + let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { + attr + } else { + return; + }; + + if attr.style == AttrStyle::Outer { + if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { + return; + } + + let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt()); + let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt()); + + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + let lines = snippet.split('\n').collect::>(); + let lines = without_block_comments(lines); + + if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { + span_lint( + cx, + EMPTY_LINE_AFTER_OUTER_ATTR, + begin_of_attr_to_item, + "Found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + ); + } + } + } + } +} + fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { if_chain! { // check cfg_attr diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a5de8429390..2758b9a7e76 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -38,7 +38,7 @@ fn clippy_driver_path() -> PathBuf { // as what we manually pass to `cargo` invocation fn third_party_crates() -> String { use std::collections::HashMap; - static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"]; + static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"]; let dep_dir = cargo::TARGET_LIB.join("deps"); let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len()); for entry in fs::read_dir(dep_dir).unwrap() { diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs new file mode 100644 index 00000000000..e6626d57a77 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -0,0 +1,37 @@ +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![allow(clippy::useless_conversion)] + +extern crate proc_macro; +extern crate quote; +extern crate syn; + +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use syn::parse_macro_input; +use syn::{parse_quote, ItemTrait, TraitItem}; + +#[proc_macro_attribute] +pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut item = parse_macro_input!(input as ItemTrait); + for inner in &mut item.items { + if let TraitItem::Method(method) = inner { + let sig = &method.sig; + let block = &mut method.default; + if let Some(block) = block { + let brace = block.brace_token; + + let my_block = quote_spanned!( brace.span => { + // Should not trigger `empty_line_after_outer_attr` + #[crate_type = "lib"] + #sig #block + Vec::new() + }); + *block = parse_quote!(#my_block); + } + } + } + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 5343dff9da1..3e92bca986a 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -1,8 +1,12 @@ +// aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_outer_attr)] #![allow(clippy::assertions_on_constants)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +#[macro_use] +extern crate proc_macro_attr; + // This should produce a warning #[crate_type = "lib"] @@ -93,4 +97,17 @@ pub struct S; /* test */ pub struct T; -fn main() { } +// This should not produce a warning +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index d8c9786541f..bf753a732f0 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:7:1 + --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:19:1 + --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:24:1 + --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:31:1 + --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:39:1 + --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:47:1 + --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] LL | | -- cgit 1.4.1-3-g733a5 From 1801841ae554a7778666c4c1085393b32eccf74d Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 26 May 2020 18:40:42 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1645c5777b2..7fa97b24699 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -71,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -87,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -108,7 +108,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", None, "consider removing `.try_into()`", ); @@ -139,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", None, &hint, ); @@ -158,7 +158,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 0b2947f7d62..84ec5370278 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,55 +10,55 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index ab4f960edb7..3787ea99144 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -31,4 +31,12 @@ fn main() { let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); let _: String = format!("Hello {}", "world").try_into().unwrap(); + let _: String = "".to_owned().try_into().unwrap(); + let _: String = match String::from("_").try_into() { + Ok(a) => a, + Err(_) => "".into(), + }; + // FIXME this is a false negative + #[allow(clippy::cmp_owned)] + if String::from("a") == TryInto::::try_into(String::from("a")).unwrap() {} } diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index 5afb5dc45d3..b765727c168 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,4 +1,4 @@ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:6:13 | LL | let _ = T::try_from(val).unwrap(); @@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider removing `T::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:7:5 | LL | val.try_into().unwrap() @@ -19,7 +19,7 @@ LL | val.try_into().unwrap() | = help: consider removing `.try_into()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:29:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); @@ -27,7 +27,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); | = help: consider removing `.try_into()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); @@ -35,7 +35,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | = help: consider removing `TryFrom::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); @@ -43,7 +43,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); | = help: consider removing `String::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); @@ -51,7 +51,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | = help: consider removing `String::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:33:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); @@ -59,5 +59,21 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | = help: consider removing `.try_into()` -error: aborting due to 7 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:34:21 + | +LL | let _: String = "".to_owned().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:35:27 + | +LL | let _: String = match String::from("_").try_into() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 7fd3bd0f57e11a65641501d6a898328ecb83ca77 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 24 Apr 2020 00:14:03 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 057d39d4c82..902f3d56c1e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -346,13 +346,8 @@ mod reexport { /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. /// /// Used in `./src/driver.rs`. -pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Conf) { +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); - store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; - store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames { - single_char_binding_names_threshold, - }); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); } @@ -1066,6 +1061,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); + store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; + store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { + single_char_binding_names_threshold, + }); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/src/driver.rs b/src/driver.rs index d3a7e24937f..70c47b42682 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -79,7 +79,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_lints::read_conf(&[], &sess); clippy_lints::register_plugins(&mut lint_store, &sess, &conf); - clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf); + clippy_lints::register_pre_expansion_lints(&mut lint_store); clippy_lints::register_renamed(&mut lint_store); })); -- cgit 1.4.1-3-g733a5 From 8e22d15055231fc0df4a07d57cd883fd89d8131b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 May 2020 16:26:55 +0200 Subject: Fix fallout in redundant_field_names --- clippy_lints/src/redundant_field_names.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index b12c3c344ef..2a81170e49e 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -2,6 +2,7 @@ use crate::utils::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -36,6 +37,9 @@ declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess, expr.span) { + return; + } if let ExprKind::Struct(_, ref fields, _) = expr.kind { for field in fields { if field.is_shorthand { -- cgit 1.4.1-3-g733a5 From 04db13eb564f6e3264a0d376ef95365b1de44797 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 May 2020 16:50:00 +0200 Subject: Fix fallout in similar_names --- clippy_lints/src/non_expressive_names.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 2b51b732075..5328773a738 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -5,6 +5,7 @@ use rustc_ast::ast::{ use rustc_ast::attr; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{Ident, SymbolStr}; @@ -354,12 +355,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { impl EarlyLintPass for NonExpressiveNames { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if in_external_macro(cx.sess, item.span) { + return; + } + if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } } fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) { + if in_external_macro(cx.sess, item.span) { + return; + } + if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } -- cgit 1.4.1-3-g733a5 From 416182347589e9503408136747593ff95fb9dd13 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 27 May 2020 00:06:50 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 3b88e4c4cb1..e556e5d59c1 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -90,8 +90,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { return; } if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) { - let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); - let self_ty = cx.tcx.type_of(self_did); + let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); + let self_ty = cx.tcx.type_of(self_def_id); if_chain! { if same_tys(cx, self_ty, return_ty(cx, id)); if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); @@ -112,10 +112,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { // generics if_chain! { if let Some(ref impling_types) = self.impling_types; - if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def(); - if let Some(self_def_id) = self_def.did.as_local(); + if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def(); + if let Some(self_local_did) = self_def.did.as_local(); then { - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id); + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); if impling_types.contains(&self_id) { return; } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 5328773a738..5f14fe97afe 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -132,7 +132,11 @@ struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { fn visit_pat(&mut self, pat: &'tcx Pat) { match pat.kind { - PatKind::Ident(_, ident, _) => self.check_ident(ident), + PatKind::Ident(_, ident, _) => { + if !pat.span.from_expansion() { + self.check_ident(ident); + } + }, PatKind::Struct(_, ref fields, _) => { for field in fields { if !field.is_shorthand { -- cgit 1.4.1-3-g733a5 From 64a05f56c33d4754808ef85e634f72a9053c56fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 28 May 2020 00:36:15 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 2ec0b5a8d6f..f5bfede75a7 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -259,6 +259,17 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. + fn should_skip_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + higher::range(cx, expr).map_or(false, |_| { + !cx.tcx + .features() + .declared_lib_features + .iter() + .any(|(name, _)| name.as_str() == "range_is_empty") + }) + } + /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssocItem) -> bool { if let ty::AssocKind::Fn = item.kind { @@ -284,6 +295,10 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } + if should_skip_range(cx, expr) { + return false; + } + let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => { diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 624e5ef8fcf..a29b832eb60 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,3 +141,11 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 7fba971cfd8..8fd0093f4fd 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,3 +141,11 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed new file mode 100644 index 00000000000..7da26f8ff4d --- /dev/null +++ b/tests/ui/len_zero_ranges.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).is_empty(); + } +} + +fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs new file mode 100644 index 00000000000..be7b4244bc0 --- /dev/null +++ b/tests/ui/len_zero_ranges.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).len() == 0; + } +} + +fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr new file mode 100644 index 00000000000..6e5fa41fb08 --- /dev/null +++ b/tests/ui/len_zero_ranges.stderr @@ -0,0 +1,10 @@ +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:10:17 + | +LL | let _ = (0..42).len() == 0; + | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 5faab874f9f8655c8f284944b5acdede5c088af4 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 23 May 2020 00:07:09 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac9057199f..f7dae3dcfff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1630,6 +1630,7 @@ Released 2018-09-13 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 902f3d56c1e..4f0ecab393d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -325,6 +325,7 @@ mod unwrap; mod use_self; mod useless_conversion; mod vec; +mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; mod wildcard_imports; @@ -847,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::OUTER_EXPN_EXPN_DATA, &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, + &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, &wildcard_dependencies::WILDCARD_DEPENDENCIES, &wildcard_imports::ENUM_GLOB_USE, @@ -1062,6 +1064,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); + store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1430,6 +1433,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), @@ -1677,6 +1681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), + LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), ]); store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 779da7e6bf2..3b7e9739211 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -138,5 +138,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs new file mode 100644 index 00000000000..86cbfa8203d --- /dev/null +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -0,0 +1,59 @@ +use crate::utils::span_lint_and_then; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +use crate::utils::{match_def_path, paths}; +use rustc_ast::ast::LitKind; +use rustc_hir as hir; + +declare_clippy_lint! { + /// **What it does:** Finds occurences of `Vec::resize(0, an_int)` + /// + /// **Why is this bad?** This is probably an argument inversion mistake. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// vec!(1, 2, 3, 4, 5).resize(0, 5) + /// ``` + pub VEC_RESIZE_TO_ZERO, + correctness, + "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake" +} + +declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VecResizeToZero { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(path_segment, _, ref args) = expr.kind; + if let Some(method_def_id) = cx.tables.type_dependent_def_id(expr.hir_id); + if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3; + if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; + if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind; + then { + let method_call_span = expr.span.with_lo(path_segment.ident.span.lo()); + span_lint_and_then( + cx, + VEC_RESIZE_TO_ZERO, + expr.span, + "emptying a vector with `resize`", + |db| { + db.help("the arguments may be inverted..."); + db.span_suggestion( + method_call_span, + "...or you can empty the vector with", + "clear()".to_string(), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f63301c7db0..1e94ca00c14 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2460,6 +2460,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "vec_resize_to_zero", + group: "correctness", + desc: "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake", + deprecation: None, + module: "vec_resize_to_zero", + }, Lint { name: "verbose_bit_mask", group: "style", diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs new file mode 100644 index 00000000000..0263e2f5f20 --- /dev/null +++ b/tests/ui/vec_resize_to_zero.rs @@ -0,0 +1,15 @@ +#![warn(clippy::vec_resize_to_zero)] + +fn main() { + // applicable here + vec![1, 2, 3, 4, 5].resize(0, 5); + + // not applicable + vec![1, 2, 3, 4, 5].resize(2, 5); + + // applicable here, but only implemented for integer litterals for now + vec!["foo", "bar", "baz"].resize(0, "bar"); + + // not applicable + vec!["foo", "bar", "baz"].resize(2, "bar") +} diff --git a/tests/ui/vec_resize_to_zero.stderr b/tests/ui/vec_resize_to_zero.stderr new file mode 100644 index 00000000000..feb846298c6 --- /dev/null +++ b/tests/ui/vec_resize_to_zero.stderr @@ -0,0 +1,13 @@ +error: emptying a vector with `resize` + --> $DIR/vec_resize_to_zero.rs:5:5 + | +LL | vec![1, 2, 3, 4, 5].resize(0, 5); + | ^^^^^^^^^^^^^^^^^^^^------------ + | | + | help: ...or you can empty the vector with: `clear()` + | + = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings` + = help: the arguments may be inverted... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 37381d33a4761a064311dd95fbc54b5da6ad3766 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 14:05:57 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 218b0d27f74..9c508fc0e4a 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -173,13 +173,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { !preds.is_empty() && { let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty); preds.iter().all(|t| { - let ty_params = &t - .skip_binder() - .trait_ref - .substs - .iter() - .skip(1) - .collect::>(); + let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::>(); implements_trait(cx, ty_empty_region, t.def_id(), ty_params) }) }, diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index dfa6223f1b9..5f794598052 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -279,13 +279,12 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - move || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability), - ); + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 1c4914a470c..7bd5f09f333 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -153,9 +153,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -216,6 +213,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); -- cgit 1.4.1-3-g733a5 From 0bcfae92f80d31cad4e5fb687da8033a38d06a32 Mon Sep 17 00:00:00 2001 From: djugei Date: Sun, 31 May 2020 15:41:33 +0200 Subject: moved cast_ptr_alignment to pedantic and expanded documentation --- clippy_lints/src/types.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6ed9ff22e46..3ac99e24684 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -974,7 +974,8 @@ declare_clippy_lint! { /// behavior. /// /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar - /// on the resulting pointer is fine. + /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like + /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. /// /// **Example:** /// ```rust @@ -982,7 +983,7 @@ declare_clippy_lint! { /// let _ = (&mut 1u8 as *mut u8) as *mut u16; /// ``` pub CAST_PTR_ALIGNMENT, - correctness, + pedantic, "cast from a pointer to a more-strictly-aligned pointer" } -- cgit 1.4.1-3-g733a5 From 18b5ceed7991e3d8616b74b42de26330ca4c40db Mon Sep 17 00:00:00 2001 From: djugei Date: Sun, 31 May 2020 16:00:29 +0200 Subject: ran update_lints --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 902f3d56c1e..6475fa67d25 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1164,6 +1164,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_POSSIBLE_TRUNCATION), LintId::of(&types::CAST_POSSIBLE_WRAP), LintId::of(&types::CAST_PRECISION_LOSS), + LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_SIGN_LOSS), LintId::of(&types::IMPLICIT_HASHER), LintId::of(&types::INVALID_UPCAST_COMPARISONS), @@ -1410,7 +1411,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::BORROWED_BOX), LintId::of(&types::BOX_VEC), - LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), @@ -1669,7 +1669,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), LintId::of(&types::ABSURD_EXTREME_COMPARISONS), - LintId::of(&types::CAST_PTR_ALIGNMENT), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), LintId::of(&unicode::ZERO_WIDTH_SPACE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f63301c7db0..b9c84654593 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -166,7 +166,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "cast_ptr_alignment", - group: "correctness", + group: "pedantic", desc: "cast from a pointer to a more-strictly-aligned pointer", deprecation: None, module: "types", -- cgit 1.4.1-3-g733a5 From 0ab823c509897ce2f516feb760fe1bf02cf77443 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 26 Aug 2019 18:34:30 +0200 Subject: Rework suggestion generation of `unit_arg` lint --- clippy_lints/src/types.rs | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3ac99e24684..8fcca4b7bb9 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -779,6 +779,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { match expr.kind { ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => { + let mut args_to_recover = vec![]; for arg in args { if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { if let ExprKind::Match(.., match_source) = &arg.kind { @@ -787,17 +788,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { } } - span_lint_and_sugg( - cx, - UNIT_ARG, - arg.span, - "passing a unit value to a function", - "if you intended to pass a unit value, use a unit literal instead", - "()".to_string(), - Applicability::MaybeIncorrect, - ); + args_to_recover.push(arg); } } + if !args_to_recover.is_empty() { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + "move the expressions in front of the call...", + format!( + "{} ", + args_to_recover + .iter() + .map(|arg| { + format!( + "{};", + snippet_with_applicability(cx, arg.span, "..", &mut applicability) + ) + }) + .collect::>() + .join(" ") + ), + applicability, + ); + db.multipart_suggestion( + "...and use unit literals instead", + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + }); + } }, _ => (), } -- cgit 1.4.1-3-g733a5 From a1a1a4b82a35b810570dbf7d2ee7f00896bee232 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 26 Aug 2019 21:43:29 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 8fcca4b7bb9..3fbea77757d 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -812,14 +812,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { ), applicability, ); - db.multipart_suggestion( - "...and use unit literals instead", - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); + for arg in args_to_recover { + db.span_suggestion( + arg.span, + "...and use unit literals instead", + "()".to_string(), + applicability, + ); + } }); } }, -- cgit 1.4.1-3-g733a5 From 0f69cafc2dd77d573e24870887a4a13cfe50515a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 17 Feb 2020 18:10:59 +0100 Subject: Rework suggestion generation and use multipart_suggestion again --- clippy_lints/src/types.rs | 55 ++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3fbea77757d..c95bd5d72bc 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -794,32 +794,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { if !args_to_recover.is_empty() { let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { + let sugg = args_to_recover + .iter() + .enumerate() + .map(|(i, arg)| { + let indent = if i == 0 { + 0 + } else { + indent_of(cx, expr.span).unwrap_or(0) + }; + format!( + "{}{};", + " ".repeat(indent), + snippet_block_with_applicability( + cx, + arg.span, + "..", + Some(expr.span), + &mut applicability + ) + ) + }) + .collect::>() + .join("\n"); db.span_suggestion( expr.span.with_hi(expr.span.lo()), - "move the expressions in front of the call...", - format!( - "{} ", - args_to_recover - .iter() - .map(|arg| { - format!( - "{};", - snippet_with_applicability(cx, arg.span, "..", &mut applicability) - ) - }) - .collect::>() - .join(" ") - ), + &format!("{}move the expressions in front of the call...", or), + format!("{}\n", sugg), + applicability, + ); + db.multipart_suggestion( + "...and use unit literals instead", + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), applicability, ); - for arg in args_to_recover { - db.span_suggestion( - arg.span, - "...and use unit literals instead", - "()".to_string(), - applicability, - ); - } }); } }, -- cgit 1.4.1-3-g733a5 From f9c325f5b657e0c37ba2016a51cddbeab7f7693f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 17 Feb 2020 18:11:50 +0100 Subject: Suggest to remove the semicolon of the last stmt in a block --- clippy_lints/src/types.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c95bd5d72bc..51d7d9b3ab7 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -794,6 +794,36 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { if !args_to_recover.is_empty() { let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { + let mut or = ""; + args_to_recover + .iter() + .filter_map(|arg| { + if_chain! { + if let ExprKind::Block(block, _) = arg.kind; + if block.expr.is_none(); + if let Some(last_stmt) = block.stmts.iter().last(); + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let Some(snip) = snippet_opt(cx, last_expr.span); + then { + Some(( + last_stmt.span, + snip, + )) + } + else { + None + } + } + }) + .for_each(|(span, sugg)| { + db.span_suggestion( + span, + "remove the semicolon from the last statement in the block", + sugg, + Applicability::MaybeIncorrect, + ); + or = "or "; + }); let sugg = args_to_recover .iter() .enumerate() -- cgit 1.4.1-3-g733a5 From 4c9cefa12232aa0224b1680f51654fe10f5cf3b7 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 18 Feb 2020 09:51:52 +0100 Subject: Move linting out in its own function --- clippy_lints/src/types.rs | 171 ++++++++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 80 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 51d7d9b3ab7..6866635b904 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -779,89 +779,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { match expr.kind { ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => { - let mut args_to_recover = vec![]; - for arg in args { - if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { - if let ExprKind::Match(.., match_source) = &arg.kind { - if *match_source == MatchSource::TryDesugar { - continue; + let args_to_recover = args + .iter() + .filter(|arg| { + if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { + if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind { + false + } else { + true } + } else { + false } - - args_to_recover.push(arg); - } - } + }) + .collect::>(); if !args_to_recover.is_empty() { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, UNIT_ARG, expr.span, "passing a unit value to a function", |db| { - let mut or = ""; - args_to_recover - .iter() - .filter_map(|arg| { - if_chain! { - if let ExprKind::Block(block, _) = arg.kind; - if block.expr.is_none(); - if let Some(last_stmt) = block.stmts.iter().last(); - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let Some(snip) = snippet_opt(cx, last_expr.span); - then { - Some(( - last_stmt.span, - snip, - )) - } - else { - None - } - } - }) - .for_each(|(span, sugg)| { - db.span_suggestion( - span, - "remove the semicolon from the last statement in the block", - sugg, - Applicability::MaybeIncorrect, - ); - or = "or "; - }); - let sugg = args_to_recover - .iter() - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability( - cx, - arg.span, - "..", - Some(expr.span), - &mut applicability - ) - ) - }) - .collect::>() - .join("\n"); - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expressions in front of the call...", or), - format!("{}\n", sugg), - applicability, - ); - db.multipart_suggestion( - "...and use unit literals instead", - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); - }); + lint_unit_args(cx, expr, &args_to_recover); } }, _ => (), @@ -869,6 +802,84 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { } } +fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + let (singular, plural) = if args_to_recover.len() > 1 { + ("", "s") + } else { + ("a ", "") + }; + span_lint_and_then( + cx, + UNIT_ARG, + expr.span, + &format!("passing {}unit value{} to a function", singular, plural), + |db| { + let mut or = ""; + args_to_recover + .iter() + .filter_map(|arg| { + if_chain! { + if let ExprKind::Block(block, _) = arg.kind; + if block.expr.is_none(); + if let Some(last_stmt) = block.stmts.iter().last(); + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let Some(snip) = snippet_opt(cx, last_expr.span); + then { + Some(( + last_stmt.span, + snip, + )) + } + else { + None + } + } + }) + .for_each(|(span, sugg)| { + db.span_suggestion( + span, + "remove the semicolon from the last statement in the block", + sugg, + Applicability::MaybeIncorrect, + ); + or = "or "; + }); + let sugg = args_to_recover + .iter() + .enumerate() + .map(|(i, arg)| { + let indent = if i == 0 { + 0 + } else { + indent_of(cx, expr.span).unwrap_or(0) + }; + format!( + "{}{};", + " ".repeat(indent), + snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) + ) + }) + .collect::>() + .join("\n"); + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + &format!("{}move the expression{} in front of the call...", or, plural), + format!("{}\n", sugg), + applicability, + ); + db.multipart_suggestion( + &format!("...and use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + }, + ); +} + fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::DesugaringKind; if let ExprKind::Call(ref callee, _) = expr.kind { -- cgit 1.4.1-3-g733a5 From a9cde3a804808e82402888a20878053404a8eded Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 18:45:16 +0200 Subject: Don't suggest to move empty blocks --- clippy_lints/src/types.rs | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6866635b904..5ca30d598eb 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -10,7 +10,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, + BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, same_tys, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, same_tys, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -847,6 +847,7 @@ fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[ }); let sugg = args_to_recover .iter() + .filter(|arg| !is_empty_block(arg)) .enumerate() .map(|(i, arg)| { let indent = if i == 0 { @@ -860,16 +861,20 @@ fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[ snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) ) }) - .collect::>() - .join("\n"); - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg), - applicability, - ); + .collect::>(); + let mut and = ""; + if !sugg.is_empty() { + let plural = if sugg.len() > 1 { "s" } else { "" }; + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + &format!("{}move the expression{} in front of the call...", or, plural), + format!("{}\n", sugg.join("\n")), + applicability, + ); + and = "...and " + } db.multipart_suggestion( - &format!("...and use {}unit literal{} instead", singular, plural), + &format!("{}use {}unit literal{} instead", and, singular, plural), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -880,6 +885,18 @@ fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[ ); } +fn is_empty_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: &[], expr: None, .. + }, + _, + ) + ) +} + fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::DesugaringKind; if let ExprKind::Call(ref callee, _) = expr.kind { -- cgit 1.4.1-3-g733a5 From 7e843515d9525b6389c3fc1bcfa6ae046c1351dc Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 14 May 2020 15:06:05 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index f7dae3dcfff..c00f84bdb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1555,6 +1555,7 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`sort_by_key_reverse`]: https://rust-lang.github.io/rust-clippy/master/index.html#sort_by_key_reverse [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 38cfa212d9f..f51855badff 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,6 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod sort_by_key_reverse; mod strings; mod suspicious_trait_impl; mod swap; @@ -779,6 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &sort_by_key_reverse::SORT_BY_KEY_REVERSE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -1391,6 +1393,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1592,6 +1595,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), + LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs new file mode 100644 index 00000000000..65830afd0f8 --- /dev/null +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -0,0 +1,28 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub SORT_BY_KEY_REVERSE, + complexity, + "default lint description" +} + +declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); + +impl LateLintPass<'_, '_> for SortByKeyReverse {} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 69578732898..1b82f34c863 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1984,6 +1984,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "sort_by_key_reverse", + group: "complexity", + desc: "default lint description", + deprecation: None, + module: "sort_by_key_reverse", + }, Lint { name: "string_add", group: "restriction", diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs new file mode 100644 index 00000000000..2338dc6e594 --- /dev/null +++ b/tests/ui/sort_by_key_reverse.rs @@ -0,0 +1,5 @@ +#![warn(clippy::sort_by_key_reverse)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From 24847ea53e332853597aca2c7dfe48a9f3be1de8 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 16 May 2020 13:50:33 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 65830afd0f8..7d7097a8125 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -1,28 +1,91 @@ +use crate::utils::{match_type, span_lint_and_sugg}; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_lint::{LateLintPass, LateContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_hir::*; declare_clippy_lint! { /// **What it does:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// which compares the second argument to the first. /// /// **Why is this bad?** + /// It is more clear to use `Vec::sort_by_key` and `std::cmp::Reverse` /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// // example code where clippy issues a warning + /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// vec.sort_by_key(|e| Reverse(e.foo())); /// ``` pub SORT_BY_KEY_REVERSE, complexity, - "default lint description" + "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" } declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); -impl LateLintPass<'_, '_> for SortByKeyReverse {} +struct LintTrigger { + vec_name: String, + closure_arg: String, + closure_reverse_body: String, + unstable: bool, +} + +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; + if let name = name_ident.ident.name.to_ident_string(); + if name == "sort_by" || name == "sort_unstable_by"; + if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; + if closure_decl.inputs.len() == 2; + if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + then { + let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); + let unstable = name == "sort_unstable_by"; + Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) + } else { + None + } + } +} + +impl LateLintPass<'_, '_> for SortByKeyReverse { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + println!("{:?}", expr); + span_lint_and_sugg( + cx, + SORT_BY_KEY_REVERSE, + expr.span, + "use Vec::sort_by_key here instead", + "try", + String::from("being a better person"), + Applicability::MachineApplicable, + ); + if let Some(trigger) = detect_lint(cx, expr) { + span_lint_and_sugg( + cx, + SORT_BY_KEY_REVERSE, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|{}| Reverse({}))", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_reverse_body, + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b82f34c863..b5d9ef0110e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1987,7 +1987,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "sort_by_key_reverse", group: "complexity", - desc: "default lint description", + desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer", deprecation: None, module: "sort_by_key_reverse", }, diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 2338dc6e594..c0350f243c7 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,5 +1,6 @@ #![warn(clippy::sort_by_key_reverse)] fn main() { - // test code goes here + let mut vec = vec![3, 6, 1, 2, 5]; + vec.sort_by(|a, b| b.cmp(a)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 8590ab4d46de4eb43e7ebd42cb2f13b0064573e6 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 18 May 2020 21:48:35 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f51855badff..e7a4c1ecaa9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -998,6 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); + store.register_late_pass(|| box sort_by_key_reverse::SortByKeyReverse); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 7d7097a8125..d70391999a0 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -1,11 +1,12 @@ -use crate::utils::{match_type, span_lint_and_sugg}; +use crate::utils; use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** @@ -40,18 +41,122 @@ struct LintTrigger { unstable: bool, } +/// Detect if the two expressions are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { + match (&a_expr.kind, &b_expr.kind) { + // Two boxes with mirrored contents + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + // Two arrays with mirrored contents + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) + => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are function calls. + // Check to see that the function itself and its arguments are mirrored + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) + => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are method calls. + // Check to see that the function is the same and the arguments are mirrored + // This is enough because the receiver of the method is listed in the arguments + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) + => left_segment.ident == right_segment.ident + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two tuples with mirrored contents + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) + => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two binary ops, which are the same operation and which have mirrored arguments + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) + => left_op.node == right_op.node + && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident), + // Two unary ops, which are the same operation and which have the same argument + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) + => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + // The two exprs are literals of some kind + (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, + (ExprKind::Cast(left_expr, _), ExprKind::Cast(right_expr, _)) + => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::Block(left, _), ExprKind::Block(right, _)) => mirrored_blocks(cx, left, a_ident, right, b_ident), + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) + => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), + // The two exprs are `a` and `b`, directly + (ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: left_ident, .. }], .. },)), + ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: right_ident, .. }], .. },)), + ) => &left_ident == a_ident && &right_ident == b_ident, + // The two exprs are Paths to the same name (which is neither a nor b) + (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), + ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) + => left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) + && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident), + // Matching expressions, but one or both is borrowed + (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) + => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) + => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) + => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + // _ => false, + (left, right) => { + println!("{:?}\n{:?}", left, right); + false + }, + } +} + +/// Detect if the two blocks are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_blocks(cx: &LateContext<'_, '_>, a_block: &Block<'_>, a_ident: &Ident, b_block: &Block<'_>, b_ident: &Ident) -> bool { + match (a_block, b_block) { + (Block { stmts: left_stmts, expr: left_expr, .. }, + Block { stmts: right_stmts, expr: right_expr, .. }) + => left_stmts.iter().zip(right_stmts.iter()).all(|(left, right)| match (&left.kind, &right.kind) { + (StmtKind::Expr(left_expr), StmtKind::Expr(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (StmtKind::Semi(left_expr), StmtKind::Semi(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (StmtKind::Item(left_item), StmtKind::Item(right_item)) => left_item.id == right_item.id, + (StmtKind::Local(left), StmtKind::Local(right)) => mirrored_locals(cx, left, a_ident, right, b_ident), + _ => false, + }) && match (left_expr, right_expr) { + (None, None) => true, + (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + _ => false, + }, + } +} + +/// Check that the two "Local"s (let statements) are equal +fn mirrored_locals(cx: &LateContext<'_, '_>, a_local: &Local<'_>, a_ident: &Ident, b_local: &Local<'_>, b_ident: &Ident) -> bool { + match (a_local, b_local) { + (Local { pat: left_pat, init: left_expr, .. }, Local { pat: right_pat, init: right_expr, .. }) + => match (left_expr, right_expr) { + (None, None) => true, + (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + _ => false, + }, + } +} + fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { if_chain! { if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; - if closure_decl.inputs.len() == 2; - if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; + if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let &[ + Param { pat: Pat { kind: PatKind::Binding(_, _, a_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, b_ident, _), .. }, .. } + ] = &closure_body.params; + if let ExprKind::MethodCall(method_path, _, [ref b_expr, ref a_expr]) = &closure_body.value.kind; + if method_path.ident.name.to_ident_string() == "cmp"; + if mirrored_exprs(&cx, &a_expr, &a_ident, &b_expr, &b_ident); then { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) + let closure_arg = a_ident.name.to_ident_string(); + let closure_reverse_body = Sugg::hir(cx, &a_expr, "..").to_string(); + Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) } else { None } @@ -60,18 +165,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_, '_> for SortByKeyReverse { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - println!("{:?}", expr); - span_lint_and_sugg( - cx, - SORT_BY_KEY_REVERSE, - expr.span, - "use Vec::sort_by_key here instead", - "try", - String::from("being a better person"), - Applicability::MachineApplicable, - ); if let Some(trigger) = detect_lint(cx, expr) { - span_lint_and_sugg( + utils::span_lint_and_sugg( cx, SORT_BY_KEY_REVERSE, expr.span, diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index e69de29bb2d..4b18a073e1a 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::sort_by_key_reverse)] + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + vec.sort_by_key(|a| Reverse(a)); + vec.sort_by_key(|a| Reverse(&(a+5).abs())); + vec.sort_by_key(|a| Reverse(&-a)); +} diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index c0350f243c7..f4fb70b7b1d 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,6 +1,9 @@ +// run-rustfix #![warn(clippy::sort_by_key_reverse)] fn main() { - let mut vec = vec![3, 6, 1, 2, 5]; + let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + vec.sort_by(|a, b| (-b).cmp(&-a)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index e69de29bb2d..36a28c04b1c 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -0,0 +1,22 @@ +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:6:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(a))` + | + = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:7:5 + | +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&(a+5).abs()))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:8:5 + | +LL | vec.sort_by(|a, b| (-b).cmp(&-a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&-a))` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 943cb94dce8fca6f3a3f7f011a2a2f9f0a665b97 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 19 May 2020 22:57:27 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index d70391999a0..31629a1dbc1 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -53,8 +53,12 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) - => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + => { + // println!("{:?}\n{:?}\n", left_expr, left_args); + // println!("{:?}\n{:?}\n", right_expr, right_args); + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments @@ -74,21 +78,17 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), // The two exprs are literals of some kind (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left_expr, _), ExprKind::Cast(right_expr, _)) + (ExprKind::Cast(left_expr, left_ty), ExprKind::Cast(right_expr, right_ty)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::Block(left, _), ExprKind::Block(right, _)) => mirrored_blocks(cx, left, a_ident, right, b_ident), (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), - // The two exprs are `a` and `b`, directly - (ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: left_ident, .. }], .. },)), - ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: right_ident, .. }], .. },)), - ) => &left_ident == a_ident && &right_ident == b_ident, - // The two exprs are Paths to the same name (which is neither a nor b) + // Two paths: either one is a and the other is b, or they're identical to each other (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) - => left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) - && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident), + => (left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) + && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 && &left_segments[0].ident == a_ident && right_segments.len() == 1 && &right_segments[0].ident == b_ident), // Matching expressions, but one or both is borrowed (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), @@ -96,43 +96,11 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), - // _ => false, - (left, right) => { - println!("{:?}\n{:?}", left, right); - false - }, - } -} - -/// Detect if the two blocks are mirrored (identical, except one -/// contains a and the other replaces it with b) -fn mirrored_blocks(cx: &LateContext<'_, '_>, a_block: &Block<'_>, a_ident: &Ident, b_block: &Block<'_>, b_ident: &Ident) -> bool { - match (a_block, b_block) { - (Block { stmts: left_stmts, expr: left_expr, .. }, - Block { stmts: right_stmts, expr: right_expr, .. }) - => left_stmts.iter().zip(right_stmts.iter()).all(|(left, right)| match (&left.kind, &right.kind) { - (StmtKind::Expr(left_expr), StmtKind::Expr(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (StmtKind::Semi(left_expr), StmtKind::Semi(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (StmtKind::Item(left_item), StmtKind::Item(right_item)) => left_item.id == right_item.id, - (StmtKind::Local(left), StmtKind::Local(right)) => mirrored_locals(cx, left, a_ident, right, b_ident), - _ => false, - }) && match (left_expr, right_expr) { - (None, None) => true, - (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - _ => false, - }, - } -} - -/// Check that the two "Local"s (let statements) are equal -fn mirrored_locals(cx: &LateContext<'_, '_>, a_local: &Local<'_>, a_ident: &Ident, b_local: &Local<'_>, b_ident: &Ident) -> bool { - match (a_local, b_local) { - (Local { pat: left_pat, init: left_expr, .. }, Local { pat: right_pat, init: right_expr, .. }) - => match (left_expr, right_expr) { - (None, None) => true, - (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - _ => false, - }, + _ => false, + // (left, right) => { + // println!("{:?}\n{:?}", left, right); + // false + // }, } } @@ -154,8 +122,12 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option then { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - let closure_arg = a_ident.name.to_ident_string(); - let closure_reverse_body = Sugg::hir(cx, &a_expr, "..").to_string(); + let closure_arg = format!("&{}", b_ident.name.to_ident_string()); + let closure_reverse_body = Sugg::hir(cx, &b_expr, "..").to_string(); + // Get rid of parentheses, because they aren't needed anymore + // while closure_reverse_body.chars().next() == Some('(') && closure_reverse_body.chars().last() == Some(')') { + // closure_reverse_body = String::from(&closure_reverse_body[1..closure_reverse_body.len()-1]); + // } Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) } else { None diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index 4b18a073e1a..d536dc385d5 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -1,9 +1,15 @@ // run-rustfix #![warn(clippy::sort_by_key_reverse)] +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; - vec.sort_by_key(|a| Reverse(a)); - vec.sort_by_key(|a| Reverse(&(a+5).abs())); - vec.sort_by_key(|a| Reverse(&-a)); + vec.sort_by_key(|&b| Reverse(b)); + vec.sort_by_key(|&b| Reverse((b + 5).abs())); + vec.sort_by_key(|&b| Reverse(id(-b))); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index f4fb70b7b1d..9c42d401755 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,9 +1,15 @@ // run-rustfix #![warn(clippy::sort_by_key_reverse)] +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - vec.sort_by(|a, b| (-b).cmp(&-a)); + vec.sort_by(|a, b| id(-b).cmp(&id(-a))); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index 36a28c04b1c..3d26ddae78a 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -1,22 +1,22 @@ error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:6:5 + --> $DIR/sort_by_key_reverse.rs:12:5 | LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(a))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` | = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:7:5 + --> $DIR/sort_by_key_reverse.rs:13:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&(a+5).abs()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:8:5 + --> $DIR/sort_by_key_reverse.rs:14:5 | -LL | vec.sort_by(|a, b| (-b).cmp(&-a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&-a))` +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 955a25ee7db234a8ab697176a433070702aabe59 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 20 May 2020 09:23:00 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 31629a1dbc1..ea850955db1 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -43,64 +43,105 @@ struct LintTrigger { /// Detect if the two expressions are mirrored (identical, except one /// contains a and the other replaces it with b) -fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { +fn mirrored_exprs( + cx: &LateContext<'_, '_>, + a_expr: &Expr<'_>, + a_ident: &Ident, + b_expr: &Expr<'_>, + b_ident: &Ident, +) -> bool { match (&a_expr.kind, &b_expr.kind) { // Two boxes with mirrored contents - (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, // Two arrays with mirrored contents - (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) - => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored - (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) - => { - // println!("{:?}\n{:?}\n", left_expr, left_args); - // println!("{:?}\n{:?}\n", right_expr, right_args); - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments - (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) - => left_segment.ident == right_segment.ident - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { + left_segment.ident == right_segment.ident + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // Two tuples with mirrored contents - (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) - => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), // Two binary ops, which are the same operation and which have mirrored arguments - (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) - => left_op.node == right_op.node + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { + left_op.node == right_op.node && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) - && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident), + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) + }, // Two unary ops, which are the same operation and which have the same argument - (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) - => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { + left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, // The two exprs are literals of some kind (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left_expr, left_ty), ExprKind::Cast(right_expr, right_ty)) - => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) - => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), + (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { + mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) + }, + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { + left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) + }, // Two paths: either one is a and the other is b, or they're identical to each other - (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), - ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) - => (left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) - && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) - || (left_segments.len() == 1 && &left_segments[0].ident == a_ident && right_segments.len() == 1 && &right_segments[0].ident == b_ident), + ( + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: left_segments, + .. + }, + )), + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: right_segments, + .. + }, + )), + ) => { + (left_segments + .iter() + .zip(right_segments.iter()) + .all(|(left, right)| left.ident == right.ident) + && left_segments + .iter() + .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 + && &left_segments[0].ident == a_ident + && right_segments.len() == 1 + && &right_segments[0].ident == b_ident) + }, // Matching expressions, but one or both is borrowed - (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) - => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) - => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), - (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) - => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + ( + ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), + ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), + ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { + mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) + }, + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), _ => false, - // (left, right) => { - // println!("{:?}\n{:?}", left, right); - // false - // }, } } diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index d536dc385d5..722675a6b71 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -12,4 +12,11 @@ fn main() { vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); vec.sort_by_key(|&b| Reverse(id(-b))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); + vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 9c42d401755..601621ffa9f 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -10,6 +10,13 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); + vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index 3d26ddae78a..b757c8a6176 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -9,8 +9,8 @@ LL | vec.sort_by(|a, b| b.cmp(a)); error: use Vec::sort_by_key here instead --> $DIR/sort_by_key_reverse.rs:13:5 | -LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead --> $DIR/sort_by_key_reverse.rs:14:5 -- cgit 1.4.1-3-g733a5 From 059e8edd15401d5544260e4058731dc8818578d5 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 24 May 2020 19:45:41 -0700 Subject: Detect also a non-reversed comparison --- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/sort_by_key.rs | 207 ++++++++++++++++++++++++++++++++ clippy_lints/src/sort_by_key_reverse.rs | 199 ------------------------------ tests/ui/sort_by_key.fixed | 26 ++++ tests/ui/sort_by_key.rs | 26 ++++ tests/ui/sort_by_key.stderr | 48 ++++++++ tests/ui/sort_by_key_reverse.fixed | 22 ---- tests/ui/sort_by_key_reverse.rs | 22 ---- tests/ui/sort_by_key_reverse.stderr | 22 ---- 9 files changed, 312 insertions(+), 270 deletions(-) create mode 100644 clippy_lints/src/sort_by_key.rs delete mode 100644 clippy_lints/src/sort_by_key_reverse.rs create mode 100644 tests/ui/sort_by_key.fixed create mode 100644 tests/ui/sort_by_key.rs create mode 100644 tests/ui/sort_by_key.stderr delete mode 100644 tests/ui/sort_by_key_reverse.fixed delete mode 100644 tests/ui/sort_by_key_reverse.rs delete mode 100644 tests/ui/sort_by_key_reverse.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7a4c1ecaa9..9e826316f21 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod sort_by_key_reverse; +mod sort_by_key; mod strings; mod suspicious_trait_impl; mod swap; @@ -780,7 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &sort_by_key_reverse::SORT_BY_KEY_REVERSE, + &sort_by_key::SORT_BY_KEY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -998,7 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box sort_by_key_reverse::SortByKeyReverse); + store.register_late_pass(|| box sort_by_key::SortByKey); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1394,7 +1394,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), + LintId::of(&sort_by_key::SORT_BY_KEY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1596,7 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), + LintId::of(&sort_by_key::SORT_BY_KEY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs new file mode 100644 index 00000000000..109845a28f4 --- /dev/null +++ b/clippy_lints/src/sort_by_key.rs @@ -0,0 +1,207 @@ +use crate::utils; +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_lint::{LateContext, LateLintPass}; +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 + /// 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 + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// ``` + /// Use instead: + /// ```rust + /// vec.sort_by_key(|a| a.foo()); + /// ``` + pub SORT_BY_KEY, + complexity, + "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" +} + +declare_lint_pass!(SortByKey => [SORT_BY_KEY]); + +struct LintTrigger { + vec_name: String, + closure_arg: String, + closure_body: String, + 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, _), 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) + }, + // 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, + } +} + +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_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(_, _, left_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } + ] = &closure_body.params; + 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( + &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"; + Some(LintTrigger { vec_name, unstable, closure_arg, closure_body }) + } else { + None + } + } +} + +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, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|&{}| {})", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_body, + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs deleted file mode 100644 index ea850955db1..00000000000 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ /dev/null @@ -1,199 +0,0 @@ -use crate::utils; -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_lint::{LateContext, LateLintPass}; -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 - /// 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 - /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); - /// ``` - /// Use instead: - /// ```rust - /// vec.sort_by_key(|e| Reverse(e.foo())); - /// ``` - pub SORT_BY_KEY_REVERSE, - complexity, - "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" -} - -declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); - -struct LintTrigger { - vec_name: String, - closure_arg: String, - closure_reverse_body: String, - 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, _), 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) - }, - // 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, - } -} - -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_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"; - 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 - } - } -} - -impl LateLintPass<'_, '_> for SortByKeyReverse { - 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, - 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/tests/ui/sort_by_key.fixed b/tests/ui/sort_by_key.fixed new file mode 100644 index 00000000000..f6535c8d8f5 --- /dev/null +++ b/tests/ui/sort_by_key.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::sort_by_key)] + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +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))); + // 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)); +} diff --git a/tests/ui/sort_by_key.rs b/tests/ui/sort_by_key.rs new file mode 100644 index 00000000000..953c573d406 --- /dev/null +++ b/tests/ui/sort_by_key.rs @@ -0,0 +1,26 @@ +// 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]; + // 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))); + // 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)); +} diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr new file mode 100644 index 00000000000..fa6a9a0fb10 --- /dev/null +++ b/tests/ui/sort_by_key.stderr @@ -0,0 +1,48 @@ +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:13:5 + | +LL | vec.sort_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| a)` + | + = note: `-D clippy::sort-by-key` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:14:5 + | +LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:15:5 + | +LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:17:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:18:5 + | +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:19:5 + | +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` + +error: unknown clippy lint: clippy::sort_by_key_reverse + --> $DIR/sort_by_key.rs:2:9 + | +LL | #![warn(clippy::sort_by_key_reverse)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed deleted file mode 100644 index 722675a6b71..00000000000 --- a/tests/ui/sort_by_key_reverse.fixed +++ /dev/null @@ -1,22 +0,0 @@ -// 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(|&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 deleted file mode 100644 index 601621ffa9f..00000000000 --- a/tests/ui/sort_by_key_reverse.rs +++ /dev/null @@ -1,22 +0,0 @@ -// 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| 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 deleted file mode 100644 index b757c8a6176..00000000000 --- a/tests/ui/sort_by_key_reverse.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:12:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` - | - = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:13:5 - | -LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:14:5 - | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 07886a97640b89f72b70805f519bd9d42d7d1c4e Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 24 May 2020 20:05:58 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs index 109845a28f4..f720d14473a 100644 --- a/clippy_lints/src/sort_by_key.rs +++ b/clippy_lints/src/sort_by_key.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -16,7 +16,7 @@ declare_clippy_lint! { /// **Why is this bad?** /// It is more clear to use `Vec::sort_by_key` (or /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than - /// using + /// using /// /// **Known problems:** None. /// @@ -36,7 +36,17 @@ declare_clippy_lint! { declare_lint_pass!(SortByKey => [SORT_BY_KEY]); -struct LintTrigger { +enum LintTrigger { + Sort(SortDetection), + SortByKey(SortByKeyDetection), +} + +struct SortDetection { + vec_name: String, + unstable: bool, +} + +struct SortByKeyDetection { vec_name: String, closure_arg: String, closure_body: String, @@ -177,7 +187,18 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option }; let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - Some(LintTrigger { vec_name, unstable, closure_arg, closure_body }) + if_chain! { + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind; + if left_name == left_ident; + then { + Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } + else { + Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + } + } } else { None } @@ -186,8 +207,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_, '_> for SortByKey { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - if let Some(trigger) = detect_lint(cx, expr) { - utils::span_lint_and_sugg( + match detect_lint(cx, expr) { + Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( cx, SORT_BY_KEY, expr.span, @@ -201,7 +222,21 @@ impl LateLintPass<'_, '_> for SortByKey { trigger.closure_body, ), Applicability::MachineApplicable, - ); + ), + Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + cx, + SORT_BY_KEY, + expr.span, + "use Vec::sort here instead", + "try", + format!( + "{}.sort{}()", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + ), + Applicability::MachineApplicable, + ), + None => {}, } } } diff --git a/tests/ui/sort_by_key.fixed b/tests/ui/sort_by_key.fixed index f6535c8d8f5..bb88df1a56c 100644 --- a/tests/ui/sort_by_key.fixed +++ b/tests/ui/sort_by_key.fixed @@ -10,7 +10,7 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples - vec.sort_by_key(|&a| a); + vec.sort(); vec.sort_by_key(|&a| (a + 5).abs()); vec.sort_by_key(|&a| id(-a)); // Reverse examples diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr index fa6a9a0fb10..291fd5500f7 100644 --- a/tests/ui/sort_by_key.stderr +++ b/tests/ui/sort_by_key.stderr @@ -1,8 +1,8 @@ -error: use Vec::sort_by_key here instead +error: use Vec::sort here instead --> $DIR/sort_by_key.rs:13:5 | LL | vec.sort_by(|a, b| a.cmp(b)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| a)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | = note: `-D clippy::sort-by-key` implied by `-D warnings` -- cgit 1.4.1-3-g733a5 From 015ab9f9259d58a48c171276f6e7190528f1a9ad Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 28 May 2020 18:18:25 -0700 Subject: Renamed to --- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/sort_by_key.rs | 242 -------------------------------- clippy_lints/src/unnecessary_sort_by.rs | 242 ++++++++++++++++++++++++++++++++ tests/ui/sort_by_key.fixed | 26 ---- tests/ui/sort_by_key.rs | 26 ---- tests/ui/sort_by_key.stderr | 48 ------- tests/ui/unnecessary_sort_by.fixed | 26 ++++ tests/ui/unnecessary_sort_by.rs | 26 ++++ tests/ui/unnecessary_sort_by.stderr | 48 +++++++ 9 files changed, 347 insertions(+), 347 deletions(-) delete mode 100644 clippy_lints/src/sort_by_key.rs create mode 100644 clippy_lints/src/unnecessary_sort_by.rs delete mode 100644 tests/ui/sort_by_key.fixed delete mode 100644 tests/ui/sort_by_key.rs delete mode 100644 tests/ui/sort_by_key.stderr create mode 100644 tests/ui/unnecessary_sort_by.fixed create mode 100644 tests/ui/unnecessary_sort_by.rs create mode 100644 tests/ui/unnecessary_sort_by.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9e826316f21..46df743b5bf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod sort_by_key; +mod unnecessary_sort_by; mod strings; mod suspicious_trait_impl; mod swap; @@ -780,7 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &sort_by_key::SORT_BY_KEY, + &unnecessary_sort_by::UNNECESSARY_SORT_BY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -998,7 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box sort_by_key::SortByKey); + store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1394,7 +1394,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&sort_by_key::SORT_BY_KEY), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1596,7 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&sort_by_key::SORT_BY_KEY), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs deleted file mode 100644 index f720d14473a..00000000000 --- a/clippy_lints/src/sort_by_key.rs +++ /dev/null @@ -1,242 +0,0 @@ -use crate::utils; -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, PathSegment, QPath}; -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:** - /// Detects when people use `Vec::sort_by` and pass in a function - /// 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 - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); - /// ``` - /// Use instead: - /// ```rust - /// vec.sort_by_key(|a| a.foo()); - /// ``` - pub SORT_BY_KEY, - complexity, - "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" -} - -declare_lint_pass!(SortByKey => [SORT_BY_KEY]); - -enum LintTrigger { - Sort(SortDetection), - SortByKey(SortByKeyDetection), -} - -struct SortDetection { - vec_name: String, - unstable: bool, -} - -struct SortByKeyDetection { - vec_name: String, - closure_arg: String, - closure_body: String, - 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, _), 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) - }, - // 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, - } -} - -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_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(_, _, left_ident, _), .. }, ..}, - Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } - ] = &closure_body.params; - 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( - &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"; - 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 - } - } -} - -impl LateLintPass<'_, '_> for SortByKey { - 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, - expr.span, - "use Vec::sort_by_key here instead", - "try", - format!( - "{}.sort{}_by_key(|&{}| {})", - trigger.vec_name, - if trigger.unstable { "_unstable" } else { "" }, - trigger.closure_arg, - 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/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs new file mode 100644 index 00000000000..c0858ec4c88 --- /dev/null +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -0,0 +1,242 @@ +use crate::utils; +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, PathSegment, QPath}; +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:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// 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 + /// closure. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// ``` + /// Use instead: + /// ```rust + /// vec.sort_by_key(|a| a.foo()); + /// ``` + pub UNNECESSARY_SORT_BY, + complexity, + "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer" +} + +declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]); + +enum LintTrigger { + Sort(SortDetection), + SortByKey(SortByKeyDetection), +} + +struct SortDetection { + vec_name: String, + unstable: bool, +} + +struct SortByKeyDetection { + vec_name: String, + closure_arg: String, + closure_body: String, + 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, _), 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) + }, + // 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, + } +} + +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_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(_, _, left_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } + ] = &closure_body.params; + 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( + &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"; + 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 + } + } +} + +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, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|&{}| {})", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_body, + ), + Applicability::MachineApplicable, + ), + Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + 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 deleted file mode 100644 index bb88df1a56c..00000000000 --- a/tests/ui/sort_by_key.fixed +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::sort_by_key)] - -use std::cmp::Reverse; - -fn id(x: isize) -> isize { - x -} - -fn main() { - let mut vec: Vec = vec![3, 6, 1, 2, 5]; - // Forward examples - vec.sort(); - 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))); - // 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)); -} diff --git a/tests/ui/sort_by_key.rs b/tests/ui/sort_by_key.rs deleted file mode 100644 index 953c573d406..00000000000 --- a/tests/ui/sort_by_key.rs +++ /dev/null @@ -1,26 +0,0 @@ -// 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]; - // 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))); - // 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)); -} diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr deleted file mode 100644 index 291fd5500f7..00000000000 --- a/tests/ui/sort_by_key.stderr +++ /dev/null @@ -1,48 +0,0 @@ -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()` - | - = 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/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed new file mode 100644 index 00000000000..bb88df1a56c --- /dev/null +++ b/tests/ui/unnecessary_sort_by.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::sort_by_key)] + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort(); + 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))); + // 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)); +} diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs new file mode 100644 index 00000000000..953c573d406 --- /dev/null +++ b/tests/ui/unnecessary_sort_by.rs @@ -0,0 +1,26 @@ +// 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]; + // 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))); + // 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)); +} diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr new file mode 100644 index 00000000000..291fd5500f7 --- /dev/null +++ b/tests/ui/unnecessary_sort_by.stderr @@ -0,0 +1,48 @@ +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()` + | + = 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 + -- cgit 1.4.1-3-g733a5 From 20cb512e81ad03a014b40c377a01fdebaea66963 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 12:06:32 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 46df743b5bf..fd832d11577 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,6 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod unnecessary_sort_by; mod strings; mod suspicious_trait_impl; mod swap; @@ -319,6 +318,7 @@ mod try_err; mod types; mod unicode; mod unnamed_address; +mod unnecessary_sort_by; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index bb88df1a56c..4521ae38d49 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::sort_by_key)] use std::cmp::Reverse; diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 953c573d406..fdb5a823369 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::sort_by_key_reverse)] use std::cmp::Reverse; diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 291fd5500f7..b6365c1709d 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,48 +1,40 @@ error: use Vec::sort here instead - --> $DIR/sort_by_key.rs:13:5 + --> $DIR/unnecessary_sort_by.rs:12:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | - = note: `-D clippy::sort-by-key` implied by `-D warnings` + = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:13:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:19:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` -error: unknown clippy lint: clippy::sort_by_key_reverse - --> $DIR/sort_by_key.rs:2:9 - | -LL | #![warn(clippy::sort_by_key_reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` - | - = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 32fde0b5116b3a1115d11c49a9bf2af2ebdd5773 Mon Sep 17 00:00:00 2001 From: Ericko Samudera Date: Mon, 25 May 2020 23:22:01 +0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac9057199f..714e25a32ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1401,6 +1401,7 @@ Released 2018-09-13 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 057d39d4c82..7c16dbd8f26 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -669,6 +669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::INTO_ITER_ON_REF, &methods::ITERATOR_STEP_BY_ZERO, &methods::ITER_CLONED_COLLECT, + &methods::ITER_NEXT_SLICE, &methods::ITER_NTH, &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, @@ -1303,6 +1304,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NEXT_SLICE), LintId::of(&methods::ITER_NTH), LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), @@ -1483,6 +1485,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NEXT_SLICE), LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 38a5829b3f7..dbe41823a9c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; +use rustc_span::symbol::Symbol; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; use std::iter::{once, Iterator}; use std::mem; @@ -2381,32 +2381,32 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(collect)); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - ".count()".to_string(), + "count()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(is_empty) { - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(iter)); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - ".next().is_none()".to_string(), + "get(0).is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(collect)); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2422,7 +2422,7 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' span, "replace with", format!( - ".any(|{}| x == {})", + "any(|{}| x == {})", arg, pred ), Applicability::MachineApplicable, @@ -2435,13 +2435,13 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' } } -fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args) = expr.kind; - if let ExprKind::MethodCall(_, ref span, _) = args[0].kind; - then { - return expr.span.with_lo(span.lo() - BytePos(1)); +fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { + let mut current_expr = expr; + while let ExprKind::MethodCall(ref path, ref span, ref args) = current_expr.kind { + if path.ident.name == target_fn_name { + return expr.span.with_lo(span.lo()); } + current_expr = &args[0]; } unreachable!() } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 32b3b7f7947..7cb04d4d81c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,7 +26,7 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, + get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability, @@ -1242,6 +1242,32 @@ declare_clippy_lint! { "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `iter().next()` on a Slice or an Array + /// + /// **Why is this bad?** These can be shortened into `.get()` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a[2..].iter().next(); + /// b.iter().next(); + /// ``` + /// should be written as: + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a.get(2); + /// b.get(0); + /// ``` + pub ITER_NEXT_SLICE, + style, + "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1273,6 +1299,7 @@ declare_lint_pass!(Methods => [ FIND_MAP, MAP_FLATTEN, ITERATOR_STEP_BY_ZERO, + ITER_NEXT_SLICE, ITER_NTH, ITER_NTH_ZERO, ITER_SKIP_NEXT, @@ -1320,6 +1347,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), + ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), @@ -2184,6 +2212,60 @@ fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args } } +fn lint_iter_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { + let caller_expr = &iter_args[0]; + + // Skip lint if the `iter().next()` expression is a for loop argument, + // since it is already covered by `&loops::ITER_NEXT_LOOP` + let mut parent_expr_opt = get_parent_expr(cx, expr); + while let Some(parent_expr) = parent_expr_opt { + if higher::for_loop(parent_expr).is_some() { + return; + } + parent_expr_opt = get_parent_expr(cx, parent_expr); + } + + if derefs_to_slice(cx, caller_expr, cx.tables.expr_ty(caller_expr)).is_some() { + // caller is a Slice + if_chain! { + if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; + if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) + = higher::range(cx, index_expr); + if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; + if let ast::LitKind::Int(start_idx, _) = start_lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on a Slice without end index.", + "try calling", + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + applicability, + ); + } + } + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(caller_expr), sym!(vec_type)) + || matches!(&walk_ptrs_ty(cx.tables.expr_ty(caller_expr)).kind, ty::Array(_, _)) + { + // caller is a Vec or an Array + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on an array", + "try calling", + format!( + "{}.get(0)", + snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) + ), + applicability, + ); + } +} + fn lint_iter_nth<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 28183810df4..a971d041ca6 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -424,7 +424,7 @@ fn erode_from_back(s: &str) -> String { } fn span_of_first_expr_in_block(block: &ast::Block) -> Option { - block.stmts.iter().next().map(|stmt| stmt.span) + block.stmts.get(0).map(|stmt| stmt.span) } #[cfg(test)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8211a57b564..79da1f3702e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -934,6 +934,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "loops", }, + Lint { + name: "iter_next_slice", + group: "style", + desc: "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`", + deprecation: None, + module: "methods", + }, Lint { name: "iter_nth", group: "perf", diff --git a/tests/ui/into_iter_on_ref.fixed b/tests/ui/into_iter_on_ref.fixed index c30d23de3f8..7f92d0dbdc9 100644 --- a/tests/ui/into_iter_on_ref.fixed +++ b/tests/ui/into_iter_on_ref.fixed @@ -40,4 +40,6 @@ fn main() { let _ = (&HashSet::::new()).iter(); //~ WARN equivalent to .iter() let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter() let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter() + + let _ = (&[1, 2, 3]).iter().next(); //~ WARN equivalent to .iter() } diff --git a/tests/ui/into_iter_on_ref.rs b/tests/ui/into_iter_on_ref.rs index 94bc1689619..416056d3fdb 100644 --- a/tests/ui/into_iter_on_ref.rs +++ b/tests/ui/into_iter_on_ref.rs @@ -40,4 +40,6 @@ fn main() { let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() + + let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() } diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 80e2d104f82..1cd6400b019 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -156,5 +156,11 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not move the LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 26 previous errors +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:44:26 + | +LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: aborting due to 27 previous errors diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed new file mode 100644 index 00000000000..79c1db87ac3 --- /dev/null +++ b/tests/ui/iter_next_slice.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.get(0); + // Should be replaced by s.get(0) + + s.get(2); + // Should be replaced by s.get(2) + + v.get(5); + // Should be replaced by v.get(5) + + v.get(0); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs new file mode 100644 index 00000000000..ef9a55f3d99 --- /dev/null +++ b/tests/ui/iter_next_slice.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.iter().next(); + // Should be replaced by s.get(0) + + s[2..].iter().next(); + // Should be replaced by s.get(2) + + v[5..].iter().next(); + // Should be replaced by v.get(5) + + v.iter().next(); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr new file mode 100644 index 00000000000..bbf61df0cda --- /dev/null +++ b/tests/ui/iter_next_slice.stderr @@ -0,0 +1,28 @@ +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:9:5 + | +LL | s.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | + = note: `-D clippy::iter-next-slice` implied by `-D warnings` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:12:5 + | +LL | s[2..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:15:5 + | +LL | v[5..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` + +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:18:5 + | +LL | v.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index b4227eaf2f8..be37dc16b9a 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -9,7 +9,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.iter().next().is_none() { + if sample.get(0).is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 8884c8e1612..9113aad90dd 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,28 +1,28 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:11:28 + --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` | = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:21 + --> $DIR/needless_collect.rs:12:15 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:15:27 + --> $DIR/needless_collect.rs:15:28 | LL | sample.iter().cloned().collect::>().contains(&1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:16:34 + --> $DIR/needless_collect.rs:16:35 | LL | sample.iter().map(|x| (x, x)).collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 9a5baed482b68e0d9806e19eb9e8676d7ff3e1c2 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:09:12 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index c0858ec4c88..33d8331c292 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -18,15 +18,25 @@ declare_clippy_lint! { /// possible) than to use `Vec::sort_by` and and a more complicated /// closure. /// - /// **Known problems:** None. + /// **Known problems:** + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't + /// imported by a use statement in the current frame, then a `use` + /// statement that imports it will need to be added (which this lint + /// can't do). /// /// **Example:** /// /// ```rust - /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); + /// vec.sort_by(|a, b| a.foo().cmp(&b.foo())); /// ``` /// Use instead: /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); /// vec.sort_by_key(|a| a.foo()); /// ``` pub UNNECESSARY_SORT_BY, @@ -50,6 +60,7 @@ struct SortByKeyDetection { vec_name: String, closure_arg: String, closure_body: String, + reverse: bool, unstable: bool, } @@ -172,16 +183,16 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; if method_path.ident.name.to_ident_string() == "cmp"; then { - let (closure_body, closure_arg) = if mirrored_exprs( + let (closure_body, closure_arg, reverse) = if mirrored_exprs( &cx, &left_expr, &left_ident, &right_expr, &right_ident ) { - (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false) } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { - (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) + (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) } else { return None; }; @@ -196,7 +207,13 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) } else { - Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) } } } else { @@ -219,9 +236,17 @@ impl LateLintPass<'_, '_> for UnnecessarySortBy { trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, - trigger.closure_body, + if trigger.reverse { + format!("Reverse({})", trigger.closure_body) + } else { + trigger.closure_body.to_string() + }, ), - Applicability::MachineApplicable, + if trigger.reverse { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, ), Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( cx, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 4521ae38d49..779fd57707a 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -10,16 +10,17 @@ fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort(); + vec.sort_unstable(); vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_by_key(|&a| id(-a)); + vec.sort_unstable_by_key(|&a| id(-a)); // Reverse examples vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_by_key(|&b| Reverse(id(-b))); + vec.sort_unstable_by_key(|&b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index fdb5a823369..0485a5630af 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -10,16 +10,17 @@ fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort_by(|a, b| a.cmp(b)); + vec.sort_unstable_by(|a, b| a.cmp(b)); vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); } diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index b6365c1709d..903b6e5099c 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -6,35 +6,41 @@ LL | vec.sort_by(|a, b| a.cmp(b)); | = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` -error: use Vec::sort_by_key here instead +error: use Vec::sort here instead --> $DIR/unnecessary_sort_by.rs:13:5 | +LL | vec.sort_unstable_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:14:5 + | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | -LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` +LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:16:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` +LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From b89880a30ce4dd7887614f305a565b6779dc4825 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:19:31 -0700 Subject: Ran update_lints --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +++--- src/lintlist/mod.rs | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index c00f84bdb85..086a1141be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1555,7 +1555,6 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization -[`sort_by_key_reverse`]: https://rust-lang.github.io/rust-clippy/master/index.html#sort_by_key_reverse [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -1602,6 +1601,7 @@ Released 2018-09-13 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fd832d11577..03addf1f4a4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -780,7 +780,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &unnecessary_sort_by::UNNECESSARY_SORT_BY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -835,6 +834,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unicode::ZERO_WIDTH_SPACE, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, + &unnecessary_sort_by::UNNECESSARY_SORT_BY, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, @@ -1394,7 +1394,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1431,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unicode::ZERO_WIDTH_SPACE), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1596,7 +1596,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), @@ -1613,6 +1612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_ARG), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b5d9ef0110e..ab9542a7b9c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1984,13 +1984,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, - Lint { - name: "sort_by_key_reverse", - group: "complexity", - desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer", - deprecation: None, - module: "sort_by_key_reverse", - }, Lint { name: "string_add", group: "restriction", @@ -2299,6 +2292,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "no_effect", }, + Lint { + name: "unnecessary_sort_by", + group: "complexity", + desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer", + deprecation: None, + module: "unnecessary_sort_by", + }, Lint { name: "unnecessary_unwrap", group: "complexity", -- cgit 1.4.1-3-g733a5 From 61587c9a294a423fb9eb9d57c5856c9e83e22d42 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 27 May 2020 16:24:53 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 05e2650d0b7..13e61fe98ba 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -24,7 +24,11 @@ declare_clippy_lint! { /// let mut a = 5; /// let b = 0; /// // ... + /// // Bad /// a = a + b; + /// + /// // Good + /// a += b; /// ``` pub ASSIGN_OP_PATTERN, style, diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 7f2ff8b9b26..05517f6f9f0 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -13,10 +13,24 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad + /// fn simple_double_parens() -> i32 { + /// ((0)) + /// } + /// + /// // Good + /// fn simple_no_parens() -> i32 { + /// 0 + /// } + /// + /// // or + /// /// # fn foo(bar: usize) {} - /// ((0)); + /// // Bad /// foo((0)); - /// ((1, 2)); + /// + /// // Good + /// foo(0); /// ``` pub DOUBLE_PARENS, complexity, diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index f4966808279..4ef963ac314 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -27,6 +27,10 @@ declare_clippy_lint! { /// ```rust /// fn foo() {} /// ``` + /// Could be written as: + /// ```rust + /// fn foo() {} + /// ``` pub DROP_BOUNDS, correctness, "Bounds of the form `T: Drop` are useless" diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index b35a8facf8b..afefa250638 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -22,8 +22,14 @@ declare_clippy_lint! { /// ```rust /// # use std::time::Duration; /// let dur = Duration::new(5, 0); + /// + /// // Bad /// let _micros = dur.subsec_nanos() / 1_000; /// let _millis = dur.subsec_nanos() / 1_000_000; + /// + /// // Good + /// let _micros = dur.subsec_micros(); + /// let _millis = dur.subsec_millis(); /// ``` pub DURATION_SUBSEC, complexity, diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index a5871cf0cd4..cb0fd59a2d4 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -25,31 +25,47 @@ declare_clippy_lint! { /// BattenbergCake, /// } /// ``` + /// Could be written as: + /// ```rust + /// enum Cake { + /// BlackForest, + /// Hummingbird, + /// Battenberg, + /// } + /// ``` pub ENUM_VARIANT_NAMES, style, "enums where all variants share a prefix/postfix" } declare_clippy_lint! { - /// **What it does:** Detects enumeration variants that are prefixed or suffixed - /// by the same characters. + /// **What it does:** Detects public enumeration variants that are + /// prefixed or suffixed by the same characters. /// - /// **Why is this bad?** Enumeration variant names should specify their variant, + /// **Why is this bad?** Public enumeration variant names should specify their variant, /// not repeat the enumeration name. /// /// **Known problems:** None. /// /// **Example:** /// ```rust - /// enum Cake { + /// pub enum Cake { /// BlackForestCake, /// HummingbirdCake, /// BattenbergCake, /// } /// ``` + /// Could be written as: + /// ```rust + /// pub enum Cake { + /// BlackForest, + /// Hummingbird, + /// Battenberg, + /// } + /// ``` pub PUB_ENUM_VARIANT_NAMES, pedantic, - "enums where all variants share a prefix/postfix" + "public enums where all variants share a prefix/postfix" } declare_clippy_lint! { @@ -66,6 +82,12 @@ declare_clippy_lint! { /// struct BlackForestCake; /// } /// ``` + /// Could be written as: + /// ```rust + /// mod cake { + /// struct BlackForest; + /// } + /// ``` pub MODULE_NAME_REPETITIONS, pedantic, "type names prefixed/postfixed with their containing module's name" diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 4e1c1f13140..d7819d737ea 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -39,7 +39,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// &x == y + /// + /// // Good + /// x == *y /// ``` pub OP_REF, style, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 1ec60a0e6e6..7227683aa5a 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -28,9 +28,16 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # fn foo(bar: usize) {} + /// + /// // Bad /// let x = Box::new(1); /// foo(*x); /// println!("{}", *x); + /// + /// // Good + /// let x = 1; + /// foo(x); + /// println!("{}", x); /// ``` pub BOXED_LOCAL, perf, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index e3e1136b676..5f0cd1ec72c 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -26,7 +26,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore + /// // Bad /// xs.map(|x| foo(x)) + /// + /// // Good + /// foo(xs) /// ``` /// where `foo(_)` is a plain function that takes the exact argument type of /// `x`. diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 5206266ccf2..37e24ff34f7 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -21,11 +21,17 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let mut x = 0; + /// + /// // Bad /// let a = { /// x = 1; /// 1 /// } + x; /// // Unclear whether a is 1 or 2. + /// + /// // Good + /// x = 1; + /// let a = 1 + x; /// ``` pub EVAL_ORDER_DEPENDENCE, complexity, -- cgit 1.4.1-3-g733a5 From 262c9dc025042646610df879dd9708eea625534d Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 29 May 2020 18:15:42 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 17639cc2a06..575462f25e6 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -18,13 +18,24 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust + /// ```rust,ignore /// struct Foo(i32); + /// + /// // Bad /// impl From for Foo { /// fn from(s: String) -> Self { /// Foo(s.parse().unwrap()) /// } /// } + /// + /// // Good + /// use std::convert::TryFrom; + /// impl TryFrom for Foo { + /// type Error = (); + /// fn try_from(s: String) -> Result { + /// s.parse() + /// } + /// } /// ``` pub FALLIBLE_IMPL_FROM, nursery, @@ -120,7 +131,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it move |diag| { diag.help( "`From` is intended for infallible conversions only. \ - Use `TryFrom` if there's a possibility for the conversion to fail."); + Use `TryFrom` if there's a possibility for the conversion to fail."); diag.span_note(fpu.result, "potential failure(s)"); }); } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 86317fb8bd5..3a912d92837 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -28,7 +28,6 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// /// let a = 3f32; /// let _ = a.powf(1.0 / 3.0); /// let _ = (1.0 + a).ln(); @@ -38,7 +37,6 @@ declare_clippy_lint! { /// is better expressed as /// /// ```rust - /// /// let a = 3f32; /// let _ = a.cbrt(); /// let _ = a.ln_1p(); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 5b092526ce4..1530538aa7d 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -25,9 +25,13 @@ declare_clippy_lint! { /// /// **Examples:** /// ```rust + /// + /// // Bad /// # let foo = "foo"; - /// format!("foo"); /// format!("{}", foo); + /// + /// // Good + /// format!("foo"); /// ``` pub USELESS_FORMAT, complexity, diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index c24a24733d7..9b5f1dee7f4 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -49,11 +49,11 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ``` rust + /// ```rust /// fn im_too_long() { - /// println!(""); + /// println!(""); /// // ... 100 more LoC - /// println!(""); + /// println!(""); /// } /// ``` pub TOO_MANY_LINES, @@ -79,10 +79,16 @@ declare_clippy_lint! { /// `some_argument.get_raw_ptr()`). /// /// **Example:** - /// ```rust + /// ```rust,ignore + /// // Bad /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // Good + /// pub unsafe fn foo(x: *const u8) { + /// println!("{}", unsafe { *x }); + /// } /// ``` pub NOT_UNSAFE_PTR_ARG_DEREF, correctness, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 155a93de4fa..fdaf37e5e08 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -25,13 +25,6 @@ declare_clippy_lint! { /// if i != 0 { /// i -= 1; /// } - /// ``` - /// Use instead: - /// ```rust - /// let end: u32 = 10; - /// let start: u32 = 5; - /// - /// let mut i: u32 = end - start; /// /// // Good /// i = i.saturating_sub(1); diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index d5dbd495680..e91fb0c2f27 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -10,7 +10,6 @@ use crate::utils::{snippet_opt, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block /// - /// /// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`. /// /// **Known problems:** None. diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index fe34d33fe65..d537ef3f323 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -15,10 +15,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// fn main() { - /// let x = 3 / 2; - /// println!("{}", x); - /// } + /// // Bad + /// let x = 3 / 2; + /// println!("{}", x); + /// + /// // Good + /// let x = 3f32 / 2f32; + /// println!("{}", x); /// ``` pub INTEGER_DIVISION, restriction, diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index e7062b7c16b..c8576bcfcb4 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -16,6 +16,7 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// fn foo() { /// println!("cake"); /// } @@ -28,6 +29,21 @@ declare_clippy_lint! { /// foo(); // prints "foo" /// } /// ``` + /// + /// ```rust + /// // Good + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "foo" + /// foo(); // prints "foo" + /// } + /// ``` pub ITEMS_AFTER_STATEMENTS, pedantic, "blocks where an item comes after a statement" -- cgit 1.4.1-3-g733a5 From 19339334cb4e9c6db5a1f7dced38edcb16707bc7 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 May 2020 11:38:48 +0200 Subject: 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 ++++++ clippy_lints/src/single_component_path_imports.rs | 4 +-- clippy_lints/src/slow_vector_initialization.rs | 8 ++++- clippy_lints/src/strings.rs | 8 +++++ 18 files changed, 195 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index ec7c4531ed7..7ba43562d7d 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -24,7 +24,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let x: u64 = 61864918973511; + /// + /// // Good + /// let x: u64 = 61_864_918_973_511; /// ``` pub UNREADABLE_LITERAL, pedantic, @@ -44,7 +48,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Probably mistyped /// 2_32; + /// + /// // Good + /// 2_i32; /// ``` pub MISTYPED_LITERAL_SUFFIXES, correctness, @@ -63,7 +71,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let x: u64 = 618_64_9189_73_511; + /// + /// // Good + /// let x: u64 = 61_864_918_973_511; /// ``` pub INCONSISTENT_DIGIT_GROUPING, style, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 94380acfcfd..146212cb2c7 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -36,10 +36,17 @@ declare_clippy_lint! { /// ```rust /// # fn bar(stool: &str) {} /// # let x = Some("abc"); + /// + /// // Bad /// match x { /// Some(ref foo) => bar(foo), /// _ => (), /// } + /// + /// // Good + /// if let Some(ref foo) = x { + /// bar(foo); + /// } /// ``` pub SINGLE_MATCH, style, @@ -97,11 +104,19 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore + /// // Bad /// match x { /// &A(ref y) => foo(y), /// &B => bar(), /// _ => frob(&x), /// } + /// + /// // Good + /// match *x { + /// A(ref y) => foo(y), + /// B => bar(), + /// _ => frob(x), + /// } /// ``` pub MATCH_REF_PATS, style, @@ -197,10 +212,15 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let x: Option<()> = None; + /// + /// // Bad /// let r: Option<&()> = match x { /// None => None, /// Some(ref v) => Some(v), /// }; + /// + /// // Good + /// let r: Option<&()> = x.as_ref(); /// ``` pub MATCH_AS_REF, complexity, @@ -219,10 +239,18 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); + /// + /// // Bad /// match x { /// Foo::A(_) => {}, /// _ => {}, /// } + /// + /// // Good + /// match x { + /// Foo::A(_) => {}, + /// Foo::B(_) => {}, + /// } /// ``` pub WILDCARD_ENUM_MATCH_ARM, restriction, @@ -242,16 +270,15 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A, B, C } /// # let x = Foo::B; + /// + /// // Bad /// match x { /// Foo::A => {}, /// Foo::B => {}, /// _ => {}, /// } - /// ``` - /// Use instead: - /// ```rust - /// # enum Foo { A, B, C } - /// # let x = Foo::B; + /// + /// // Good /// match x { /// Foo::A => {}, /// Foo::B => {}, @@ -273,10 +300,17 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// match "foo" { /// "a" => {}, /// "bar" | _ => {}, /// } + /// + /// // Good + /// match "foo" { + /// "a" => {}, + /// _ => {}, + /// } /// ``` pub WILDCARD_IN_OR_PATTERNS, complexity, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index e1d524c2231..a3b7134a376 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -38,10 +38,16 @@ declare_clippy_lint! { /// dereferences, e.g., changing `*x` to `x` within the function. /// /// **Example:** - /// ```rust + /// ```rust,ignore + /// // Bad /// fn foo(ref x: u8) -> bool { /// true /// } + /// + /// // Good + /// fn foo(x: &u8) -> bool { + /// true + /// } /// ``` pub TOPLEVEL_REF_ARG, style, @@ -60,7 +66,11 @@ declare_clippy_lint! { /// ```rust /// # let x = 1.0; /// + /// // Bad /// if x == f32::NAN { } + /// + /// // Good + /// if x.is_nan() { } /// ``` pub CMP_NAN, correctness, @@ -83,8 +93,15 @@ declare_clippy_lint! { /// ```rust /// let x = 1.2331f64; /// let y = 1.2332f64; + /// + /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats + /// + /// // Good + /// let error = 0.01f64; // Use an epsilon for comparison + /// if (y - 1.23f64).abs() < error { } + /// if (y - x).abs() > error { } /// ``` pub FLOAT_CMP, correctness, @@ -191,7 +208,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let a = 0 as *const u32; + /// + /// // Good + /// let a = std::ptr::null::(); /// ``` pub ZERO_PTR, style, @@ -214,7 +235,13 @@ declare_clippy_lint! { /// ```rust /// let x: f64 = 1.0; /// const ONE: f64 = 1.00; - /// x == ONE; // where both are floats + /// + /// // Bad + /// if x == ONE { } // where both are floats + /// + /// // Good + /// let error = 0.1f64; // Use an epsilon for comparison + /// if (x - ONE).abs() < error { } /// ``` pub FLOAT_CMP_CONST, restriction, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 552222eba2e..ad39e59d067 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -59,7 +59,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// fn foo(a: i32, _a: i32) {} + /// + /// // Good + /// fn bar(a: i32, _b: i32) {} /// ``` pub DUPLICATE_UNDERSCORE_ARGUMENT, style, @@ -77,7 +81,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore - /// (|| 42)() + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 /// ``` pub REDUNDANT_CLOSURE_CALL, complexity, @@ -112,7 +120,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// let y = 0x1a9BAcD; + /// + /// // Good + /// let y = 0x1A9BACD; /// ``` pub MIXED_CASE_HEX_LITERALS, style, @@ -129,7 +141,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// let y = 123832i32; + /// + /// // Good + /// let y = 123832_i32; /// ``` pub UNSEPARATED_LITERAL_SUFFIX, pedantic, @@ -207,9 +223,16 @@ declare_clippy_lint! { /// ```rust /// # let v = Some("abc"); /// + /// // Bad + /// match v { + /// Some(x) => (), + /// y @ _ => (), + /// } + /// + /// // Good /// match v { /// Some(x) => (), - /// y @ _ => (), // easier written as `y`, + /// y => (), /// } /// ``` pub REDUNDANT_PATTERN, @@ -235,16 +258,13 @@ declare_clippy_lint! { /// # struct TupleStruct(u32, u32, u32); /// # let t = TupleStruct(1, 2, 3); /// + /// // Bad /// match t { /// TupleStruct(0, .., _) => (), /// _ => (), /// } - /// ``` - /// can be written as - /// ```rust - /// # struct TupleStruct(u32, u32, u32); - /// # let t = TupleStruct(1, 2, 3); /// + /// // Good /// match t { /// TupleStruct(0, ..) => (), /// _ => (), diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 67a1ac78a67..58a8e1a1064 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -16,7 +16,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// my_vec.push(&mut value) + /// + /// // Good + /// my_vec.push(&value) /// ``` pub UNNECESSARY_MUT_PASSED, style, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 4e1a8be4892..78b15afc5a7 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -22,9 +22,15 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// # let y = true; + /// + /// // Bad /// # use std::sync::Mutex; - /// # let y = 1; /// let x = Mutex::new(&y); + /// + /// // Good + /// # use std::sync::atomic::AtomicBool; + /// let x = AtomicBool::new(y); /// ``` pub MUTEX_ATOMIC, perf, @@ -46,6 +52,10 @@ declare_clippy_lint! { /// ```rust /// # use std::sync::Mutex; /// let x = Mutex::new(0usize); + /// + /// // Good + /// # use std::sync::atomic::AtomicUsize; + /// let x = AtomicUsize::new(0usize); /// ``` pub MUTEX_INTEGER, nursery, diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index efa77db822d..15b129fa098 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -15,8 +15,7 @@ use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for expressions of the form `if c { true } else { - /// false }` - /// (or vice versa) and suggest using the condition directly. + /// false }` (or vice versa) and suggests using the condition directly. /// /// **Why is this bad?** Redundant code. /// diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 9ee875d7516..5880d1d6102 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -18,12 +18,16 @@ declare_clippy_lint! { /// **Why is this bad?** Suggests that the receiver of the expression borrows /// the expression. /// + /// **Known problems:** None. + /// /// **Example:** /// ```rust + /// // Bad /// let x: &i32 = &&&&&&5; - /// ``` /// - /// **Known problems:** None. + /// // Good + /// let x: &i32 = &5; + /// ``` pub NEEDLESS_BORROW, nursery, "taking a reference that is going to be automatically dereferenced" diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 9c508fc0e4a..fbdf927b824 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -40,9 +40,8 @@ declare_clippy_lint! { /// assert_eq!(v.len(), 42); /// } /// ``` - /// + /// should be /// ```rust - /// // should be /// fn foo(v: &[i32]) { /// assert_eq!(v.len(), 42); /// } diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 4b2586877e5..d866bab2f64 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -21,6 +21,16 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; + /// + /// // Bad + /// Point { + /// x: 1, + /// y: 1, + /// z: 1, + /// ..zero_point + /// }; + /// + /// // Ok /// Point { /// x: 1, /// y: 1, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 4eac571f966..c77b44e0c99 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -47,7 +47,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// fn foo(&Vec) { .. } + /// + /// // Good + /// fn foo(&[u32]) { .. } /// ``` pub PTR_ARG, style, @@ -65,9 +69,15 @@ declare_clippy_lint! { /// /// **Example:** /// ```ignore + /// // Bad /// if x == ptr::null { /// .. /// } + /// + /// // Good + /// if x.is_null() { + /// .. + /// } /// ``` pub CMP_NULL, style, @@ -76,19 +86,16 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** This lint checks for functions that take immutable - /// references and return - /// mutable ones. + /// references and return mutable ones. /// /// **Why is this bad?** This is trivially unsound, as one can create two - /// mutable references - /// from the same (immutable!) source. This - /// [error](https://github.com/rust-lang/rust/issues/39465) + /// mutable references from the same (immutable!) source. + /// This [error](https://github.com/rust-lang/rust/issues/39465) /// actually lead to an interim Rust release 1.15.1. /// /// **Known problems:** To be on the conservative side, if there's at least one - /// mutable reference - /// with the output lifetime, this lint will not trigger. In practice, this - /// case is unlikely anyway. + /// mutable reference with the output lifetime, this lint will not trigger. + /// In practice, this case is unlikely anyway. /// /// **Example:** /// ```ignore diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ea654467b86..e4361b00fb4 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -88,7 +88,7 @@ impl QuestionMark { replacement_str, applicability, ) - } + } } } } diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index d5797468e9d..fe457aad50e 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -16,8 +16,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore + /// // Bad /// let a = f(*&mut b); /// let c = *&d; + /// + /// // Good + /// let a = f(b); + /// let c = d; /// ``` pub DEREF_ADDROF, complexity, diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 30084e3e1ff..a2c35c42673 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -86,11 +86,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex { if let Some(span) = is_expn_of(expr.span, "regex"); then { if !self.spans.contains(&span) { - span_lint(cx, - REGEX_MACRO, - span, - "`regex!(_)` found. \ - Please use `Regex::new(_)`, which is faster for now."); + span_lint( + cx, + REGEX_MACRO, + span, + "`regex!(_)` found. \ + Please use `Regex::new(_)`, which is faster for now." + ); self.spans.insert(span); } self.last = Some(block.hir_id); diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 11360b0ef84..68c36f91891 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -25,7 +25,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let x = 1; + /// + /// // Bad /// let x = &x; + /// + /// // Good + /// let y = &x; // use different variable name /// ``` pub SHADOW_SAME, restriction, @@ -77,7 +82,12 @@ declare_clippy_lint! { /// # let y = 1; /// # let z = 2; /// let x = y; + /// + /// // Bad /// let x = z; // shadows the earlier binding + /// + /// // Good + /// let w = z; // use different variable name /// ``` pub SHADOW_UNRELATED, pedantic, diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 8d767a7fec8..2e853e8301d 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust, ignore + /// ```rust,ignore /// use regex; /// /// fn main() { @@ -24,7 +24,7 @@ declare_clippy_lint! { /// } /// ``` /// Better as - /// ```rust, ignore + /// ```rust,ignore /// fn main() { /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); /// } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index fb3706be1c2..a7c4f2c2291 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -22,11 +22,17 @@ declare_clippy_lint! { /// ```rust /// # use core::iter::repeat; /// # let len = 4; + /// + /// // Bad /// let mut vec1 = Vec::with_capacity(len); /// vec1.resize(len, 0); /// /// let mut vec2 = Vec::with_capacity(len); - /// vec2.extend(repeat(0).take(len)) + /// vec2.extend(repeat(0).take(len)); + /// + /// // Good + /// let mut vec1 = vec![0; len]; + /// let mut vec2 = vec![0; len]; /// ``` pub SLOW_VECTOR_INITIALIZATION, perf, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 2c51271e312..f84566ef707 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -24,6 +24,10 @@ declare_clippy_lint! { /// ```rust /// let mut x = "Hello".to_owned(); /// x = x + ", World"; + /// + /// // More readable + /// x += ", World"; + /// x.push_str(", World"); /// ``` pub STRING_ADD_ASSIGN, pedantic, @@ -69,7 +73,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// let bs = "a byte string".as_bytes(); + /// + /// // Good + /// let bs = b"a byte string"; /// ``` pub STRING_LIT_AS_BYTES, style, -- cgit 1.4.1-3-g733a5 From 9893254dff38c2644612c8465ae9abfa553f4ea3 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 May 2020 12:08:41 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 52ca962e7ef..fbc29efdeb2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -218,7 +218,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let x = Ok::<_, ()>(()); - /// x.ok().expect("why did I do this again?") + /// + /// // Bad + /// x.ok().expect("why did I do this again?"); + /// + /// // Good + /// x.expect("why did I do this again?"); /// ``` pub OK_EXPECT, style, @@ -273,8 +278,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let opt = Some(1); - /// opt.map_or(None, |a| Some(a + 1)) - /// # ; + /// + /// // Bad + /// opt.map_or(None, |a| Some(a + 1)); + /// + /// // Good + /// opt.and_then(|a| Some(a + 1)); /// ``` pub OPTION_MAP_OR_NONE, style, @@ -390,14 +399,19 @@ declare_clippy_lint! { /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, /// /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// single method call using `_.flat_map(_)` /// /// **Known problems:** /// /// **Example:** /// ```rust /// let vec = vec![vec![1]]; + /// + /// // Bad /// vec.iter().map(|x| x.iter()).flatten(); + /// + /// // Good + /// vec.iter().flat_map(|x| x.iter()); /// ``` pub MAP_FLATTEN, pedantic, @@ -417,7 +431,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let vec = vec![1]; + /// + /// // Bad /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); + /// + /// // Good + /// vec.iter().filter_map(|x| Some(*x * 2)); /// ``` pub FILTER_MAP, pedantic, @@ -634,7 +653,12 @@ declare_clippy_lint! { /// ```rust /// # use std::rc::Rc; /// let x = Rc::new(1); + /// + /// // Bad /// x.clone(); + /// + /// // Good + /// Rc::clone(&x); /// ``` pub CLONE_ON_REF_PTR, restriction, @@ -741,7 +765,12 @@ declare_clippy_lint! { /// **Known problems:** Does not catch multi-byte unicode characters. /// /// **Example:** - /// `_.split("x")` could be `_.split('x')` + /// ```rust,ignore + /// // Bad + /// _.split("x"); + /// + /// // Good + /// _.split('x'); pub SINGLE_CHAR_PATTERN, perf, "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" @@ -964,8 +993,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `.chars().last()` or - /// `.chars().next_back()` on a `str` to check if it ends with a given char. + /// **What it does:** Checks for usage of `_.chars().last()` or + /// `_.chars().next_back()` on a `str` to check if it ends with a given char. /// /// **Why is this bad?** Readability, this can be written more concisely as /// `_.ends_with(_)`. @@ -975,8 +1004,12 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let name = "_"; - /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-') - /// # ; + /// + /// // Bad + /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); + /// + /// // Good + /// name.ends_with('_') || name.ends_with('-'); /// ``` pub CHARS_LAST_CMP, style, @@ -1044,17 +1077,15 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None }); - /// ``` - /// As there is no transformation of the argument this could be written as: - /// ```rust + /// + /// // As there is no transformation of the argument this could be written as: /// let _ = (0..3).filter(|&x| x > 2); /// ``` /// /// ```rust /// let _ = (0..4).filter_map(|x| Some(x + 1)); - /// ``` - /// As there is no conditional check on the argument this could be written as: - /// ```rust + /// + /// // As there is no conditional check on the argument this could be written as: /// let _ = (0..4).map(|x| x + 1); /// ``` pub UNNECESSARY_FILTER_MAP, @@ -1075,7 +1106,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// // Bad /// let _ = (&vec![3, 4, 5]).into_iter(); + /// + /// // Good + /// let _ = (&vec![3, 4, 5]).iter(); /// ``` pub INTO_ITER_ON_REF, style, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index a3b7134a376..51282ab93ef 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { /// ```rust /// let x = 1.2331f64; /// let y = 1.2332f64; - /// + /// /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 1174f421577..a8d4c7620b1 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -17,8 +17,14 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust,ignore - /// foo(&vec![1, 2]) + /// ```rust + /// # fn foo(my_vec: &[u8]) {} + /// + /// // Bad + /// foo(&vec![1, 2]); + /// + /// // Good + /// foo(&[1, 2]); /// ``` pub USELESS_VEC, perf, diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs index 4d8d4438d88..7247518e19b 100644 --- a/clippy_lints/src/verbose_file_reads.rs +++ b/clippy_lints/src/verbose_file_reads.rs @@ -9,6 +9,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) + /// /// **Known problems:** None. /// /// **Example:** diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 32d9a45c37d..b637253bd02 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -19,8 +19,14 @@ declare_clippy_lint! { /// still around. /// /// **Example:** - /// ```rust + /// ```rust,ignore + /// // Bad /// use std::cmp::Ordering::*; + /// foo(Less); + /// + /// // Good + /// use std::cmp::Ordering; + /// foo(Ordering::Less) /// ``` pub ENUM_GLOB_USE, pedantic, @@ -60,15 +66,15 @@ declare_clippy_lint! { /// /// **Example:** /// - /// Bad: /// ```rust,ignore + /// // Bad /// use crate1::*; /// /// foo(); /// ``` /// - /// Good: /// ```rust,ignore + /// // Good /// use crate1::foo; /// /// foo(); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 5f794598052..22ce484b24e 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -23,7 +23,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// // Bad /// println!(""); + /// + /// // Good + /// println!(); /// ``` pub PRINTLN_EMPTY_STRING, style, @@ -32,8 +36,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** This lint warns when you use `print!()` with a format - /// string that - /// ends in a newline. + /// string that ends in a newline. /// /// **Why is this bad?** You should use `println!()` instead, which appends the /// newline. @@ -125,7 +128,12 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); + /// + /// // Bad /// writeln!(buf, ""); + /// + /// // Good + /// writeln!(buf); /// ``` pub WRITELN_EMPTY_STRING, style, @@ -147,7 +155,12 @@ declare_clippy_lint! { /// # use std::fmt::Write; /// # let mut buf = String::new(); /// # let name = "World"; + /// + /// // Bad /// write!(buf, "Hello {}!\n", name); + /// + /// // Good + /// writeln!(buf, "Hello {}!", name); /// ``` pub WRITE_WITH_NEWLINE, style, @@ -168,7 +181,12 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); + /// + /// // Bad /// writeln!(buf, "{}", "foo"); + /// + /// // Good + /// writeln!(buf, "foo"); /// ``` pub WRITE_LITERAL, style, diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index fb4700d8743..0820385e01b 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -14,7 +14,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// 0.0f32 / 0.0; + /// // Bad + /// let nan = 0.0f32 / 0.0; + /// + /// // Good + /// let nan = f32::NAN; /// ``` pub ZERO_DIVIDED_BY_ZERO, complexity, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ab9542a7b9c..6b6e2c7324c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1735,7 +1735,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "pub_enum_variant_names", group: "pedantic", - desc: "enums where all variants share a prefix/postfix", + desc: "public enums where all variants share a prefix/postfix", deprecation: None, module: "enum_variants", }, -- cgit 1.4.1-3-g733a5 From 137a3b4d3242cfe331f8f9b87c51ac0c431fe160 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 1 Jun 2020 10:16:01 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index 4ef963ac314..5a7f759486e 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` /// Could be written as: /// ```rust - /// fn foo() {} + /// fn foo() {} /// ``` pub DROP_BOUNDS, correctness, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5f0cd1ec72c..d093025fd3d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// xs.map(|x| foo(x)) /// /// // Good - /// foo(xs) + /// xs.map(foo) /// ``` /// where `foo(_)` is a plain function that takes the exact argument type of /// `x`. diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 37e24ff34f7..74144fb299d 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -30,8 +30,11 @@ declare_clippy_lint! { /// // Unclear whether a is 1 or 2. /// /// // Good - /// x = 1; - /// let a = 1 + x; + /// let tmp = { + /// x = 1; + /// 1 + /// }; + /// let a = tmp + x; /// ``` pub EVAL_ORDER_DEPENDENCE, complexity, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 575462f25e6..92812816461 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust,ignore + /// ```rust /// struct Foo(i32); /// /// // Bad @@ -27,13 +27,21 @@ declare_clippy_lint! { /// Foo(s.parse().unwrap()) /// } /// } + /// ``` /// + /// ```rust /// // Good + /// struct Foo(i32); + /// /// use std::convert::TryFrom; /// impl TryFrom for Foo { /// type Error = (); /// fn try_from(s: String) -> Result { - /// s.parse() + /// if let Ok(parsed) = s.parse() { + /// Ok(Foo(parsed)) + /// } else { + /// Err(()) + /// } /// } /// } /// ``` diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 9b5f1dee7f4..325b6cf32a3 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -52,7 +52,7 @@ declare_clippy_lint! { /// ```rust /// fn im_too_long() { /// println!(""); - /// // ... 100 more LoC + /// // ... 100 more LoC /// println!(""); /// } /// ``` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fbc29efdeb2..a8d5c10d5da 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -436,7 +436,11 @@ declare_clippy_lint! { /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); /// /// // Good - /// vec.iter().filter_map(|x| Some(*x * 2)); + /// vec.iter().filter_map(|x| if *x == 0 { + /// Some(*x * 2) + /// } else { + /// None + /// }); /// ``` pub FILTER_MAP, pedantic, -- cgit 1.4.1-3-g733a5 From fbf0b84b32aab798258838d5e932dbc56c4a1813 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 2 Jun 2020 21:42:33 +0700 Subject: Make use of slice pattern --- clippy_lints/src/utils/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 6c1488664bf..7e07e7751e3 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -165,8 +165,8 @@ pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&st /// Checks if an expression references a variable of the given name. pub fn match_var(expr: &Expr<'_>, var: Name) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == var { - return true; + if let [p] = path.segments { + return p.ident.name == var; } } false @@ -181,8 +181,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { match *path { - QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]), - QPath::Resolved(..) => None, + QPath::Resolved(_, ref path) => path.segments.get(0), QPath::TypeRelative(_, ref seg) => Some(seg), } } @@ -201,9 +200,12 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { QPath::Resolved(_, ref path) => match_path(path, segments), QPath::TypeRelative(ref ty, ref segment) => match ty.kind { TyKind::Path(ref inner_path) => { - !segments.is_empty() - && match_qpath(inner_path, &segments[..(segments.len() - 1)]) - && segment.ident.name.as_str() == segments[segments.len() - 1] + if let [prefix @ .., end] = segments { + if match_qpath(inner_path, prefix) { + return segment.ident.name.as_str() == *end; + } + } + false }, _ => false, }, -- cgit 1.4.1-3-g733a5 From b39fd5f62f80cb9bb47ac44d7100f694e0c7301c Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 3 Jun 2020 09:04:24 +0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d9776dd50a8..e845ef99c7c 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -58,24 +58,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions { } }; - if_chain! { - if let Some(cv) = result; - if let Some(to_type) = cv.to_type; - - then { + if let Some(cv) = result { + if let Some(to_type) = cv.to_type { let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut - applicability); + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); span_lint_and_sugg( cx, CHECKED_CONVERSIONS, item.span, "Checked cast can be simplified.", "try", - format!("{}::try_from({}).is_ok()", - to_type, - snippet), - applicability + format!("{}::try_from({}).is_ok()", to_type, snippet), + applicability, ); } } @@ -184,7 +178,7 @@ fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { if_chain! { if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind; if let Some((candidate, check)) = normalize_le_ge(op, left, right); - if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS); + if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); then { Conversion::try_new(candidate, from, to) @@ -224,7 +218,7 @@ fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> O /// Check for `expr >= (to_type::MIN as from_type)` fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { - if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) { + if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") { Conversion::try_new(candidate, from, to) } else { None @@ -232,10 +226,16 @@ fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Op } /// Tries to extract the from- and to-type from a cast expression -fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> { - // `to_type::maxmin_value() as from_type` +fn get_types_from_cast<'a>( + expr: &'a Expr<'_>, + types: &'a [&str], + func: &'a str, + assoc_const: &'a str, +) -> Option<(&'a str, &'a str)> { + // `to_type::max_value() as from_type` + // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { - // to_type::maxmin_value(), from_type + // to_type::max_value(), from_type if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; if let TyKind::Path(ref from_type_path) = &from_type.kind; if let Some(from_sym) = int_ty_to_sym(from_type_path); @@ -247,17 +247,17 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) } }; - // `from_type::from(to_type::maxmin_value())` + // `from_type::from(to_type::max_value())` let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { if_chain! { - // `from_type::from, to_type::maxmin_value()` + // `from_type::from, to_type::max_value()` if let ExprKind::Call(ref from_func, ref args) = &expr.kind; - // `to_type::maxmin_value()` + // `to_type::max_value()` if args.len() == 1; if let limit = &args[0]; // `from_type::from` if let ExprKind::Path(ref path) = &from_func.kind; - if let Some(from_sym) = get_implementing_type(path, INTS, FROM); + if let Some(from_sym) = get_implementing_type(path, INTS, "from"); then { Some((limit, from_sym)) @@ -268,22 +268,26 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) }); if let Some((limit, from_type)) = limit_from { - if_chain! { - if let ExprKind::Call(ref fun_name, _) = &limit.kind; - // `to_type, maxmin_value` - if let ExprKind::Path(ref path) = &fun_name.kind; - // `to_type` - if let Some(to_type) = get_implementing_type(path, types, func); - - then { - Some((from_type, to_type)) - } else { - None - } + match limit.kind { + // `from_type::from(_)` + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref path) = path.kind { + // `to_type` + if let Some(to_type) = get_implementing_type(path, types, func) { + return Some((from_type, to_type)); + } + } + }, + // `to_type::MAX` + ExprKind::Path(ref path) => { + if let Some(to_type) = get_implementing_type(path, types, assoc_const) { + return Some((from_type, to_type)); + } + }, + _ => {}, } - } else { - None - } + }; + None } /// Gets the type which implements the called function @@ -336,10 +340,6 @@ fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> O } // Constants -const FROM: &str = "from"; -const MAX_VALUE: &str = "max_value"; -const MIN_VALUE: &str = "min_value"; - const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"]; diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 7febd6f3761..12290db3dcf 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,106 +1,76 @@ // run-rustfix +#![allow( + clippy::cast_lossless, + // Int::max_value will be deprecated in the future + deprecated, +)] #![warn(clippy::checked_conversions)] -#![allow(clippy::cast_lossless)] -#![allow(dead_code)] + use std::convert::TryFrom; // Positive tests // Signed to unsigned -fn i64_to_u32(value: i64) -> Option { - if u32::try_from(value).is_ok() { - Some(value as u32) - } else { - None - } +pub fn i64_to_u32(value: i64) { + let _ = u32::try_from(value).is_ok(); + let _ = u32::try_from(value).is_ok(); } -fn i64_to_u16(value: i64) -> Option { - if u16::try_from(value).is_ok() { - Some(value as u16) - } else { - None - } +pub fn i64_to_u16(value: i64) { + let _ = u16::try_from(value).is_ok(); + let _ = u16::try_from(value).is_ok(); } -fn isize_to_u8(value: isize) -> Option { - if u8::try_from(value).is_ok() { - Some(value as u8) - } else { - None - } +pub fn isize_to_u8(value: isize) { + let _ = u8::try_from(value).is_ok(); + let _ = u8::try_from(value).is_ok(); } // Signed to signed -fn i64_to_i32(value: i64) -> Option { - if i32::try_from(value).is_ok() { - Some(value as i32) - } else { - None - } +pub fn i64_to_i32(value: i64) { + let _ = i32::try_from(value).is_ok(); + let _ = i32::try_from(value).is_ok(); } -fn i64_to_i16(value: i64) -> Option { - if i16::try_from(value).is_ok() { - Some(value as i16) - } else { - None - } +pub fn i64_to_i16(value: i64) { + let _ = i16::try_from(value).is_ok(); + let _ = i16::try_from(value).is_ok(); } // Unsigned to X -fn u32_to_i32(value: u32) -> Option { - if i32::try_from(value).is_ok() { - Some(value as i32) - } else { - None - } +pub fn u32_to_i32(value: u32) { + let _ = i32::try_from(value).is_ok(); + let _ = i32::try_from(value).is_ok(); } -fn usize_to_isize(value: usize) -> isize { - if isize::try_from(value).is_ok() && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn usize_to_isize(value: usize) { + let _ = isize::try_from(value).is_ok() && value as i32 == 5; + let _ = isize::try_from(value).is_ok() && value as i32 == 5; } -fn u32_to_u16(value: u32) -> isize { - if u16::try_from(value).is_ok() && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn u32_to_u16(value: u32) { + let _ = u16::try_from(value).is_ok() && value as i32 == 5; + let _ = u16::try_from(value).is_ok() && value as i32 == 5; } // Negative tests -fn no_i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= 0 { - Some(value as i32) - } else { - None - } +pub fn no_i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= 0; + let _ = value <= (i32::MAX as i64) && value >= 0; } -fn no_isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { - Some(value as u8) - } else { - None - } +pub fn no_isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize); + let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize); } -fn i8_to_u8(value: i8) -> Option { - if value >= 0 { - Some(value as u8) - } else { - None - } +pub fn i8_to_u8(value: i8) { + let _ = value >= 0; } fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index a643354e243..895a301e821 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,106 +1,76 @@ // run-rustfix +#![allow( + clippy::cast_lossless, + // Int::max_value will be deprecated in the future + deprecated, +)] #![warn(clippy::checked_conversions)] -#![allow(clippy::cast_lossless)] -#![allow(dead_code)] + use std::convert::TryFrom; // Positive tests // Signed to unsigned -fn i64_to_u32(value: i64) -> Option { - if value <= (u32::max_value() as i64) && value >= 0 { - Some(value as u32) - } else { - None - } +pub fn i64_to_u32(value: i64) { + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; } -fn i64_to_u16(value: i64) -> Option { - if value <= i64::from(u16::max_value()) && value >= 0 { - Some(value as u16) - } else { - None - } +pub fn i64_to_u16(value: i64) { + let _ = value <= i64::from(u16::max_value()) && value >= 0; + let _ = value <= i64::from(u16::MAX) && value >= 0; } -fn isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= 0 { - Some(value as u8) - } else { - None - } +pub fn isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= 0; + let _ = value <= (u8::MAX as isize) && value >= 0; } // Signed to signed -fn i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { - Some(value as i32) - } else { - None - } +pub fn i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); + let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); } -fn i64_to_i16(value: i64) -> Option { - if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { - Some(value as i16) - } else { - None - } +pub fn i64_to_i16(value: i64) { + let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); + let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); } // Unsigned to X -fn u32_to_i32(value: u32) -> Option { - if value <= i32::max_value() as u32 { - Some(value as i32) - } else { - None - } +pub fn u32_to_i32(value: u32) { + let _ = value <= i32::max_value() as u32; + let _ = value <= i32::MAX as u32; } -fn usize_to_isize(value: usize) -> isize { - if value <= isize::max_value() as usize && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn usize_to_isize(value: usize) { + let _ = value <= isize::max_value() as usize && value as i32 == 5; + let _ = value <= isize::MAX as usize && value as i32 == 5; } -fn u32_to_u16(value: u32) -> isize { - if value <= u16::max_value() as u32 && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn u32_to_u16(value: u32) { + let _ = value <= u16::max_value() as u32 && value as i32 == 5; + let _ = value <= u16::MAX as u32 && value as i32 == 5; } // Negative tests -fn no_i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= 0 { - Some(value as i32) - } else { - None - } +pub fn no_i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= 0; + let _ = value <= (i32::MAX as i64) && value >= 0; } -fn no_isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { - Some(value as u8) - } else { - None - } +pub fn no_isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize); + let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize); } -fn i8_to_u8(value: i8) -> Option { - if value >= 0 { - Some(value as u8) - } else { - None - } +pub fn i8_to_u8(value: i8) { + let _ = value >= 0; } fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index f678f009621..648ba3ccd01 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,52 +1,100 @@ error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:13:8 + --> $DIR/checked_conversions.rs:17:13 | -LL | if value <= (u32::max_value() as i64) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` +LL | let _ = value <= (u32::max_value() as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` | = note: `-D clippy::checked-conversions` implied by `-D warnings` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:21:8 + --> $DIR/checked_conversions.rs:18:13 | -LL | if value <= i64::from(u16::max_value()) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:29:8 + --> $DIR/checked_conversions.rs:22:13 | -LL | if value <= (u8::max_value() as isize) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` +LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:39:8 + --> $DIR/checked_conversions.rs:23:13 | -LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` +LL | let _ = value <= i64::from(u16::MAX) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:47:8 + --> $DIR/checked_conversions.rs:27:13 | -LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` +LL | let _ = value <= (u8::max_value() as isize) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:57:8 + --> $DIR/checked_conversions.rs:28:13 | -LL | if value <= i32::max_value() as u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` +LL | let _ = value <= (u8::MAX as isize) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:65:8 + --> $DIR/checked_conversions.rs:34:13 | -LL | if value <= isize::max_value() as usize && value as i32 == 5 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` +LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:73:8 + --> $DIR/checked_conversions.rs:35:13 | -LL | if value <= u16::max_value() as u32 && value as i32 == 5 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` +LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: aborting due to 8 previous errors +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:39:13 + | +LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:40:13 + | +LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:46:13 + | +LL | let _ = value <= i32::max_value() as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:47:13 + | +LL | let _ = value <= i32::MAX as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:51:13 + | +LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:52:13 + | +LL | let _ = value <= isize::MAX as usize && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:56:13 + | +LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:57:13 + | +LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: aborting due to 16 previous errors diff --git a/tests/ui/checked_conversions.stdout b/tests/ui/checked_conversions.stdout deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit 1.4.1-3-g733a5 From 7654125d27d95d5c329e554115b510efc1ab1e60 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 4 Jun 2020 03:34:22 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 146212cb2c7..6d7af45a472 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -270,7 +270,6 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A, B, C } /// # let x = Foo::B; - /// /// // Bad /// match x { /// Foo::A => {}, -- cgit 1.4.1-3-g733a5 From 413713c8848ec94d5f1709a41537c300da398806 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 5 Jun 2020 22:28:58 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 16b46423c8f..c40a387d297 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -36,13 +36,9 @@ declare_clippy_lint! { "common metadata is defined in `Cargo.toml`" } -fn warning(cx: &LateContext<'_, '_>, message: &str) { - span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message); -} - fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) { let message = format!("package `{}` is missing `{}` metadata", package.name, field); - warning(cx, &message); + span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); } fn is_empty_str(value: &Option) -> bool { @@ -66,12 +62,7 @@ impl LateLintPass<'_, '_> for CargoCommonMetadata { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() { - metadata - } else { - warning(cx, "could not read cargo metadata"); - return; - }; + let metadata = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false); for package in metadata.packages { if is_empty_vec(&package.authors) { diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index b6770804e18..6c42014b4c8 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; -use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId}; +use cargo_metadata::{DependencyKind, Node, Package, PackageId}; use if_chain::if_chain; use itertools::Itertools; @@ -42,13 +42,7 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { return; } - let metadata = if let Ok(metadata) = MetadataCommand::new().exec() { - metadata - } else { - span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata"); - return; - }; - + let metadata = unwrap_cargo_metadata!(cx, MULTIPLE_CRATE_VERSIONS, true); let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str(); let mut packages = metadata.packages; packages.sort_by(|a, b| a.name.cmp(&b.name)); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 7e07e7751e3..2cdb6486fcb 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1405,6 +1405,24 @@ pub fn run_lints(cx: &LateContext<'_, '_>, lints: &[&'static Lint], id: HirId) - }) } +#[macro_export] +macro_rules! unwrap_cargo_metadata { + ($cx: ident, $lint: ident, $deps: expr) => {{ + let mut command = cargo_metadata::MetadataCommand::new(); + if !$deps { + command.no_deps(); + } + + match command.exec() { + Ok(metadata) => metadata, + Err(err) => { + span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err)); + return; + }, + } + }}; +} + #[cfg(test)] mod test { use super::{trim_multiline, without_block_comments}; diff --git a/clippy_lints/src/wildcard_dependencies.rs b/clippy_lints/src/wildcard_dependencies.rs index d8d48eb1535..511518082be 100644 --- a/clippy_lints/src/wildcard_dependencies.rs +++ b/clippy_lints/src/wildcard_dependencies.rs @@ -34,12 +34,7 @@ impl LateLintPass<'_, '_> for WildcardDependencies { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() { - metadata - } else { - span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata"); - return; - }; + let metadata = unwrap_cargo_metadata!(cx, WILDCARD_DEPENDENCIES, false); for dep in &metadata.packages[0].dependencies { // VersionReq::any() does not work -- cgit 1.4.1-3-g733a5 From 623faac84ec56fa545163ab81d7e3b759a392353 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 6 Jun 2020 11:50:59 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 66722786eab..b6d50bdfa14 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,9 +1,9 @@ -use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span_lint_and_note, span_lint_and_then}; +use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; use crate::utils::{SpanlessEq, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; use std::collections::hash_map::Entry; @@ -242,15 +242,11 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) { /// Implementation of `MATCH_SAME_ARMS`. fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) { - fn same_bindings<'tcx>( - cx: &LateContext<'_, 'tcx>, - lhs: &FxHashMap>, - rhs: &FxHashMap>, - ) -> bool { + fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { lhs.len() == rhs.len() && lhs .iter() - .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty))) + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) } if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { @@ -269,7 +265,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) { (min_index..=max_index).all(|index| arms[index].guard.is_none()) && SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && // all patterns should have the same bindings - same_bindings(cx, &bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) + same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) }; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index dbe41823a9c..57c62d73964 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -8,7 +8,7 @@ use crate::utils::{ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg}; +use crate::utils::{is_type_diagnostic_item, qpath_res, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -24,7 +24,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::region; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -1256,7 +1256,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { let receiver_ty = cx.tables.expr_ty(&args[0]); let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]); - if same_tys(cx, receiver_ty, receiver_ty_adjusted) { + if TyS::same_type(receiver_ty, receiver_ty_adjusted) { let mut applicability = Applicability::MachineApplicable; let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); span_lint_and_sugg( @@ -1277,7 +1277,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e mutbl: Mutability::Not, }, ); - if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) { + if TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) { lint_iter_method(cx, args, arg, method_name) } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a9c0ff24fa6..78d69690c2d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -18,7 +18,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -29,9 +29,9 @@ use crate::utils::{ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, + remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, + walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1548,7 +1548,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { let contains_self_ty = |ty: Ty<'tcx>| { ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => same_tys(cx, self_ty, inner_ty), + GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }) @@ -1575,7 +1575,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { } } - if name == "new" && !same_tys(cx, ret_ty, self_ty) { + if name == "new" && !TyS::same_type(ret_ty, self_ty) { span_lint( cx, NEW_RET_NO_SELF, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index e556e5d59c1..dd236535c18 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,13 +1,13 @@ use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then}; +use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirIdSet; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -93,7 +93,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); let self_ty = cx.tcx.type_of(self_def_id); if_chain! { - if same_tys(cx, self_ty, return_ty(cx, id)); + if TyS::same_type(self_ty, return_ty(cx, id)); if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); then { if self.impling_types.is_none() { diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5ca30d598eb..bc5fe44b30f 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -17,7 +17,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TypeckTables}; +use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckTables}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; @@ -31,7 +31,7 @@ use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, same_tys, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, + qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; @@ -2556,7 +2556,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; then { - if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) { + if !TyS::same_type(self.target.ty(), self.body.expr_ty(e)) { return; } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 7fa97b24699..141035a980a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,12 +1,12 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, + is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -65,7 +65,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.tables.expr_ty(e); let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { + if TyS::same_type(a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); span_lint_and_sugg( cx, @@ -81,7 +81,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { let a = cx.tables.expr_ty(e); let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { + if TyS::same_type(a, b) { let sugg = snippet(cx, args[0].span, "").into_owned(); span_lint_and_sugg( cx, @@ -101,7 +101,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; if let Some(a_type) = substs.types().next(); - if same_tys(cx, a_type, b); + if TyS::same_type(a_type, b); then { span_lint_and_help( @@ -131,7 +131,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; if let Some(a_type) = substs.types().next(); - if same_tys(cx, a_type, b); + if TyS::same_type(a_type, b); then { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); @@ -148,7 +148,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::FROM_FROM); - if same_tys(cx, a, b); + if TyS::same_type(a, b); then { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2cdb6486fcb..9a6750c51ab 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -40,7 +40,7 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -879,20 +879,6 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> T cx.tcx.erase_late_bound_regions(&ret_ty) } -/// Checks if two types are the same. -/// -/// This discards any lifetime annotations, too. -// -// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` == -// `for <'b> Foo<'b>`, but not for type parameters). -pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a)); - let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b)); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok()) -} - /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind { -- cgit 1.4.1-3-g733a5 From dc13016ac2df1ce2663660389409b15eb2cf7e40 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 3 Jun 2020 01:13:57 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs new file mode 100644 index 00000000000..8b877f696af --- /dev/null +++ b/clippy_lints/src/let_and_return.rs @@ -0,0 +1,82 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Block, ExprKind, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_lint_pass!(LetReturn => [LET_AND_RETURN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if_chain! { + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + then { + span_lint_and_then( + cx, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(snippet) = snippet_opt(cx, initexpr.span) { + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f8923b2660..6bc9e23bac5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -239,6 +239,7 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; +mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -596,6 +597,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, + &let_and_return::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -772,7 +774,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::REGEX_MACRO, ®ex::TRIVIAL_REGEX, - &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, @@ -1022,6 +1023,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box formatting::Formatting); store.register_early_pass(|| box misc_early::MiscEarlyLints); store.register_early_pass(|| box returns::Return); + store.register_late_pass(|| box let_and_return::LetReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1265,6 +1267,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1390,7 +1393,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::REGEX_MACRO), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), @@ -1474,6 +1476,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1526,7 +1529,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::REGEX_MACRO), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 35464f629c3..3c939744173 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for return statements at the end of a block. @@ -36,33 +36,6 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. /// @@ -90,7 +63,7 @@ enum RetReplacement { Block, } -declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]); +declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]); impl Return { // Check the final stmt or expr in a block for unnecessary return. @@ -105,7 +78,7 @@ impl Return { } } - // Check a the final expression in a block if it's a return. + // Check the final expression in a block if it's a return. fn check_final_expr( &mut self, cx: &EarlyContext<'_>, @@ -186,54 +159,6 @@ impl Return { }, } } - - // Check for "let x = EXPR; x" - fn check_let_return(cx: &EarlyContext<'_>, block: &ast::Block) { - let mut it = block.stmts.iter(); - - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = it.next_back(); - if let ast::StmtKind::Expr(ref retexpr) = retexpr.kind; - if let Some(stmt) = it.next_back(); - if let ast::StmtKind::Local(ref local) = stmt.kind; - // don't lint in the presence of type inference - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(ref initexpr) = local.init; - if let ast::PatKind::Ident(_, ident, _) = local.pat.kind; - if let ast::ExprKind::Path(_, ref path) = retexpr.kind; - if match_path_ast(path, &[&*ident.name.as_str()]); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } } impl EarlyLintPass for Return { @@ -254,7 +179,6 @@ impl EarlyLintPass for Return { } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - Self::check_let_return(cx, block); if_chain! { if let Some(ref stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d5d07ccb2eb..bb191f9be92 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1023,7 +1023,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", deprecation: None, - module: "returns", + module: "let_and_return", }, Lint { name: "let_underscore_lock", -- cgit 1.4.1-3-g733a5 From dac8a3c1ca19d2b5934ecbe2ed79ae6c156fd885 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 7 Jun 2020 00:30:39 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs index 8b877f696af..6d3fb317bcf 100644 --- a/clippy_lints/src/let_and_return.rs +++ b/clippy_lints/src/let_and_return.rs @@ -1,8 +1,12 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Block, ExprKind, PatKind, StmtKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; @@ -49,6 +53,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { if let PatKind::Binding(.., ident, _) = local.pat.kind; if let ExprKind::Path(qpath) = &retexpr.kind; if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); if !in_external_macro(cx.sess(), initexpr.span); if !in_external_macro(cx.sess(), retexpr.span); if !in_external_macro(cx.sess(), local.span); @@ -80,3 +85,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { } } } + +fn last_statement_borrows<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + borrows: bool, +} + +impl BorrowVisitor<'_, '_> { + fn fn_def_id(&self, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => self.cx.tables.type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => self.cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } + } +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = self.fn_def_id(expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 06638e7187b..39410acea4e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -400,7 +400,7 @@ pub fn method_calls<'tcx>( /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s. /// /// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`, -/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec` +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec` /// containing the `Expr`s for /// `.bar()` and `.baz()` pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option]>> { diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 23645d48fe7..09614b8c1ad 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -67,4 +67,72 @@ macro_rules! tuple_encode { tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); +mod no_lint_if_stmt_borrows { + mod issue_3792 { + use std::io::{self, BufRead, Stdin}; + + fn read_line() -> String { + let stdin = io::stdin(); + let line = stdin.lock().lines().next().unwrap().unwrap(); + line + } + } + + mod issue_3324 { + use std::cell::RefCell; + use std::rc::{Rc, Weak}; + + fn test(value: Weak>) -> u32 { + let value = value.upgrade().unwrap(); + let ret = value.borrow().baz(); + ret + } + + struct Bar {} + + impl Bar { + fn new() -> Self { + Bar {} + } + fn baz(&self) -> u32 { + 0 + } + } + + fn main() { + let a = Rc::new(RefCell::new(Bar::new())); + let b = Rc::downgrade(&a); + test(b); + } + } + + mod free_function { + struct Inner; + + struct Foo<'a> { + inner: &'a Inner, + } + + impl Drop for Foo<'_> { + fn drop(&mut self) {} + } + + impl Foo<'_> { + fn value(&self) -> i32 { + 42 + } + } + + fn some_foo(inner: &Inner) -> Foo<'_> { + Foo { inner } + } + + fn test() -> i32 { + let x = Inner {}; + let value = some_foo(&x).value(); + value + } + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From ebfc1da07d2cd1cba87a3df79c5ffbfc0d25618c Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 7 Jun 2020 20:38:28 +0200 Subject: 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 +- tests/ui/reversed_empty_ranges_unfixable.stderr | 18 +++---- 6 files changed, 51 insertions(+), 66 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 1eb26d97ed4..45de4d29375 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -241,14 +241,26 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> { - match get_parent_expr(cx, expr) { - parent_expr @ Some(Expr { + fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { kind: ExprKind::Index(..), .. - }) => parent_expr, - _ => None, + }) + ) + } + + fn is_for_loop_arg(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let mut cur_expr = expr; + while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { + match higher::for_loop(parent_expr) { + Some((_, args, _)) if args.hir_id == expr.hir_id => return true, + _ => cur_expr = parent_expr, + } } + + false } fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { @@ -267,34 +279,18 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { - if let Some(parent_expr) = inside_indexing_expr(cx, expr) { - let (reason, outcome) = if ordering == Ordering::Equal { - ("empty", "always yield an empty slice") - } else { - ("reversed", "panic at run-time") - }; - - span_lint_and_then( - cx, - REVERSED_EMPTY_RANGES, - expr.span, - &format!("this range is {} and using it to index a slice will {}", reason, outcome), - |diag| { - if_chain! { - if ordering == Ordering::Equal; - if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind; - then { - diag.span_suggestion( - parent_expr.span, - "if you want an empty slice, use", - format!("[] as &[{}]", slice_ty), - Applicability::MaybeIncorrect - ); - } - } - } - ); - } else { + if inside_indexing_expr(cx, expr) { + // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... + if ordering != Ordering::Equal { + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is reversed and using it to index a slice will panic at run-time", + ); + } + // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` + } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { span_lint_and_then( cx, REVERSED_EMPTY_RANGES, diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index 332c0427ef6..79e482eec30 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -4,8 +4,6 @@ const ANSWER: i32 = 42; fn main() { - let arr = [1, 2, 3, 4, 5]; - // These should be linted: (21..=42).rev().for_each(|x| println!("{}", x)); @@ -14,16 +12,18 @@ fn main() { for _ in (-42..=-21).rev() {} for _ in (21u32..42u32).rev() {} - let _ = &[] as &[i32]; - // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); + let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; for _ in 21..=42 {} for _ in 21..42 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; } diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index 901ec8bcc09..b2e8bf33771 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -4,8 +4,6 @@ const ANSWER: i32 = 42; fn main() { - let arr = [1, 2, 3, 4, 5]; - // These should be linted: (42..=21).for_each(|x| println!("{}", x)); @@ -14,16 +12,18 @@ fn main() { for _ in -21..=-42 {} for _ in 42u32..21u32 {} - let _ = &arr[3..3]; - // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); + let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; for _ in 21..=42 {} for _ in 21..42 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; } diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 9a646fd9939..de83c4f3d63 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:11:5 + --> $DIR/reversed_empty_ranges_fixable.rs:9:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:12:13 + --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:14:14 + --> $DIR/reversed_empty_ranges_fixable.rs:12:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ^^^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:15:14 + --> $DIR/reversed_empty_ranges_fixable.rs:13:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ @@ -43,11 +43,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: this range is empty and using it to index a slice will always yield an empty slice - --> $DIR/reversed_empty_ranges_fixable.rs:17:18 - | -LL | let _ = &arr[3..3]; - | ----^^^^- help: if you want an empty slice, use: `[] as &[i32]` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs index 561a35625f0..264d3d1e95a 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -4,11 +4,12 @@ const ANSWER: i32 = 42; const SOME_NUM: usize = 3; fn main() { - let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[3usize..=1usize]; let _ = &arr[SOME_NUM..1]; for _ in ANSWER..ANSWER {} + + // Should not be linted, see issue #5689 + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); } diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr index 240188cbb46..f23d4eb0f9c 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -1,28 +1,22 @@ -error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 - | -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 + --> $DIR/reversed_empty_ranges_unfixable.rs:8:18 | 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: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 -- cgit 1.4.1-3-g733a5 From 7b6dc7b33dc437a59330ef3f5426102ca60fbf51 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 19 Mar 2020 14:14:52 +0100 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index a4a184480fb..adc945a6944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1683,6 +1683,7 @@ Released 2018-09-13 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f8923b2660..9809f953d67 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,6 @@ // error-pattern:cargo-clippy +#![feature(bindings_after_at)] #![feature(box_syntax)] #![feature(box_patterns)] #![feature(or_patterns)] @@ -12,6 +13,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(crate_visibility_modifier)] #![feature(concat_idents)] +#![feature(drain_filter)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) @@ -319,6 +321,7 @@ mod types; mod unicode; mod unnamed_address; mod unnecessary_sort_by; +mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; @@ -836,6 +839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, @@ -1073,6 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }); + store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1433,6 +1438,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1616,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs new file mode 100644 index 00000000000..2723af03c0b --- /dev/null +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -0,0 +1,407 @@ +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; +use crate::utils::{over, span_lint_and_then}; +use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::mut_visit::*; +use rustc_ast::ptr::P; +use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::DUMMY_SP; + +use std::cell::Cell; +use std::mem; + +declare_clippy_lint! { + /// **What it does:** + /// + /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and + /// suggests replacing the pattern with a nested one, `Some(0 | 2)`. + /// + /// Another way to think of this is that it rewrites patterns in + /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. + /// + /// **Why is this bad?** + /// + /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// if let Some(0) | Some(2) = Some(0) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// #![feature(or_patterns)] + /// + /// fn main() { + /// if let Some(0 | 2) = Some(0) {} + /// } + /// ``` + pub UNNESTED_OR_PATTERNS, + complexity, + "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" +} + +declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); + +impl EarlyLintPass for UnnestedOrPatterns { + fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { + lint_unnested_or_patterns(cx, &a.pat); + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::Let(pat, _) = &e.kind { + lint_unnested_or_patterns(cx, pat); + } + } + + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { + lint_unnested_or_patterns(cx, &p.pat); + } + + fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { + lint_unnested_or_patterns(cx, &l.pat); + } +} + +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { + if !cx.sess.opts.unstable_features.is_nightly_build() { + // User cannot do `#![feature(or_patterns)]`, so bail. + return; + } + + if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + // This is a leaf pattern, so cloning is unprofitable. + return; + } + + let mut pat = P(pat.clone()); + + // Nix all the paren patterns everywhere so that they aren't in our way. + remove_all_parens(&mut pat); + + // Transform all unnested or-patterns into nested ones, and if there were none, quit. + if !unnest_or_patterns(&mut pat) { + return; + } + + span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| { + insert_necessary_parens(&mut pat); + db.span_suggestion_verbose( + pat.span, + "nest the patterns", + pprust::pat_to_string(&pat), + Applicability::MachineApplicable, + ); + }); +} + +/// Remove all `(p)` patterns in `pat`. +fn remove_all_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + noop_visit_pat(pat, self); + let inner = match &mut pat.kind { + Paren(i) => mem::replace(&mut i.kind, Wild), + _ => return, + }; + pat.kind = inner; + } + } + Visitor.visit_pat(pat); +} + +/// Insert parens where necessary according to Rust's precedence rules for patterns. +fn insert_necessary_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + use ast::{BindingMode::*, Mutability::*}; + noop_visit_pat(pat, self); + let target = match &mut pat.kind { + // `i @ a | b`, `box a | b`, and `& mut? a | b`. + Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p, + Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)` + _ => return, + }; + target.kind = Paren(P(take_pat(target))); + } + } + Visitor.visit_pat(pat); +} + +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`. +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`. +fn unnest_or_patterns(pat: &mut P) -> bool { + struct Visitor { + changed: bool, + } + impl MutVisitor for Visitor { + fn visit_pat(&mut self, p: &mut P) { + // This is a bottom up transformation, so recurse first. + noop_visit_pat(p, self); + + // Don't have an or-pattern? Just quit early on. + let alternatives = match &mut p.kind { + Or(ps) => ps, + _ => return, + }; + + // Collapse or-patterns directly nested in or-patterns. + let mut idx = 0; + let mut this_level_changed = false; + while idx < alternatives.len() { + let inner = if let Or(ps) = &mut alternatives[idx].kind { + mem::take(ps) + } else { + idx += 1; + continue; + }; + this_level_changed = true; + alternatives.splice(idx..=idx, inner); + } + + // Focus on `p_n` and then try to transform all `p_i` where `i > n`. + let mut focus_idx = 0; + while focus_idx < alternatives.len() { + this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx); + focus_idx += 1; + } + self.changed |= this_level_changed; + + // Deal with `Some(Some(0)) | Some(Some(1))`. + if this_level_changed { + noop_visit_pat(p, self); + } + } + } + + let mut visitor = Visitor { changed: false }; + visitor.visit_pat(pat); + visitor.changed +} + +/// Match `$scrutinee` against `$pat` and extract `$then` from it. +/// Panics if there is no match. +macro_rules! always_pat { + ($scrutinee:expr, $pat:pat => $then:expr) => { + match $scrutinee { + $pat => $then, + _ => unreachable!(), + } + }; +} + +/// Focus on `focus_idx` in `alternatives`, +/// attempting to extend it with elements of the same constructor `C` +/// in `alternatives[focus_idx + 1..]`. +fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) -> bool { + // Extract the kind; we'll need to make some changes in it. + let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild); + // We'll focus on `alternatives[focus_idx]`, + // so we're draining from `alternatives[focus_idx + 1..]`. + let start = focus_idx + 1; + + // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`. + let changed = match &mut focus_kind { + // These pattern forms are "leafs" and do not have sub-patterns. + // Therefore they are not some form of constructor `C`, + // with which a pattern `C(P0)` may be formed, + // which we would want to join with other `C(Pj)`s. + Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Dealt with elsewhere. + | Or(_) | Paren(_) => false, + // Transform `box x | ... | box y` into `box (x | y)`. + // + // The cases below until `Slice(...)` deal *singleton* products. + // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`. + Box(target) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Box(_)), + |k| always_pat!(k, Box(p) => p), + ), + // Transform `&m x | ... | &m y` into `&m (x, y)`. + Ref(target, m1) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| always_pat!(k, Ref(p, _) => p), + ), + // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. + Ident(b1, i1, Some(target)) => extend_with_matching( + target, start, alternatives, + // Binding names must match. + |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), + |k| always_pat!(k, Ident(_, _, Some(p)) => p), + ), + // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. + Slice(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Slice(ps) => ps), + ), + // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post]`. + Tuple(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Tuple(ps) => ps), + ), + // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post]`. + TupleStruct(path1, ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!( + k, + TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + ), + |k| always_pat!(k, TupleStruct(_, ps) => ps), + ), + // Transform a record pattern `S { fp_0, ..., fp_n }`. + Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives), + }; + + alternatives[focus_idx].kind = focus_kind; + changed +} + +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`. +/// In particular, for a record pattern, the order in which the field patterns is irrelevant. +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. +fn extend_with_struct_pat( + path1: &ast::Path, + fps1: &mut Vec, + rest1: bool, + start: usize, + alternatives: &mut Vec>, +) -> bool { + (0..fps1.len()).any(|idx| { + let pos_in_2 = Cell::new(None); // The element `k`. + let tail_or = drain_matching( + start, + alternatives, + |k| { + matches!(k, Struct(path2, fps2, rest2) + if rest1 == *rest2 // If one struct pattern has `..` so must the other. + && eq_path(path1, path2) + && fps1.len() == fps2.len() + && fps1.iter().enumerate().all(|(idx_1, fp1)| { + if idx_1 == idx { + // In the case of `k`, we merely require identical field names + // so that we will transform into `ident_k: p1_k | p2_k`. + let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + pos_in_2.set(pos); + pos.is_some() + } else { + fps2.iter().any(|fp2| eq_field_pat(fp1, fp2)) + } + })) + }, + // Extract `p2_k`. + |k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + ); + extend_with_tail_or(&mut fps1[idx].pat, tail_or) + }) +} + +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`. +/// Here, the idea is that we fixate on some `p_k` in `C`, +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`), +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), +/// where `~` denotes semantic equality. +fn extend_with_matching_product( + targets: &mut Vec>, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind, &[P], usize) -> bool, + extract: impl Fn(PatKind) -> Vec>, +) -> bool { + (0..targets.len()).any(|idx| { + let tail_or = drain_matching( + start, + alternatives, + |k| predicate(k, targets, idx), + |k| extract(k).swap_remove(idx), + ); + extend_with_tail_or(&mut targets[idx], tail_or) + }) +} + +/// Extract the pattern from the given one and replace it with `Wild`. +/// This is meant for temporarily swapping out the pattern for manipulation. +fn take_pat(from: &mut Pat) -> Pat { + let dummy = Pat { + id: DUMMY_NODE_ID, + kind: Wild, + span: DUMMY_SP, + }; + mem::replace(from, dummy) +} + +/// Extend `target` as an or-pattern with the alternatives +/// in `tail_or` if there are any and return if there were. +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec>) -> bool { + fn extend(target: &mut Pat, mut tail_or: Vec>) { + match target { + // On an existing or-pattern in the target, append to it. + Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), + // Otherwise convert the target to an or-pattern. + target => { + let mut init_or = vec![P(take_pat(target))]; + init_or.append(&mut tail_or); + target.kind = Or(init_or); + }, + } + } + + let changed = !tail_or.is_empty(); + if changed { + // Extend the target. + extend(target, tail_or); + } + changed +} + +// Extract all inner patterns in `alternatives` matching our `predicate`. +// Only elements beginning with `start` are considered for extraction. +fn drain_matching( + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> Vec> { + let mut tail_or = vec![]; + let mut idx = 0; + for pat in alternatives.drain_filter(|p| { + // Check if we should extract, but only if `idx >= start`. + idx += 1; + idx > start && predicate(&p.kind) + }) { + tail_or.push(extract(pat.into_inner().kind)); + } + tail_or +} + +fn extend_with_matching( + target: &mut Pat, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> bool { + extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract)) +} + +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? +fn eq_pre_post(ps1: &[P], ps2: &[P], idx: usize) -> bool { + ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. + && ps1.len() == ps2.len() + && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) + && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) +} diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs new file mode 100755 index 00000000000..69a7b6c051e --- /dev/null +++ b/clippy_lints/src/utils/ast_utils.rs @@ -0,0 +1,525 @@ +//! Utilities for manipulating and extracting information from `rustc_ast::ast`. +//! +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. + +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::{both, over}; +use rustc_ast::ast::{self, *}; +use rustc_ast::ptr::P; +use std::mem; + +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. +pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { + left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) +} + +pub fn eq_id(l: Ident, r: Ident) -> bool { + l.name == r.name +} + +pub fn eq_pat(l: &Pat, r: &Pat) -> bool { + use PatKind::*; + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_pat(l, r), + (_, Paren(r)) => eq_pat(l, r), + (Wild, Wild) | (Rest, Rest) => true, + (Lit(l), Lit(r)) => eq_expr(l, r), + (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), + (Range(lf, lt, le), Range(rf, rt, re)) => { + eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) + }, + (Box(l), Box(r)) + | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) + | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), + (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)), + (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => { + lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) + }, + (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + _ => false, + } +} + +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { + match (l, r) { + (RangeEnd::Excluded, RangeEnd::Excluded) => true, + (RangeEnd::Included(l), RangeEnd::Included(r)) => { + matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq) + }, + _ => false, + } +} + +pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_pat(&l.pat, &r.pat) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { + l.position == r.position && eq_ty(&l.ty, &r.ty) +} + +pub fn eq_path(l: &Path, r: &Path) -> bool { + over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r)) +} + +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { + eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) +} + +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { + match (l, r) { + (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => { + over(&l.args, &r.args, |l, r| eq_angle_arg(l, r)) + }, + (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { + over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) + }, + _ => false, + } +} + +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { + match (l, r) { + (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), + (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r), + _ => false, + } +} + +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { + match (l, r) { + (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident), + (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r), + (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value), + _ => false, + } +} + +pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { + both(l, r, |l, r| eq_expr(l, r)) +} + +pub fn eq_expr(l: &Expr, r: &Expr) -> bool { + use ExprKind::*; + if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { + return false; + } + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_expr(l, r), + (_, Paren(r)) => eq_expr(l, r), + (Err, Err) => true, + (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), + (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), + (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), + (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (MethodCall(lc, la), MethodCall(rc, ra)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), + (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), + (Lit(l), Lit(r)) => l.kind == r.kind, + (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), + (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re), + (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), + (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), + (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { + eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) + }, + (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt), + (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), + (TryBlock(l), TryBlock(r)) => eq_block(l, r), + (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), + (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), + (Continue(ll), Continue(rl)) => eq_label(ll, rl), + (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), + (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), + (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), + (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)), + (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => { + lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb) + }, + (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), + (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), + (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { + eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + }, + _ => false, + } +} + +pub fn eq_field(l: &Field, r: &Field) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_expr(&l.expr, &r.expr) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_arm(l: &Arm, r: &Arm) -> bool { + l.is_placeholder == r.is_placeholder + && eq_pat(&l.pat, &r.pat) + && eq_expr(&l.body, &r.body) + && eq_expr_opt(&l.guard, &r.guard) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_label(l: &Option(A); +pub struct Bar { + a: Foo, + b: Foo, +} + +impl Unpin for Bar +where + Foo: Unpin, + Foo: Unpin, +{ +} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 6a1073a23f6..148c19c7d07 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] = help: consider combining the bounds: `T: Copy + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:24:5 + --> $DIR/type_repetition_in_bounds.rs:25:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 2d5930a3da7048d784489f28b44a769880b6ceff Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 3 Jul 2020 11:48:28 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 650edbb4b11..0ef70311fb1 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,5 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{GenericBound, Generics, WherePredicate}; @@ -11,6 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** Repeating the type for every bound makes the code /// less readable than combining the bounds /// + /// **Known problems:** None. + /// /// **Example:** /// ```rust /// pub fn foo(t: T) where T: Copy, T: Clone {} @@ -53,12 +56,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut map = FxHashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { - if let WherePredicate::BoundPredicate(ref p) = bound { - if p.bounds.len() as u64 > self.max_trait_bounds { - return; - } + if_chain! { + if let WherePredicate::BoundPredicate(ref p) = bound; + if p.bounds.len() as u64 <= self.max_trait_bounds; + if !in_macro(p.span); let h = hash(&p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()) { + if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()); + + then { let mut hint_string = format!( "consider combining the bounds: `{}:", snippet(cx, p.bounded_ty.span, "_") diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 9f1700567d1..766190f2099 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -37,17 +37,36 @@ where } // Generic distinction (see #4323) -pub struct Foo(A); -pub struct Bar { - a: Foo, - b: Foo, +mod issue4323 { + pub struct Foo(A); + pub struct Bar { + a: Foo, + b: Foo, + } + + impl Unpin for Bar + where + Foo: Unpin, + Foo: Unpin, + { + } } -impl Unpin for Bar -where - Foo: Unpin, - Foo: Unpin, -{ +// Extern macros shouldn't lint (see #4326) +extern crate serde; +mod issue4326 { + use serde::{Deserialize, Serialize}; + + trait Foo {} + impl Foo for String {} + + #[derive(Debug, Serialize, Deserialize)] + struct Bar + where + S: Foo, + { + foo: S, + } } fn main() {} -- cgit 1.4.1-3-g733a5 From c3c402783f1d12852982f9dc734cc4c07b9fce33 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Mon, 4 Nov 2019 19:39:03 +0100 Subject: 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 +++ tests/ui/pattern_type_mismatch/mutability.stderr | 19 ++ .../pattern_type_mismatch/pattern_alternatives.rs | 24 ++ .../pattern_alternatives.stderr | 27 ++ tests/ui/pattern_type_mismatch/pattern_structs.rs | 45 ++++ .../pattern_type_mismatch/pattern_structs.stderr | 67 +++++ tests/ui/pattern_type_mismatch/pattern_tuples.rs | 57 +++++ .../ui/pattern_type_mismatch/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b88044d6ce8..ed8f16e65bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1588,6 +1588,7 @@ Released 2018-09-13 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d31a597b66a..4890349999f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -268,6 +268,7 @@ mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; +pub mod pattern_type_mismatch; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -741,6 +742,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &panic_unimplemented::UNREACHABLE, &partialeq_ne_impl::PARTIALEQ_NE_IMPL, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, + &pattern_type_mismatch::PATTERN_TYPE_MISMATCH, &precedence::PRECEDENCE, &ptr::CMP_NULL, &ptr::MUT_FROM_REF, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); + store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1097,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), LintId::of(&panic_unimplemented::UNREACHABLE), + LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH), LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs new file mode 100644 index 00000000000..a07279c92b0 --- /dev/null +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -0,0 +1,276 @@ +use crate::utils::{last_path_segment, span_help_and_lint}; +use rustc::lint::in_external_macro; +use rustc::ty::subst::SubstsRef; +use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; +use rustc_hir::{ + intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, + QPath, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for patterns that aren't exact representations of the types + /// they are applied to. + /// + /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable + /// because it increases ownership hints in the code, and will guard against some changes + /// in ownership. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // Bad + /// let value = &Some(Box::new(23)); + /// match value { + /// Some(inner) => println!("{}", inner), + /// None => println!("none"), + /// } + /// + /// // Good + /// let value = &Some(Box::new(23)); + /// match *value { + /// Some(ref inner) => println!("{}", inner), + /// None => println!("none"), + /// } + /// ``` + pub PATTERN_TYPE_MISMATCH, + restriction, + "type of pattern does not match the expression type" +} + +declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Local(ref local) = stmt.kind { + if let Some(init) = &local.init { + if let Some(init_ty) = cx.tables.node_type_opt(init.hir_id) { + let pat = &local.pat; + if in_external_macro(cx.sess(), pat.span) { + return; + } + let deref_possible = match local.source { + LocalSource::Normal => DerefPossible::Possible, + _ => DerefPossible::Impossible, + }; + apply_lint(cx, pat, init_ty, deref_possible); + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(ref expr, arms, source) = expr.kind { + match source { + MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { + if let Some(expr_ty) = cx.tables.node_type_opt(expr.hir_id) { + 'pattern_checks: for arm in arms { + let pat = &arm.pat; + if in_external_macro(cx.sess(), pat.span) { + continue 'pattern_checks; + } + if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) { + break 'pattern_checks; + } + } + } + }, + _ => (), + } + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: intravisit::FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + hir_id: HirId, + ) { + if let Some(fn_sig) = cx.tables.liberated_fn_sigs().get(hir_id) { + for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { + apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); + } + } + } +} + +#[derive(Debug, Clone, Copy)] +enum DerefPossible { + Possible, + Impossible, +} + +fn apply_lint<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &Pat<'_>, + expr_ty: Ty<'tcx>, + deref_possible: DerefPossible, +) -> bool { + let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); + if let Some((span, mutability, level)) = maybe_mismatch { + span_help_and_lint( + cx, + PATTERN_TYPE_MISMATCH, + span, + "type of pattern does not match the expression type", + &format!( + "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", + match (deref_possible, level) { + (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", + _ => "", + }, + match mutability { + Mutability::Mut => "&mut _", + Mutability::Not => "&_", + }, + ), + ); + true + } else { + false + } +} + +#[derive(Debug, Copy, Clone)] +enum Level { + Top, + Lower, +} + +#[allow(rustc::usage_of_ty_tykind)] +fn find_first_mismatch<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &Pat<'_>, + ty: Ty<'tcx>, + level: Level, +) -> Option<(Span, Mutability, Level)> { + if let PatKind::Ref(ref sub_pat, _) = pat.kind { + if let TyKind::Ref(_, sub_ty, _) = ty.kind { + return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); + } + } + + if let TyKind::Ref(_, _, mutability) = ty.kind { + if is_non_ref_pattern(&pat.kind) { + return Some((pat.span, mutability, level)); + } + } + + if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let Some(variant) = get_variant(adt_def, qpath) { + let field_defs = &variant.fields; + return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); + } + } + } + + if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let Some(variant) = get_variant(adt_def, qpath) { + let field_defs = &variant.fields; + let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); + return find_first_mismatch_in_tuple(cx, pats, ty_iter); + } + } + } + + if let PatKind::Tuple(ref pats, _) = pat.kind { + if let TyKind::Tuple(..) = ty.kind { + return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); + } + } + + if let PatKind::Or(sub_pats) = pat.kind { + for pat in sub_pats { + let maybe_mismatch = find_first_mismatch(cx, pat, ty, level); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + } + } + + None +} + +fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> { + if adt_def.is_struct() { + if let Some(variant) = adt_def.variants.iter().next() { + return Some(variant); + } + } + + if adt_def.is_enum() { + let pat_ident = last_path_segment(qpath).ident; + for variant in &adt_def.variants { + if variant.ident == pat_ident { + return Some(variant); + } + } + } + + None +} + +fn find_first_mismatch_in_tuple<'a, 'tcx, I>( + cx: &LateContext<'a, 'tcx>, + pats: &[&Pat<'_>], + ty_iter_src: I, +) -> Option<(Span, Mutability, Level)> +where + I: IntoIterator>, +{ + let mut field_tys = ty_iter_src.into_iter(); + 'fields: for pat in pats { + let field_ty = if let Some(ty) = field_tys.next() { + ty + } else { + break 'fields; + }; + + let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + } + + None +} + +fn find_first_mismatch_in_struct<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + field_pats: &[FieldPat<'_>], + field_defs: &[FieldDef], + substs_ref: SubstsRef<'tcx>, +) -> Option<(Span, Mutability, Level)> { + for field_pat in field_pats { + 'definitions: for field_def in field_defs { + if field_pat.ident == field_def.ident { + let field_ty = field_def.ty(cx.tcx, substs_ref); + let pat = &field_pat.pat; + let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + break 'definitions; + } + } + } + + None +} + +fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool { + match pat_kind { + PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true, + PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)), + _ => false, + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a2998d74130..6402efc2521 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1697,6 +1697,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "path_buf_push_overwrite", }, + Lint { + name: "pattern_type_mismatch", + group: "restriction", + desc: "type of pattern does not match the expression type", + deprecation: None, + module: "pattern_type_mismatch", + }, Lint { name: "possible_missing_comma", group: "correctness", diff --git a/tests/ui/pattern_type_mismatch/mutability.rs b/tests/ui/pattern_type_mismatch/mutability.rs new file mode 100644 index 00000000000..9b4f2f1f579 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/mutability.rs @@ -0,0 +1,40 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn should_lint() { + let value = &Some(23); + match value { + Some(_) => (), + _ => (), + } + + let value = &mut Some(23); + match value { + Some(_) => (), + _ => (), + } +} + +fn should_not_lint() { + let value = &Some(23); + match value { + &Some(_) => (), + _ => (), + } + match *value { + Some(_) => (), + _ => (), + } + + let value = &mut Some(23); + match value { + &mut Some(_) => (), + _ => (), + } + match *value { + Some(_) => (), + _ => (), + } +} diff --git a/tests/ui/pattern_type_mismatch/mutability.stderr b/tests/ui/pattern_type_mismatch/mutability.stderr new file mode 100644 index 00000000000..3421d568365 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/mutability.stderr @@ -0,0 +1,19 @@ +error: type of pattern does not match the expression type + --> $DIR/mutability.rs:9:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/mutability.rs:15:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs new file mode 100644 index 00000000000..065ea9fb9b5 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs @@ -0,0 +1,24 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn alternatives() { + enum Value<'a> { + Unused, + A(&'a Option), + B, + } + let ref_value = &Value::A(&Some(23)); + + // not ok + if let Value::B | Value::A(_) = ref_value {} + if let &Value::B | &Value::A(Some(_)) = ref_value {} + if let Value::B | Value::A(Some(_)) = *ref_value {} + + // ok + if let &Value::B | &Value::A(_) = ref_value {} + if let Value::B | Value::A(_) = *ref_value {} + if let &Value::B | &Value::A(&Some(_)) = ref_value {} + if let Value::B | Value::A(&Some(_)) = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr new file mode 100644 index 00000000000..d285c93782c --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr @@ -0,0 +1,27 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:15:12 + | +LL | if let Value::B | Value::A(_) = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:16:34 + | +LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:17:32 + | +LL | if let Value::B | Value::A(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 3 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.rs b/tests/ui/pattern_type_mismatch/pattern_structs.rs new file mode 100644 index 00000000000..417b1c107c5 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_structs.rs @@ -0,0 +1,45 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn struct_types() { + struct Struct<'a> { + ref_inner: &'a Option, + } + let ref_value = &Struct { ref_inner: &Some(42) }; + + // not ok + let Struct { .. } = ref_value; + if let &Struct { ref_inner: Some(_) } = ref_value {} + if let Struct { ref_inner: Some(_) } = *ref_value {} + + // ok + let &Struct { .. } = ref_value; + let Struct { .. } = *ref_value; + if let &Struct { ref_inner: &Some(_) } = ref_value {} + if let Struct { ref_inner: &Some(_) } = *ref_value {} +} + +fn struct_enum_variants() { + enum StructEnum<'a> { + Empty, + Var { inner_ref: &'a Option }, + } + let ref_value = &StructEnum::Var { inner_ref: &Some(42) }; + + // not ok + if let StructEnum::Var { .. } = ref_value {} + if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} + if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} + if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} + if let StructEnum::Empty = ref_value {} + + // ok + if let &StructEnum::Var { .. } = ref_value {} + if let StructEnum::Var { .. } = *ref_value {} + if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {} + if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {} + if let &StructEnum::Empty = ref_value {} + if let StructEnum::Empty = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/tests/ui/pattern_type_mismatch/pattern_structs.stderr new file mode 100644 index 00000000000..d428e85b0c9 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_structs.stderr @@ -0,0 +1,67 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:13:9 + | +LL | let Struct { .. } = ref_value; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:14:33 + | +LL | if let &Struct { ref_inner: Some(_) } = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:15:32 + | +LL | if let Struct { ref_inner: Some(_) } = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:32:12 + | +LL | if let StructEnum::Var { .. } = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:33:12 + | +LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:34:42 + | +LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:35:41 + | +LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:36:12 + | +LL | if let StructEnum::Empty = ref_value {} + | ^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 8 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/tests/ui/pattern_type_mismatch/pattern_tuples.rs new file mode 100644 index 00000000000..19504a051d8 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_tuples.rs @@ -0,0 +1,57 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn tuple_types() { + struct TupleStruct<'a>(&'a Option); + let ref_value = &TupleStruct(&Some(42)); + + // not ok + let TupleStruct(_) = ref_value; + if let &TupleStruct(Some(_)) = ref_value {} + if let TupleStruct(Some(_)) = *ref_value {} + + // ok + let &TupleStruct(_) = ref_value; + let TupleStruct(_) = *ref_value; + if let &TupleStruct(&Some(_)) = ref_value {} + if let TupleStruct(&Some(_)) = *ref_value {} +} + +fn tuple_enum_variants() { + enum TupleEnum<'a> { + Empty, + Var(&'a Option), + } + let ref_value = &TupleEnum::Var(&Some(42)); + + // not ok + if let TupleEnum::Var(_) = ref_value {} + if let &TupleEnum::Var(Some(_)) = ref_value {} + if let TupleEnum::Var(Some(_)) = *ref_value {} + if let TupleEnum::Empty = ref_value {} + + // ok + if let &TupleEnum::Var(_) = ref_value {} + if let TupleEnum::Var(_) = *ref_value {} + if let &TupleEnum::Var(&Some(_)) = ref_value {} + if let TupleEnum::Var(&Some(_)) = *ref_value {} + if let &TupleEnum::Empty = ref_value {} + if let TupleEnum::Empty = *ref_value {} +} + +fn plain_tuples() { + let ref_value = &(&Some(23), &Some(42)); + + // not ok + let (_a, _b) = ref_value; + if let &(_a, Some(_)) = ref_value {} + if let (_a, Some(_)) = *ref_value {} + + // ok + let &(_a, _b) = ref_value; + let (_a, _b) = *ref_value; + if let &(_a, &Some(_)) = ref_value {} + if let (_a, &Some(_)) = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr new file mode 100644 index 00000000000..edd0074d00d --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr @@ -0,0 +1,83 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:11:9 + | +LL | let TupleStruct(_) = ref_value; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:12:25 + | +LL | if let &TupleStruct(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:13:24 + | +LL | if let TupleStruct(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:30:12 + | +LL | if let TupleEnum::Var(_) = ref_value {} + | ^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:31:28 + | +LL | if let &TupleEnum::Var(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:32:27 + | +LL | if let TupleEnum::Var(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:33:12 + | +LL | if let TupleEnum::Empty = ref_value {} + | ^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:48:9 + | +LL | let (_a, _b) = ref_value; + | ^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:49:18 + | +LL | if let &(_a, Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:50:17 + | +LL | if let (_a, Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 10 previous errors + diff --git a/tests/ui/pattern_type_mismatch/syntax.rs b/tests/ui/pattern_type_mismatch/syntax.rs new file mode 100644 index 00000000000..e89917c41e8 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/syntax.rs @@ -0,0 +1,146 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn syntax_match() { + let ref_value = &Some(&Some(42)); + + // not ok + match ref_value { + Some(_) => (), + None => (), + } + + // ok + match ref_value { + &Some(_) => (), + &None => (), + } + match *ref_value { + Some(_) => (), + None => (), + } +} + +fn syntax_if_let() { + let ref_value = &Some(42); + + // not ok + if let Some(_) = ref_value {} + + // ok + if let &Some(_) = ref_value {} + if let Some(_) = *ref_value {} +} + +fn syntax_while_let() { + let ref_value = &Some(42); + + // not ok + while let Some(_) = ref_value { + break; + } + + // ok + while let &Some(_) = ref_value { + break; + } + while let Some(_) = *ref_value { + break; + } +} + +fn syntax_for() { + let ref_value = &Some(23); + let slice = &[(2, 3), (4, 2)]; + + // not ok + for (_a, _b) in slice.iter() {} + + // ok + for &(_a, _b) in slice.iter() {} +} + +fn syntax_let() { + let ref_value = &(2, 3); + + // not ok + let (_n, _m) = ref_value; + + // ok + let &(_n, _m) = ref_value; + let (_n, _m) = *ref_value; +} + +fn syntax_fn() { + // not ok + fn foo((_a, _b): &(i32, i32)) {} + + // ok + fn foo_ok_1(&(_a, _b): &(i32, i32)) {} +} + +fn syntax_closure() { + fn foo(f: F) + where + F: FnOnce(&(i32, i32)), + { + } + + // not ok + foo(|(_a, _b)| ()); + + // ok + foo(|&(_a, _b)| ()); +} + +fn macro_with_expression() { + macro_rules! matching_macro { + ($e:expr) => { + $e + }; + } + let value = &Some(23); + + // not ok + matching_macro!(match value { + Some(_) => (), + _ => (), + }); + + // ok + matching_macro!(match value { + &Some(_) => (), + _ => (), + }); + matching_macro!(match *value { + Some(_) => (), + _ => (), + }); +} + +fn macro_expansion() { + macro_rules! matching_macro { + ($e:expr) => { + // not ok + match $e { + Some(_) => (), + _ => (), + } + + // ok + match $e { + &Some(_) => (), + _ => (), + } + match *$e { + Some(_) => (), + _ => (), + } + }; + } + + let value = &Some(23); + matching_macro!(value); +} diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr new file mode 100644 index 00000000000..110e8421a5e --- /dev/null +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -0,0 +1,78 @@ +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:11:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:30:12 + | +LL | if let Some(_) = ref_value {} + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:41:15 + | +LL | while let Some(_) = ref_value { + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:59:9 + | +LL | for (_a, _b) in slice.iter() {} + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:69:9 + | +LL | let (_n, _m) = ref_value; + | ^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:78:12 + | +LL | fn foo((_a, _b): &(i32, i32)) {} + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:92:10 + | +LL | foo(|(_a, _b)| ()); + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:108:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:128:17 + | +LL | Some(_) => (), + | ^^^^^^^ +... +LL | matching_macro!(value); + | ----------------------- in this macro invocation + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 9 previous errors + -- cgit 1.4.1-3-g733a5 From 55877d7b4a6a2c713412db7b30ef79b2a252956e Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Sun, 9 Feb 2020 17:57:35 +0100 Subject: span_help_and_lint -> span_lint_and_help --- clippy_lints/src/pattern_type_mismatch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a07279c92b0..d3f65db1916 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,4 +1,4 @@ -use crate::utils::{last_path_segment, span_help_and_lint}; +use crate::utils::{last_path_segment, span_lint_and_help}; use rustc::lint::in_external_macro; use rustc::ty::subst::SubstsRef; use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; @@ -115,7 +115,7 @@ fn apply_lint<'a, 'tcx>( ) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); if let Some((span, mutability, level)) = maybe_mismatch { - span_help_and_lint( + span_lint_and_help( cx, PATTERN_TYPE_MISMATCH, span, -- cgit 1.4.1-3-g733a5 From 6447507ab150ebd1787ca6af385db49dfdf45978 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 8 Jun 2020 15:10:57 +0200 Subject: Fix rebase fallout --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4890349999f..3d8ce10aef3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -268,7 +268,7 @@ mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; -pub mod pattern_type_mismatch; +mod pattern_type_mismatch; mod precedence; mod ptr; mod ptr_offset_with_cast; diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index d3f65db1916..a6079a8b5bc 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,12 +1,12 @@ use crate::utils::{last_path_segment, span_lint_and_help}; -use rustc::lint::in_external_macro; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; use rustc_hir::{ intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -120,6 +120,7 @@ fn apply_lint<'a, 'tcx>( PATTERN_TYPE_MISMATCH, span, "type of pattern does not match the expression type", + None, &format!( "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", match (deref_possible, level) { -- cgit 1.4.1-3-g733a5 From 92ecc53691f3edd67526dca423a2b4083d12a221 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Wed, 1 Jul 2020 15:49:06 +0200 Subject: Catching up with rustc changes --- clippy_lints/src/pattern_type_mismatch.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a6079a8b5bc..fcc9b16068f 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Local(ref local) = stmt.kind { if let Some(init) = &local.init { - if let Some(init_ty) = cx.tables.node_type_opt(init.hir_id) { + if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) { let pat = &local.pat; if in_external_macro(cx.sess(), pat.span) { return; @@ -67,7 +67,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { if let ExprKind::Match(ref expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { - if let Some(expr_ty) = cx.tables.node_type_opt(expr.hir_id) { + if let Some(expr_ty) = cx.tables().node_type_opt(expr.hir_id) { 'pattern_checks: for arm in arms { let pat = &arm.pat; if in_external_macro(cx.sess(), pat.span) { @@ -93,7 +93,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { _: Span, hir_id: HirId, ) { - if let Some(fn_sig) = cx.tables.liberated_fn_sigs().get(hir_id) { + if let Some(fn_sig) = cx.tables().liberated_fn_sigs().get(hir_id) { for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); } -- cgit 1.4.1-3-g733a5 From d617551a6a3830a5324898f2046b97aad8c6067a Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Wed, 1 Jul 2020 15:49:46 +0200 Subject: Expanded lint documentation --- clippy_lints/src/pattern_type_mismatch.rs | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index fcc9b16068f..9fa5860ba30 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -14,6 +14,22 @@ declare_clippy_lint! { /// **What it does:** Checks for patterns that aren't exact representations of the types /// they are applied to. /// + /// To satisfy this lint, you will have to adjust either the expression that is matched + /// against or the pattern itself, as well as the bindings that are introduced by the + /// adjusted patterns. For matching you will have to either dereference the expression + /// with the `*` operator, or amend the patterns to explicitly match against `&` + /// or `&mut ` depending on the reference mutability. For the bindings you need + /// to use the inverse. You can leave them as plain bindings if you wish for the value + /// to be copied, but you must use `ref mut ` or `ref ` to construct + /// a reference into the matched structure. + /// + /// If you are looking for a way to learn about ownership semantics in more detail, it + /// is recommended to look at IDE options available to you to highlight types, lifetimes + /// and reference semantics in your code. The available tooling would expose these things + /// in a general way even outside of the various pattern matching mechanics. Of course + /// this lint can still be used to highlight areas of interest and ensure a good understanding + /// of ownership semantics. + /// /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable /// because it increases ownership hints in the code, and will guard against some changes /// in ownership. @@ -22,6 +38,10 @@ declare_clippy_lint! { /// /// **Example:** /// + /// This example shows the basic adjustments necessary to satisfy the lint. Note how + /// the matched expression is explicitly dereferenced with `*` and the `inner` variable + /// is bound to a shared borrow via `ref inner`. + /// /// ```rust,ignore /// // Bad /// let value = &Some(Box::new(23)); @@ -37,6 +57,25 @@ declare_clippy_lint! { /// None => println!("none"), /// } /// ``` + /// + /// The following example demonstrates one of the advantages of the more verbose style. + /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable + /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot + /// accidentally modify the wrong part of the structure. + /// + /// ```rust,ignore + /// // Bad + /// let mut values = vec![(2, 3), (3, 4)]; + /// for (a, b) in &mut values { + /// *a += *b; + /// } + /// + /// // Good + /// let mut values = vec![(2, 3), (3, 4)]; + /// for &mut (ref mut a, b) in &mut values { + /// *a += b; + /// } + /// ``` pub PATTERN_TYPE_MISMATCH, restriction, "type of pattern does not match the expression type" -- cgit 1.4.1-3-g733a5 From aa4bee228f23b3e1a1d91ed3a4606af3c6b60895 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Fri, 3 Jul 2020 18:20:19 +0200 Subject: LateContext has only one lifetime parameter now --- clippy_lints/src/pattern_type_mismatch.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 9fa5860ba30..8587a79e821 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -83,8 +83,8 @@ declare_clippy_lint! { declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { - fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { +impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Local(ref local) = stmt.kind { if let Some(init) = &local.init { if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) { @@ -102,7 +102,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { } } - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(ref expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { @@ -125,7 +125,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { fn check_fn( &mut self, - cx: &LateContext<'a, 'tcx>, + cx: &LateContext<'tcx>, _: intravisit::FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, @@ -146,8 +146,8 @@ enum DerefPossible { Impossible, } -fn apply_lint<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn apply_lint<'tcx>( + cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible, @@ -185,8 +185,8 @@ enum Level { } #[allow(rustc::usage_of_ty_tykind)] -fn find_first_mismatch<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn find_first_mismatch<'tcx>( + cx: &LateContext<'tcx>, pat: &Pat<'_>, ty: Ty<'tcx>, level: Level, @@ -259,8 +259,8 @@ fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a Variant None } -fn find_first_mismatch_in_tuple<'a, 'tcx, I>( - cx: &LateContext<'a, 'tcx>, +fn find_first_mismatch_in_tuple<'tcx, I>( + cx: &LateContext<'tcx>, pats: &[&Pat<'_>], ty_iter_src: I, ) -> Option<(Span, Mutability, Level)> @@ -284,8 +284,8 @@ where None } -fn find_first_mismatch_in_struct<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn find_first_mismatch_in_struct<'tcx>( + cx: &LateContext<'tcx>, field_pats: &[FieldPat<'_>], field_defs: &[FieldDef], substs_ref: SubstsRef<'tcx>, -- cgit 1.4.1-3-g733a5 From c0fd452840c7cc53b3396268f62ed2a0a2e8fef7 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Fri, 3 Jul 2020 18:23:36 +0200 Subject: fmt fix --- clippy_lints/src/pattern_type_mismatch.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 8587a79e821..a49dc87c0b4 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -146,12 +146,7 @@ enum DerefPossible { Impossible, } -fn apply_lint<'tcx>( - cx: &LateContext<'tcx>, - pat: &Pat<'_>, - expr_ty: Ty<'tcx>, - deref_possible: DerefPossible, -) -> bool { +fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); if let Some((span, mutability, level)) = maybe_mismatch { span_lint_and_help( -- cgit 1.4.1-3-g733a5 From bf48a2d50d82cccac58d7c4c73700eaf66926aee Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 5 Mar 2020 10:35:05 -0800 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index ed8f16e65bb..1a081bb85fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1577,6 +1577,7 @@ Released 2018-09-13 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d5be893ffb..cd91e7ceb32 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -264,6 +264,7 @@ mod non_copy_const; mod non_expressive_names; mod open_options; mod option_env_unwrap; +mod option_if_let_else; mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; @@ -734,6 +735,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &non_expressive_names::SIMILAR_NAMES, &open_options::NONSENSICAL_OPEN_OPTIONS, &option_env_unwrap::OPTION_ENV_UNWRAP, + &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, @@ -1052,6 +1054,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); @@ -1369,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), @@ -1517,6 +1521,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs new file mode 100644 index 00000000000..f092f1297c1 --- /dev/null +++ b/clippy_lints/src/option_if_let_else.rs @@ -0,0 +1,202 @@ +use crate::utils::sugg::Sugg; +use crate::utils::{match_type, paths, span_lint_and_sugg}; +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::marker::PhantomData; + +declare_clippy_lint! { + /// **What it does:** + /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// idiomatically done with `Option::map_or` (if the else bit is a simple + /// expression) or `Option::map_or_else` (if the else bit is a longer + /// block). + /// + /// **Why is this bad?** + /// Using the dedicated functions of the Option type is clearer and + /// more concise than an if let expression. + /// + /// **Known problems:** + /// This lint uses whether the block is just an expression or if it has + /// more statements to decide whether to use `Option::map_or` or + /// `Option::map_or_else`. If you have a single expression which calls + /// an expensive function, then it would be more efficient to use + /// `Option::map_or_else`, but this lint would suggest `Option::map_or`. + /// + /// Also, this lint uses a deliberately conservative metric for checking + /// if the inside of either body contains breaks or continues which will + /// cause it to not suggest a fix if either block contains a loop with + /// continues or breaks contained within the loop. + /// + /// **Example:** + /// + /// ```rust + /// # let optional: Option = Some(0); + /// let _ = if let Some(foo) = optional { + /// foo + /// } else { + /// 5 + /// }; + /// let _ = if let Some(foo) = optional { + /// foo + /// } else { + /// let y = do_complicated_function(); + /// y*y + /// }; + /// ``` + /// + /// should be + /// + /// ```rust + /// # let optional: Option = Some(0); + /// let _ = optional.map_or(5, |foo| foo); + /// let _ = optional.map_or_else(||{ + /// let y = do_complicated_function; + /// y*y + /// }, |foo| foo); + /// ``` + pub OPTION_IF_LET_ELSE, + style, + "reimplementation of Option::map_or" +} + +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); + +/// Returns true iff the given expression is the result of calling Result::ok +fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind; + if path.ident.name.to_ident_string() == "ok"; + if match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT); + then { + true + } else { + false + } + } +} + +/// A struct containing information about occurences of the +/// `if let Some(..) = .. else` construct that this lint detects. +struct OptionIfLetElseOccurence { + option: String, + method_sugg: String, + some_expr: String, + none_expr: String, +} + +struct ReturnBreakContinueVisitor<'tcx> { + seen_return_break_continue: bool, + phantom_data: PhantomData<&'tcx bool>, +} +impl<'tcx> ReturnBreakContinueVisitor<'tcx> { + fn new() -> ReturnBreakContinueVisitor<'tcx> { + ReturnBreakContinueVisitor { + seen_return_break_continue: false, + phantom_data: PhantomData, + } + } +} +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + rustc_hir::intravisit::walk_expr(self, ex); + }, + } + } +} + +fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { + let mut recursive_visitor: ReturnBreakContinueVisitor<'tcx> = ReturnBreakContinueVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} + +/// If this expression is the option if let/else construct we're detecting, then +/// this function returns an OptionIfLetElseOccurence struct with details if +/// this construct is found, or None if this construct is not found. +fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { + //(String, String, String, String)> { + if_chain! { + if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if arms.len() == 2; + if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); + if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already + if let PatKind::TupleStruct(_, &[inner_pat], _) = &arms[0].pat.kind; + if let PatKind::Binding(_, _, id, _) = &inner_pat.kind; + if !contains_return_break_continue(arms[0].body); + if !contains_return_break_continue(arms[1].body); + then { + let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) + = &arms[0].body.kind { + if let &[] = &statements { + expr + } else { + &arms[0].body + } + } else { + return None; + }; + let (none_body, method_sugg) = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) + = &arms[1].body.kind { + if let &[] = &statements { + (expr, "map_or") + } else { + (&arms[1].body, "map_or_else") + } + } else { + return None; + }; + let capture_name = id.name.to_ident_string(); + Some(OptionIfLetElseOccurence { + option: format!("{}", Sugg::hir(cx, let_body, "..")), + method_sugg: format!("{}", method_sugg), + some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), + none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")) + }) + } else { + None + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let Some(detection) = detect_option_if_let_else(cx, expr) { + span_lint_and_sugg( + cx, + OPTION_IF_LET_ELSE, + expr.span, + format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), + "try", + format!( + "{}.{}({}, {})", + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6402efc2521..b499d565fa7 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1620,6 +1620,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "option_env_unwrap", }, + Lint { + name: "option_if_let_else", + group: "style", + desc: "reimplementation of Option::map_or", + deprecation: None, + module: "option_if_let_else", + }, Lint { name: "option_map_or_none", group: "style", diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed new file mode 100644 index 00000000000..3aa895120d1 --- /dev/null +++ b/tests/ui/option_if_let_else.fixed @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::option_if_let_else)] + +fn bad1(string: Option<&str>) -> (bool, &str) { + string.map_or((false, "hello"), |x| (true, x)) +} + +fn longer_body(arg: Option) -> u32 { + arg.map_or(13, |x| { + let y = x * x; + y * y + }) +} + +fn test_map_or_else(arg: Option) { + let _ = arg.map_or_else(|| { + let mut y = 1; + y = (y + 2 / y) / 2; + y = (y + 2 / y) / 2; + y + }, |x| x * x * x * x); +} + +fn negative_tests(arg: Option) -> u32 { + let _ = if let Some(13) = arg { "unlucky" } else { "lucky" }; + for _ in 0..10 { + let _ = if let Some(x) = arg { + x + } else { + continue; + }; + } + let _ = if let Some(x) = arg { + return x; + } else { + 5 + }; + 7 +} + +fn main() { + let optional = Some(5); + let _ = optional.map_or(5, |x| x + 2); + let _ = bad1(None); + let _ = longer_body(None); + test_map_or_else(None); + let _ = negative_tests(None); +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs new file mode 100644 index 00000000000..7d029b0bcf4 --- /dev/null +++ b/tests/ui/option_if_let_else.rs @@ -0,0 +1,56 @@ +// run-rustfix +#![warn(clippy::option_if_let_else)] + +fn bad1(string: Option<&str>) -> (bool, &str) { + if let Some(x) = string { + (true, x) + } else { + (false, "hello") + } +} + +fn longer_body(arg: Option) -> u32 { + if let Some(x) = arg { + let y = x * x; + y * y + } else { + 13 + } +} + +fn test_map_or_else(arg: Option) { + let _ = if let Some(x) = arg { + x * x * x * x + } else { + let mut y = 1; + y = (y + 2 / y) / 2; + y = (y + 2 / y) / 2; + y + }; +} + +fn negative_tests(arg: Option) -> u32 { + let _ = if let Some(13) = arg { "unlucky" } else { "lucky" }; + for _ in 0..10 { + let _ = if let Some(x) = arg { + x + } else { + continue; + }; + } + let _ = if let Some(x) = arg { + return x; + } else { + 5 + }; + 7 +} + +fn main() { + let optional = Some(5); + let _ = if let Some(x) = optional { x + 2 } else { 5 }; + let _ = bad1(None); + let _ = longer_body(None); + test_map_or_else(None); + let _ = negative_tests(None); +} diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr new file mode 100644 index 00000000000..d6cf0836733 --- /dev/null +++ b/tests/ui/option_if_let_else.stderr @@ -0,0 +1,62 @@ +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:5:5 + | +LL | / if let Some(x) = string { +LL | | (true, x) +LL | | } else { +LL | | (false, "hello") +LL | | } + | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))` + | + = note: `-D clippy::option-if-let-else` implied by `-D warnings` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:13:5 + | +LL | / if let Some(x) = arg { +LL | | let y = x * x; +LL | | y * y +LL | | } else { +LL | | 13 +LL | | } + | |_____^ + | +help: try + | +LL | arg.map_or(13, |x| { +LL | let y = x * x; +LL | y * y +LL | }) + | + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:22:13 + | +LL | let _ = if let Some(x) = arg { + | _____________^ +LL | | x * x * x * x +LL | | } else { +LL | | let mut y = 1; +... | +LL | | y +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = arg.map_or_else(|| { +LL | let mut y = 1; +LL | y = (y + 2 / y) / 2; +LL | y = (y + 2 / y) / 2; +LL | y +LL | }, |x| x * x * x * x); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:51:13 + | +LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 82f8d4d6f1645dd08b107c3ead9155412637739b Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 25 Apr 2020 08:32:33 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index f092f1297c1..1edec1cad6e 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,3 +1,4 @@ +use crate::utils; use crate::utils::sugg::Sugg; use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; @@ -89,6 +90,7 @@ struct OptionIfLetElseOccurence { method_sugg: String, some_expr: String, none_expr: String, + wrap_braces: bool, } struct ReturnBreakContinueVisitor<'tcx> { @@ -140,6 +142,7 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { //(String, String, String, String)> { if_chain! { + // if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); @@ -170,11 +173,23 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - return None; }; let capture_name = id.name.to_ident_string(); + let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + if_chain! { + if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr; + if expr.hir_id == arms[1].body.hir_id; + then { + true + } else { + false + } + } + }); Some(OptionIfLetElseOccurence { option: format!("{}", Sugg::hir(cx, let_body, "..")), method_sugg: format!("{}", method_sugg), some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), - none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")) + none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), + wrap_braces, }) } else { None @@ -192,8 +207,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), "try", format!( - "{}.{}({}, {})", - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr + "{}{}.{}({}, {}){}", + if detection.wrap_braces { "{ " } else { "" }, + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + if detection.wrap_braces { " }" } else { "" }, ), Applicability::MachineApplicable, ); diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 3aa895120d1..343e099b2b7 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,6 +5,12 @@ fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) } +fn bad2(string: Option<&str>) -> Option<(bool, &str)> { + if string.is_none() { + None + } else { string.map_or(Some((false, "")), |x| Some((true, x))) } +} + fn longer_body(arg: Option) -> u32 { arg.map_or(13, |x| { let y = x * x; @@ -42,6 +48,7 @@ fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); + let _ = bad2(None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7d029b0bcf4..b0c203f0637 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -9,6 +9,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) { } } +fn bad2(string: Option<&str>) -> Option<(bool, &str)> { + if string.is_none() { + None + } else if let Some(x) = string { + Some((true, x)) + } else { + Some((false, "")) + } +} + fn longer_body(arg: Option) -> u32 { if let Some(x) = arg { let y = x * x; @@ -50,6 +60,7 @@ fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); + let _ = bad2(None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index d6cf0836733..656cfb2f62a 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -11,7 +11,18 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:13:5 + --> $DIR/option_if_let_else.rs:15:12 + | +LL | } else if let Some(x) = string { + | ____________^ +LL | | Some((true, x)) +LL | | } else { +LL | | Some((false, "")) +LL | | } + | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:23:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -30,7 +41,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:22:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -53,10 +64,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:51:13 + --> $DIR/option_if_let_else.rs:61:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From b85796fe3613e20a4af21933783a3d993bb8d7ad Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 25 Apr 2020 09:08:23 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 1edec1cad6e..66971ee0262 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -175,7 +175,12 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - let capture_name = id.name.to_ident_string(); let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if_chain! { - if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr; + if let Some(Expr { kind: ExprKind::Match( + _, + arms, + MatchSource::IfDesugar{contains_else_clause: true} + | MatchSource::IfLetDesugar{contains_else_clause: true}), + .. } ) = parent.expr; if expr.hir_id == arms[1].body.hir_id; then { true @@ -184,8 +189,19 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } }); + let parens_around_option = match &let_body.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Block(..) + | ExprKind::Field(..) + | ExprKind::Path(_) + => false, + _ => true, + }; Some(OptionIfLetElseOccurence { - option: format!("{}", Sugg::hir(cx, let_body, "..")), + option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }), method_sugg: format!("{}", method_sugg), some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), @@ -209,7 +225,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { format!( "{}{}.{}({}, {}){}", if detection.wrap_braces { "{ " } else { "" }, - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + detection.option, + detection.method_sugg, + detection.none_expr, + detection.some_expr, if detection.wrap_braces { " }" } else { "" }, ), Applicability::MachineApplicable, diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 343e099b2b7..80b162714ac 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,12 +5,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) } -fn bad2(string: Option<&str>) -> Option<(bool, &str)> { +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None } else { string.map_or(Some((false, "")), |x| Some((true, x))) } } +fn unop_bad(string: &Option<&str>) -> usize { + (*string).map_or(0, |s| s.len()) +} + fn longer_body(arg: Option) -> u32 { arg.map_or(13, |x| { let y = x * x; @@ -48,7 +52,8 @@ fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); - let _ = bad2(None); + let _ = else_if_option(None); + let _ = unop_bad(&None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index b0c203f0637..7c43fbea373 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -9,7 +9,7 @@ fn bad1(string: Option<&str>) -> (bool, &str) { } } -fn bad2(string: Option<&str>) -> Option<(bool, &str)> { +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None } else if let Some(x) = string { @@ -19,6 +19,14 @@ fn bad2(string: Option<&str>) -> Option<(bool, &str)> { } } +fn unop_bad(string: &Option<&str>) -> usize { + if let Some(s) = *string { + s.len() + } else { + 0 + } +} + fn longer_body(arg: Option) -> u32 { if let Some(x) = arg { let y = x * x; @@ -60,7 +68,8 @@ fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); - let _ = bad2(None); + let _ = else_if_option(None); + let _ = unop_bad(&None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 656cfb2f62a..b932fe59759 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -24,6 +24,16 @@ LL | | } error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:23:5 | +LL | / if let Some(s) = *string { +LL | | s.len() +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: try: `(*string).map_or(0, |s| s.len())` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:31:5 + | LL | / if let Some(x) = arg { LL | | let y = x * x; LL | | y * y @@ -41,7 +51,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -64,10 +74,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:69:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 88c8afdddff07adeff4c87431cbe8bc630a36d68 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 9 May 2020 20:20:57 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 66971ee0262..4e501f4ca02 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -140,18 +140,24 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { /// this function returns an OptionIfLetElseOccurence struct with details if /// this construct is found, or None if this construct is not found. fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { - //(String, String, String, String)> { if_chain! { - // if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly + if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; - if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); + // if type_is_option(cx, &cx.tables.expr_ty(let_body).kind); if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already - if let PatKind::TupleStruct(_, &[inner_pat], _) = &arms[0].pat.kind; - if let PatKind::Binding(_, _, id, _) = &inner_pat.kind; + if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; + if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); + if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue(arms[0].body); if !contains_return_break_continue(arms[1].body); then { + let (capture_mut, capture_ref, capture_ref_mut) = match bind_annotation { + BindingAnnotation::Unannotated => (false, false, false), + BindingAnnotation::Mutable => (true, false, false), + BindingAnnotation::Ref => (false, true, false), + BindingAnnotation::RefMut => (false, false, true), + }; let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) = &arms[0].body.kind { if let &[] = &statements { @@ -189,7 +195,7 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } }); - let parens_around_option = match &let_body.kind { + let (parens_around_option, as_ref, as_mut, let_body) = match &let_body.kind { ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) @@ -197,13 +203,15 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - | ExprKind::Block(..) | ExprKind::Field(..) | ExprKind::Path(_) - => false, - _ => true, + => (false, capture_ref, capture_ref_mut, let_body), + ExprKind::Unary(UnOp::UnDeref, expr) => (false, capture_ref, capture_ref_mut, expr), + ExprKind::AddrOf(_, mutability, expr) => (false, mutability == &Mutability::Not, mutability == &Mutability::Mut, expr), + _ => (true, capture_ref, capture_ref_mut, let_body), }; Some(OptionIfLetElseOccurence { - option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }), + option: format!("{}{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }, if as_mut { ".as_mut()" } else if as_ref { ".as_ref()" } else { "" }), method_sugg: format!("{}", method_sugg), - some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), + some_expr: format!("|{}{}{}| {}", if false { "ref " } else { "" }, if capture_mut { "mut " } else { "" }, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), wrap_braces, }) diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 80b162714ac..695a460cc4e 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -11,8 +11,22 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } else { string.map_or(Some((false, "")), |x| Some((true, x))) } } -fn unop_bad(string: &Option<&str>) -> usize { - (*string).map_or(0, |s| s.len()) +fn unop_bad(string: &Option<&str>, mut num: Option) { + let _ = string.map_or(0, |s| s.len()); + let _ = num.as_ref().map_or(&0, |s| s); + let _ = num.as_mut().map_or(&mut 0, |s| { + *s += 1; + s + }); + let _ = num.as_ref().map_or(&0, |s| s); + let _ = num.map_or(0, |mut s| { + s += 1; + s + }); + let _ = num.as_mut().map_or(&mut 0, |s| { + *s += 1; + s + }); } fn longer_body(arg: Option) -> u32 { @@ -53,7 +67,7 @@ fn main() { let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); let _ = else_if_option(None); - let _ = unop_bad(&None); + unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7c43fbea373..6f9d506d347 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -19,12 +19,40 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } } -fn unop_bad(string: &Option<&str>) -> usize { - if let Some(s) = *string { +fn unop_bad(string: &Option<&str>, mut num: Option) { + let _ = if let Some(s) = *string { s.len() } else { 0 - } + }; + let _ = if let Some(s) = &num { + s + } else { + &0 + }; + let _ = if let Some(s) = &mut num { + *s += 1; + s + } else { + &mut 0 + }; + let _ = if let Some(ref s) = num { + s + } else { + &0 + }; + let _ = if let Some(mut s) = num { + s += 1; + s + } else { + 0 + }; + let _ = if let Some(ref mut s) = num { + *s += 1; + s + } else { + &mut 0 + }; } fn longer_body(arg: Option) -> u32 { @@ -69,7 +97,7 @@ fn main() { let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); let _ = else_if_option(None); - let _ = unop_bad(&None); + unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index b932fe59759..9240d3edb27 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -22,17 +22,100 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:23:5 + --> $DIR/option_if_let_else.rs:23:13 | -LL | / if let Some(s) = *string { +LL | let _ = if let Some(s) = *string { + | _____________^ LL | | s.len() LL | | } else { LL | | 0 -LL | | } - | |_____^ help: try: `(*string).map_or(0, |s| s.len())` +LL | | }; + | |_____^ help: try: `string.map_or(0, |s| s.len())` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs: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:31:5 + --> $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 -- cgit 1.4.1-3-g733a5 From f73b455b99694fbc5ddec38317f705f546729db2 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 9 May 2020 20:44:56 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4e501f4ca02..208aa550765 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -69,17 +69,12 @@ declare_clippy_lint! { declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); -/// Returns true iff the given expression is the result of calling Result::ok +/// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { - if_chain! { - if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind; - if path.ident.name.to_ident_string() == "ok"; - if match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT); - then { - true - } else { - false - } + if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind { + path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) + } else { + false } } @@ -136,66 +131,108 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { recursive_visitor.seen_return_break_continue } +/// Extracts the body of a given arm. If the arm contains only an expression, +/// then it returns the expression. Otherwise, it returns the entire block +fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { + if let ExprKind::Block( + Block { + stmts: statements, + expr: Some(expr), + .. + }, + _, + ) = &arm.body.kind + { + if let [] = statements { + Some(&expr) + } else { + Some(&arm.body) + } + } else { + None + } +} + +/// If this is the else body of an if/else expression, then we need to wrap +/// it in curcly braces. Otherwise, we don't. +fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + if let Some(Expr { + kind: + ExprKind::Match( + _, + arms, + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + }, + ), + .. + }) = parent.expr + { + expr.hir_id == arms[1].body.hir_id + } else { + false + } + }) +} + +fn format_option_in_sugg( + cx: &LateContext<'_, '_>, + cond_expr: &Expr<'_>, + parens_around_option: bool, + as_ref: bool, + as_mut: bool, +) -> String { + format!( + "{}{}{}{}", + if parens_around_option { "(" } else { "" }, + Sugg::hir(cx, cond_expr, ".."), + if parens_around_option { ")" } else { "" }, + if as_mut { + ".as_mut()" + } else if as_ref { + ".as_ref()" + } else { + "" + } + ) +} + /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an OptionIfLetElseOccurence struct with details if +/// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; - // if type_is_option(cx, &cx.tables.expr_ty(let_body).kind); - if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already + if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue(arms[0].body); if !contains_return_break_continue(arms[1].body); then { - let (capture_mut, capture_ref, capture_ref_mut) = match bind_annotation { - BindingAnnotation::Unannotated => (false, false, false), - BindingAnnotation::Mutable => (true, false, false), - BindingAnnotation::Ref => (false, true, false), - BindingAnnotation::RefMut => (false, false, true), - }; - let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) - = &arms[0].body.kind { - if let &[] = &statements { - expr - } else { - &arms[0].body - } - } else { - return None; - }; - let (none_body, method_sugg) = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) - = &arms[1].body.kind { - if let &[] = &statements { - (expr, "map_or") - } else { - (&arms[1].body, "map_or_else") - } - } else { - return None; + let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; + let some_body = extract_body_from_arm(&arms[0])?; + let none_body = extract_body_from_arm(&arms[1])?; + let method_sugg = match &none_body.kind { + ExprKind::Block(..) => "map_or_else", + _ => "map_or", }; let capture_name = id.name.to_ident_string(); - let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { - if_chain! { - if let Some(Expr { kind: ExprKind::Match( - _, - arms, - MatchSource::IfDesugar{contains_else_clause: true} - | MatchSource::IfLetDesugar{contains_else_clause: true}), - .. } ) = parent.expr; - if expr.hir_id == arms[1].body.hir_id; - then { - true - } else { - false - } - } - }); - let (parens_around_option, as_ref, as_mut, let_body) = match &let_body.kind { + let wrap_braces = should_wrap_in_braces(cx, expr); + let (as_ref, as_mut) = match &cond_expr.kind { + ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), + ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), + _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), + }; + let parens_around_option = match &cond_expr.kind { + // Put parens around the option expression if not doing so might + // mess up the order of operations. ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) @@ -203,15 +240,20 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - | ExprKind::Block(..) | ExprKind::Field(..) | ExprKind::Path(_) - => (false, capture_ref, capture_ref_mut, let_body), - ExprKind::Unary(UnOp::UnDeref, expr) => (false, capture_ref, capture_ref_mut, expr), - ExprKind::AddrOf(_, mutability, expr) => (false, mutability == &Mutability::Not, mutability == &Mutability::Mut, expr), - _ => (true, capture_ref, capture_ref_mut, let_body), + | ExprKind::Unary(UnOp::UnDeref, _) + | ExprKind::AddrOf(..) + => false, + _ => true, + }; + let cond_expr = match &cond_expr.kind { + // Pointer dereferencing happens automatically, so we can omit it in the suggestion + ExprKind::Unary(UnOp::UnDeref, expr)|ExprKind::AddrOf(_, _, expr) => expr, + _ => cond_expr, }; Some(OptionIfLetElseOccurence { - option: format!("{}{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }, if as_mut { ".as_mut()" } else if as_ref { ".as_ref()" } else { "" }), - method_sugg: format!("{}", method_sugg), - some_expr: format!("|{}{}{}| {}", if false { "ref " } else { "" }, if capture_mut { "mut " } else { "" }, capture_name, Sugg::hir(cx, some_body, "..")), + option: format_option_in_sugg(cx, cond_expr, parens_around_option, as_ref, as_mut), + method_sugg: method_sugg.to_string(), + some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), wrap_braces, }) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 6f9d506d347..dee80d26bd9 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -20,27 +20,15 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } fn unop_bad(string: &Option<&str>, mut num: Option) { - let _ = if let Some(s) = *string { - s.len() - } else { - 0 - }; - let _ = if let Some(s) = &num { - s - } else { - &0 - }; + let _ = if let Some(s) = *string { s.len() } else { 0 }; + let _ = if let Some(s) = &num { s } else { &0 }; let _ = if let Some(s) = &mut num { *s += 1; s } else { &mut 0 }; - let _ = if let Some(ref s) = num { - s - } else { - &0 - }; + let _ = if let Some(ref s) = num { s } else { &0 }; let _ = if let Some(mut s) = num { s += 1; s diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 9240d3edb27..7005850efaf 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -24,27 +24,17 @@ LL | | } error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:23:13 | -LL | let _ = if let Some(s) = *string { - | _____________^ -LL | | s.len() -LL | | } else { -LL | | 0 -LL | | }; - | |_____^ help: try: `string.map_or(0, |s| s.len())` +LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:28:13 + --> $DIR/option_if_let_else.rs:24:13 | -LL | let _ = if let Some(s) = &num { - | _____________^ -LL | | s -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` +LL | let _ = if let Some(s) = &num { s } else { &0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -64,18 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:31:13 | -LL | let _ = if let Some(ref s) = num { - | _____________^ -LL | | s -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` +LL | let _ = if let Some(ref s) = num { s } else { &0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:44:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -95,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:50:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -115,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:59:5 + --> $DIR/option_if_let_else.rs:47:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -134,7 +119,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:68:13 + --> $DIR/option_if_let_else.rs:56:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -157,7 +142,7 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:97:13 + --> $DIR/option_if_let_else.rs:85:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -- cgit 1.4.1-3-g733a5 From 7c4de9d3dee3a7e16118df5c1cd2080af7350d98 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 11 May 2020 17:52:47 -0700 Subject: Refactoring pt. 2 --- clippy_lints/src/option_if_let_else.rs | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 208aa550765..6e8d7515671 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -179,18 +179,10 @@ fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } -fn format_option_in_sugg( - cx: &LateContext<'_, '_>, - cond_expr: &Expr<'_>, - parens_around_option: bool, - as_ref: bool, - as_mut: bool, -) -> String { +fn format_option_in_sugg(cx: &LateContext<'_, '_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( - "{}{}{}{}", - if parens_around_option { "(" } else { "" }, - Sugg::hir(cx, cond_expr, ".."), - if parens_around_option { ")" } else { "" }, + "{}{}", + Sugg::hir(cx, cond_expr, "..").maybe_par(), if as_mut { ".as_mut()" } else if as_ref { @@ -230,28 +222,13 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), }; - let parens_around_option = match &cond_expr.kind { - // Put parens around the option expression if not doing so might - // mess up the order of operations. - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - | ExprKind::Block(..) - | ExprKind::Field(..) - | ExprKind::Path(_) - | ExprKind::Unary(UnOp::UnDeref, _) - | ExprKind::AddrOf(..) - => false, - _ => true, - }; let cond_expr = match &cond_expr.kind { // Pointer dereferencing happens automatically, so we can omit it in the suggestion - ExprKind::Unary(UnOp::UnDeref, expr)|ExprKind::AddrOf(_, _, expr) => expr, + ExprKind::Unary(UnOp::UnDeref, expr) | ExprKind::AddrOf(_, _, expr) => expr, _ => cond_expr, }; Some(OptionIfLetElseOccurence { - option: format_option_in_sugg(cx, cond_expr, parens_around_option, as_ref, as_mut), + option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), -- cgit 1.4.1-3-g733a5 From 5e20475e47d3db1d32a7649a7c3a107caba32a14 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:34:10 -0700 Subject: Don't lint if contains a macro --- clippy_lints/src/option_if_let_else.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 6e8d7515671..be0c44cae34 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -10,8 +10,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::marker::PhantomData; - declare_clippy_lint! { /// **What it does:** /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more @@ -88,19 +86,17 @@ struct OptionIfLetElseOccurence { wrap_braces: bool, } -struct ReturnBreakContinueVisitor<'tcx> { +struct ReturnBreakContinueMacroVisitor { seen_return_break_continue: bool, - phantom_data: PhantomData<&'tcx bool>, } -impl<'tcx> ReturnBreakContinueVisitor<'tcx> { - fn new() -> ReturnBreakContinueVisitor<'tcx> { - ReturnBreakContinueVisitor { +impl ReturnBreakContinueMacroVisitor { + fn new() -> ReturnBreakContinueMacroVisitor { + ReturnBreakContinueMacroVisitor { seen_return_break_continue: false, - phantom_data: PhantomData, } } } -impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -119,14 +115,18 @@ impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { // desugaring, as this will detect a break if there's a while loop // or a for loop inside the expression. _ => { - rustc_hir::intravisit::walk_expr(self, ex); + if utils::in_macro(ex.span) { + self.seen_return_break_continue = true; + } else { + rustc_hir::intravisit::walk_expr(self, ex); + } }, } } } -fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { - let mut recursive_visitor: ReturnBreakContinueVisitor<'tcx> = ReturnBreakContinueVisitor::new(); +fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { + let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); recursive_visitor.visit_expr(expression); recursive_visitor.seen_return_break_continue } @@ -205,8 +205,8 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue(arms[0].body); - if !contains_return_break_continue(arms[1].body); + if !contains_return_break_continue_macro(arms[0].body); + if !contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; -- cgit 1.4.1-3-g733a5 From 5150277a4f765bb113e163adc7eb495dcbb57129 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:37:21 -0700 Subject: 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 +--- clippy_lints/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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 2d0855f6895..cfad9d79f2b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -481,15 +481,11 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { } fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { - if let Some(stmt) = block.stmts.first() { - match &stmt.kind { + block.stmts.first().map_or(block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr), _ => false, - } - } else { - block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)) - } + }) } fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool { @@ -499,11 +495,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: & ExprKind::Ret(None) | ExprKind::Break(_, None) => false, ExprKind::Call(path_expr, _) => { if let ExprKind::Path(qpath) = &path_expr.kind { - if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { - !match_def_path(cx, fun_id, &paths::BEGIN_PANIC) - } else { - true - } + tables.qpath_res(qpath, path_expr.hir_id).opt_def_id().map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)) } else { true } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index f911cb68ea5..7e44618e90e 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -135,13 +135,9 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } } -impl<'tcx> ArmVisitor<'_, 'tcx> { +impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { - if let Some(arm_mutex) = self.found_mutex { - SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) - } else { - false - } + self.found_mutex.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 26d96428771..f57fa830adc 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -303,14 +303,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => { - if let Some(principal) = tt.principal() { - cx.tcx + tt.principal().map_or(false, |principal| cx.tcx .associated_items(principal.def_id()) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) - } else { - false - } + .any(|item| is_is_empty(cx, &item))) }, ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7ba43562d7d..ea2e23bd3a1 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,10 +264,11 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') - } else if let Some(fraction) = &mut num_lit.fraction { - (fraction, &["32", "64"][..], 'f') } else { - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + num_lit.fraction.as_mut().map_or( + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), + |fraction| (fraction, &["32", "64"][..], 'f') + ) }; let mut split = part.rsplit('_'); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d821b513484..8d48f39a045 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -687,11 +687,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } }, ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => { - if let Some(ref e) = *e { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) - } else { - NeverLoopResult::AlwaysBreak - } + e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)) }, ExprKind::InlineAsm(ref asm) => asm .operands @@ -1882,11 +1878,7 @@ fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc match ty.kind { ty::Array(_, n) => { - if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) { - (0..=32).contains(&val) - } else { - false - } + n.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |val| (0..=32).contains(&val)) }, _ => false, } @@ -1899,11 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< return None; } if let StmtKind::Local(ref local) = block.stmts[0].kind { - if let Some(expr) = local.init { - Some(expr) - } else { - None - } + local.init.map(|expr| expr) } else { None } @@ -2023,15 +2011,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if let PatKind::Binding(.., ident, _) = local.pat.kind { self.name = Some(ident.name); - self.state = if let Some(ref init) = local.init { - if is_integer_const(&self.cx, init, 0) { + self.state = local.init.as_ref().map_or(VarState::Declared, |init| if is_integer_const(&self.cx, init, 0) { VarState::Warn } else { VarState::Declared - } - } else { - VarState::Declared - } + }) } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 160304865c5..ddad16e163e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2461,11 +2461,7 @@ fn derefs_to_slice<'tcx>( ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), ty::Array(_, size) => { - if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) { - size < 32 - } else { - false - } + size.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |size| size < 32) }, ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index fdcba110542..97909c97fc7 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -78,11 +78,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::Block(ref block, _) => { - if let Some(expr) = &block.expr { - check_expression(cx, arg_id, &expr) - } else { - (false, false) - } + block.expr.as_ref().map_or((false, false), |expr| check_expression(cx, arg_id, &expr)) }, hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 0a2d577396a..a3d1a500aa7 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -86,16 +86,19 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt if args.len() != 2 { return None; } - if let Some(c) = constant_simple(cx, cx.tables(), &args[0]) { - if constant_simple(cx, cx.tables(), &args[1]).is_none() { - // otherwise ignore - Some((m, c, &args[1])) + constant_simple(cx, cx.tables, &args[0]).map_or_else( + || if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { + Some((m, c, &args[0])) } else { None + }, + |c| { + if constant_simple(cx, cx.tables, &args[1]).is_none() { + // otherwise ignore + Some((c, &args[1])) + } else { + None + } } - } else if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { - Some((m, c, &args[0])) - } else { - None - } + ).map(|(c, arg)| (m, c, arg)) } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 6a256627bd1..5b0f9d6e3ec 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -682,16 +682,12 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: /// `unused_variables`'s idea /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, expr) { - match parent.kind { + get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind { ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => { SpanlessEq::new(cx).eq_expr(rhs, expr) }, _ => is_used(cx, parent), - } - } else { - true - } + }) } /// Tests whether an expression is in a macro expansion (e.g., something diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index be0c44cae34..b6d08b8ae17 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -37,6 +37,7 @@ declare_clippy_lint! { /// /// ```rust /// # let optional: Option = Some(0); + /// # fn do_complicated_function() -> u32 { 5 }; /// let _ = if let Some(foo) = optional { /// foo /// } else { @@ -54,9 +55,10 @@ declare_clippy_lint! { /// /// ```rust /// # let optional: Option = Some(0); + /// # fn do_complicated_function() -> u32 { 5 }; /// let _ = optional.map_or(5, |foo| foo); /// let _ = optional.map_or_else(||{ - /// let y = do_complicated_function; + /// let y = do_complicated_function(); /// y*y /// }, |foo| foo); /// ``` diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c939744173..4d54e3117fa 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -259,15 +259,10 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - ( + fn_source.rfind("->").map_or((ty.span, Applicability::MaybeIncorrect), |rpos| ( ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, - ) - } else { - (ty.span, Applicability::MaybeIncorrect) - } + )) } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 7da47ee4ff9..de94fb87147 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -164,15 +164,11 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & } fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { - let var_ty = cx.tables().node_type_opt(pat_id); - if let Some(var_ty) = var_ty { - match var_ty.kind { + let var_ty = cx.tables.node_type_opt(pat_id); + var_ty.map_or(false, |var_ty| match var_ty.kind { ty::Adt(..) => false, _ => true, - } - } else { - false - } + }) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b1345f0de5e..df87c1b9802 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1205,16 +1205,14 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; let opt = snippet_opt(cx, op.span); - let sugg = if let Some(ref snip) = opt { - if should_strip_parens(op, snip) { + let sugg = opt.as_ref().map_or_else(|| { + applicability = Applicability::HasPlaceholders; + ".." + }, |snip| if should_strip_parens(op, snip) { &snip[1..snip.len() - 1] } else { snip.as_str() - } - } else { - applicability = Applicability::HasPlaceholders; - ".." - }; + }); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f85db1e2006..eac7ae2358e 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -167,14 +167,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind; then { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - let should_check = if let Some(ref params) = *parameters { - !params.parenthesized && !params.args.iter().any(|arg| match arg { + let should_check = parameters.as_ref().map_or(true, |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { GenericArg::Lifetime(_) => true, _ => false, - }) - } else { - true - }; + })); if should_check { let visitor = &mut UseSelfVisitor { diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 4dcf6c105ec..2d72f9c3fe1 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -65,8 +65,7 @@ pub fn get_attr<'a>( }; let attr_segments = &attr.path.segments; if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { - if let Some(deprecation_status) = - BUILTIN_ATTRIBUTES + BUILTIN_ATTRIBUTES .iter() .find_map(|(builtin_name, deprecation_status)| { if *builtin_name == attr_segments[1].ident.to_string() { @@ -74,8 +73,10 @@ pub fn get_attr<'a>( } else { None } - }) - { + }).map_or_else(|| { + sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + false + }, |deprecation_status| { let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { @@ -97,10 +98,7 @@ pub fn get_attr<'a>( attr_segments[1].ident.to_string() == name }, } - } else { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); - false - } + }) } else { false } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 99ba7d64331..6f23e968006 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -153,11 +153,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap(); let trt_id = cx.tcx.trait_of_item(def_id); - if let Some(trt_id) = trt_id { - match_def_path(cx, trt_id, path) - } else { - false - } + trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } /// Checks if an expression references a variable of the given name. @@ -600,21 +596,13 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// // ^^^^^^^^^^ /// ``` pub fn first_line_of_span(cx: &T, span: Span) -> Span { - if let Some(first_char_pos) = first_char_in_first_line(cx, span) { - span.with_lo(first_char_pos) - } else { - span - } + first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } fn first_char_in_first_line(cx: &T, span: Span) -> Option { let line_span = line_span(cx, span); - if let Some(snip) = snippet_opt(cx, line_span) { - snip.find(|c: char| !c.is_whitespace()) - .map(|pos| line_span.lo() + BytePos::from_usize(pos)) - } else { - None - } + snippet_opt(cx, line_span).and_then(|snip| snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos))) } /// Returns the indentation of the line of a span @@ -626,11 +614,7 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Option { - if let Some(snip) = snippet_opt(cx, line_span(cx, span)) { - snip.find(|c: char| !c.is_whitespace()) - } else { - None - } + snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Extends the span to the beginning of the spans line, incl. whitespaces. @@ -738,8 +722,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio let enclosing_node = map .get_enclosing_scope(hir_id) .and_then(|enclosing_id| map.find(enclosing_id)); - if let Some(node) = enclosing_node { - match node { + enclosing_node.and_then(|node| match node { Node::Block(block) => Some(block), Node::Item(&Item { kind: ItemKind::Fn(_, _, eid), @@ -753,10 +736,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio _ => None, }, _ => None, - } - } else { - None - } + }) } /// Returns the base type for HIR references and pointers. @@ -1328,11 +1308,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - if let Some(did) = did { - must_use_attr(&cx.tcx.get_attrs(did)).is_some() - } else { - false - } + did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 99413153d49..7a79741b30b 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -200,12 +200,10 @@ impl<'a> NumericLiteral<'a> { fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) { debug_assert!(lit_kind.is_numeric()); - if let Some(suffix_length) = lit_suffix_length(lit_kind) { + lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| { let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length); (unsuffixed, Some(suffix)) - } else { - (src, None) - } + }) } fn lit_suffix_length(lit_kind: &LitKind) -> Option { diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 8fc97f2fd64..20bea3cbabe 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -492,8 +492,7 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp { /// before it on its line. fn indentation(cx: &T, span: Span) -> Option { let lo = cx.sess().source_map().lookup_char_pos(span.lo()); - if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) { - if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { + lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */).and_then(|line| if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { // We can mix char and byte positions here because we only consider `[ \t]`. if lo.col == CharPos(pos) { Some(line[..pos].into()) @@ -502,10 +501,7 @@ fn indentation(cx: &T, span: Span) -> Option { } } else { None - } - } else { - None - } + }) } /// Convenience extension trait for `DiagnosticBuilder`. diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 732f4b28e06..3b10b7b82a0 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -297,12 +297,10 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = if let Some(e) = expr { - snippet_with_applicability(cx, e.span, "v", &mut applicability) - } else { + let suggestion = expr.map_or_else(|| { applicability = Applicability::HasPlaceholders; Cow::Borrowed("v") - }; + }, |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable)); span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From 93f0f5d37b45c648aebf87fe7e7379599ba3e726 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 12 Jun 2020 08:32:38 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..01c0e4b0302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1491,7 +1491,6 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero -[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cd91e7ceb32..fe34e4390d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1161,6 +1161,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_continue::NEEDLESS_CONTINUE), LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), @@ -1372,7 +1373,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), @@ -1521,7 +1521,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index a3d1a500aa7..6ec7f8cae5d 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -99,6 +99,5 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt } else { None } - } - ).map(|(c, arg)| (m, c, arg)) + }) } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index b6d08b8ae17..c877cc6fa8b 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { /// }, |foo| foo); /// ``` pub OPTION_IF_LET_ELSE, - style, + pedantic, "reimplementation of Option::map_or" } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4d54e3117fa..3c939744173 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -259,10 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source.rfind("->").map_or((ty.span, Applicability::MaybeIncorrect), |rpos| ( + if let Some(rpos) = fn_source.rfind("->") { + #[allow(clippy::cast_possible_truncation)] + ( ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, - )) + ) + } else { + (ty.span, Applicability::MaybeIncorrect) + } } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b499d565fa7..e681f47f949 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1622,7 +1622,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "option_if_let_else", - group: "style", + group: "pedantic", desc: "reimplementation of Option::map_or", deprecation: None, module: "option_if_let_else", -- cgit 1.4.1-3-g733a5 From ccb999851aaa4d3e9cd97110bd6523a6c3df46fd Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 25 Jun 2020 11:39:58 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c0e4b0302..1a081bb85fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1491,6 +1491,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c877cc6fa8b..7e299dd5fd9 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -71,7 +71,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { - if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind { + if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) } else { false -- cgit 1.4.1-3-g733a5 From 6ce981225b73d6c3514b0abb759a5282521a17d9 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 25 Jun 2020 11:58:47 -0700 Subject: 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 +- clippy_lints/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 | 65 ++++++++++++---------- clippy_lints/src/utils/mod.rs | 32 ++++++----- clippy_lints/src/utils/sugg.rs | 16 ++++-- clippy_lints/src/write.rs | 11 ++-- 18 files changed, 147 insertions(+), 115 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index cfad9d79f2b..c4397560d7d 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -480,12 +480,15 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { } } -fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { - block.stmts.first().map_or(block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), |stmt| match &stmt.kind { +fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { + block.stmts.first().map_or( + block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), + |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr), _ => false, - }) + }, + ) } fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool { @@ -495,7 +498,10 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: & ExprKind::Ret(None) | ExprKind::Break(_, None) => false, ExprKind::Call(path_expr, _) => { if let ExprKind::Path(qpath) = &path_expr.kind { - tables.qpath_res(qpath, path_expr.hir_id).opt_def_id().map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)) + tables + .qpath_res(qpath, path_expr.hir_id) + .opt_def_id() + .map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)) } else { true } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 7e44618e90e..5426e14ead5 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -136,8 +136,9 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { - self.found_mutex.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) + fn same_mutex(&self, cx: &LateContext<'_, '_>, op_mutex: &Expr<'_>) -> bool { + self.found_mutex + .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f57fa830adc..1b09328ceab 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -302,12 +302,12 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr)); match ty.kind { - ty::Dynamic(ref tt, ..) => { - tt.principal().map_or(false, |principal| cx.tcx - .associated_items(principal.def_id()) - .in_definition_order() - .any(|item| is_is_empty(cx, &item))) - }, + ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + cx.tcx + .associated_items(principal.def_id()) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did), ty::Array(..) | ty::Slice(..) | ty::Str => true, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index ea2e23bd3a1..a36fdca5d5d 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -265,10 +265,12 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') } else { - num_lit.fraction.as_mut().map_or( - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), - |fraction| (fraction, &["32", "64"][..], 'f') - ) + num_lit + .fraction + .as_mut() + .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| { + (fraction, &["32", "64"][..], 'f') + }) }; let mut split = part.rsplit('_'); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8d48f39a045..b803d753b6d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -686,9 +686,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, - ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => { - e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)) - }, + ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { + combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + }), ExprKind::InlineAsm(ref asm) => asm .operands .iter() @@ -1877,9 +1877,9 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc match ty.kind { - ty::Array(_, n) => { - n.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |val| (0..=32).contains(&val)) - }, + ty::Array(_, n) => n + .try_eval_usize(cx.tcx, cx.param_env) + .map_or(false, |val| (0..=32).contains(&val)), _ => false, } } @@ -1891,7 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< return None; } if let StmtKind::Local(ref local) = block.stmts[0].kind { - local.init.map(|expr| expr) + local.init //.map(|expr| expr) } else { None } @@ -2011,11 +2011,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if let PatKind::Binding(.., ident, _) = local.pat.kind { self.name = Some(ident.name); - self.state = local.init.as_ref().map_or(VarState::Declared, |init| if is_integer_const(&self.cx, init, 0) { + self.state = local.init.as_ref().map_or(VarState::Declared, |init| { + if is_integer_const(&self.cx, init, 0) { VarState::Warn } else { VarState::Declared - }) + } + }) } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ddad16e163e..f1c8894c0ee 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2460,9 +2460,9 @@ fn derefs_to_slice<'tcx>( ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), - ty::Array(_, size) => { - size.try_eval_usize(cx.tcx, cx.param_env).map_or(false, |size| size < 32) - }, + ty::Array(_, size) => size + .try_eval_usize(cx.tcx, cx.param_env) + .map_or(false, |size| size < 32), ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 97909c97fc7..75e123eb593 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -77,9 +77,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, - hir::ExprKind::Block(ref block, _) => { - block.expr.as_ref().map_or((false, false), |expr| check_expression(cx, arg_id, &expr)) - }, + hir::ExprKind::Block(ref block, _) => block + .expr + .as_ref() + .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)), hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 6ec7f8cae5d..5eb8398d68e 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MinMaxPass { } } -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] enum MinMax { Min, Max, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 5b0f9d6e3ec..3d4225f36a7 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -683,11 +683,9 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind { - ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => { - SpanlessEq::new(cx).eq_expr(rhs, expr) - }, - _ => is_used(cx, parent), - }) + ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), + _ => is_used(cx, parent), + }) } /// Tests whether an expression is in a macro expansion (e.g., something diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 7e299dd5fd9..ab9ea76a838 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -260,7 +260,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { detection.some_expr, if detection.wrap_braces { " }" } else { "" }, ), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c939744173..faef7e724dd 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -259,15 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - ( - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - } else { - (ty.span, Applicability::MaybeIncorrect) - } + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index de94fb87147..f16db2df3a9 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -166,9 +166,9 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { let var_ty = cx.tables.node_type_opt(pat_id); var_ty.map_or(false, |var_ty| match var_ty.kind { - ty::Adt(..) => false, - _ => true, - }) + ty::Adt(..) => false, + _ => true, + }) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index df87c1b9802..d6f31a99bb3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1205,14 +1205,19 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; let opt = snippet_opt(cx, op.span); - let sugg = opt.as_ref().map_or_else(|| { - applicability = Applicability::HasPlaceholders; - ".." - }, |snip| if should_strip_parens(op, snip) { - &snip[1..snip.len() - 1] - } else { - snip.as_str() - }); + let sugg = opt.as_ref().map_or_else( + || { + applicability = Applicability::HasPlaceholders; + ".." + }, + |snip| { + if should_strip_parens(op, snip) { + &snip[1..snip.len() - 1] + } else { + snip.as_str() + } + }, + ); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index eac7ae2358e..39a8c020872 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -167,10 +167,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind; then { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - let should_check = parameters.as_ref().map_or(true, |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { + let should_check = parameters.as_ref().map_or( + true, + |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { GenericArg::Lifetime(_) => true, _ => false, - })); + }) + ); if should_check { let visitor = &mut UseSelfVisitor { diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 2d72f9c3fe1..4bb4b087c55 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -66,39 +66,44 @@ pub fn get_attr<'a>( let attr_segments = &attr.path.segments; if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { BUILTIN_ATTRIBUTES - .iter() - .find_map(|(builtin_name, deprecation_status)| { - if *builtin_name == attr_segments[1].ident.to_string() { - Some(deprecation_status) - } else { - None - } - }).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(); + .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 }, - DeprecationStatus::None => { - diag.cancel(); - attr_segments[1].ident.to_string() == name + |deprecation_status| { + let mut diag = + sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + match *deprecation_status { + DeprecationStatus::Deprecated => { + diag.emit(); + false + }, + DeprecationStatus::Replaced(new_name) => { + diag.span_suggestion( + attr_segments[1].ident.span, + "consider using", + new_name.to_string(), + Applicability::MachineApplicable, + ); + diag.emit(); + false + }, + DeprecationStatus::None => { + diag.cancel(); + attr_segments[1].ident.to_string() == name + }, + } }, - } - }) + ) } else { false } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 6f23e968006..3a3b79925ff 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -601,8 +601,10 @@ pub fn first_line_of_span(cx: &T, span: Span) -> Span { fn first_char_in_first_line(cx: &T, span: Span) -> Option { let line_span = line_span(cx, span); - snippet_opt(cx, line_span).and_then(|snip| snip.find(|c: char| !c.is_whitespace()) - .map(|pos| line_span.lo() + BytePos::from_usize(pos))) + snippet_opt(cx, line_span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos)) + }) } /// Returns the indentation of the line of a span @@ -723,20 +725,20 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio .get_enclosing_scope(hir_id) .and_then(|enclosing_id| map.find(enclosing_id)); enclosing_node.and_then(|node| match node { - Node::Block(block) => Some(block), - Node::Item(&Item { - kind: ItemKind::Fn(_, _, eid), - .. - }) - | Node::ImplItem(&ImplItem { - kind: ImplItemKind::Fn(_, eid), - .. - }) => match cx.tcx.hir().body(eid).value.kind { - ExprKind::Block(ref block, _) => Some(block), - _ => None, - }, - _ => None, + Node::Block(block) => Some(block), + Node::Item(&Item { + kind: ItemKind::Fn(_, _, eid), + .. }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Fn(_, eid), + .. + }) => match cx.tcx.hir().body(eid).value.kind { + ExprKind::Block(ref block, _) => Some(block), + _ => None, + }, + _ => None, + }) } /// Returns the base type for HIR references and pointers. diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 20bea3cbabe..0ac7714fbeb 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -492,15 +492,19 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp { /// before it on its line. fn indentation(cx: &T, span: Span) -> Option { let lo = cx.sess().source_map().lookup_char_pos(span.lo()); - lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */).and_then(|line| if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { - // We can mix char and byte positions here because we only consider `[ \t]`. - if lo.col == CharPos(pos) { - Some(line[..pos].into()) + lo.file + .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) + .and_then(|line| { + if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { + // We can mix char and byte positions here because we only consider `[ \t]`. + if lo.col == CharPos(pos) { + Some(line[..pos].into()) + } else { + None + } } else { None } - } else { - None }) } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 3b10b7b82a0..063f94582b9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -297,10 +297,13 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else(|| { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable)); + let suggestion = expr.map_or_else( + || { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }, + |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable), + ); span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From 1c32263176d95ae47928d1955e44a4315ffcea2d Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 1 Jul 2020 11:41:11 -0700 Subject: Formatted updates to lints --- clippy_lints/src/minmax.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 5eb8398d68e..2e5f5f10f4b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -87,10 +87,12 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt return None; } constant_simple(cx, cx.tables, &args[0]).map_or_else( - || if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { - Some((m, c, &args[0])) - } else { - None + || { + if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { + Some((m, c, &args[0])) + } else { + None + } }, |c| { if constant_simple(cx, cx.tables, &args[1]).is_none() { @@ -99,5 +101,6 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt } else { None } - }) + }, + ) } -- cgit 1.4.1-3-g733a5 From c8f700ea697f74ef8f86891b050c859cf457e3ab Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 3 Jul 2020 20:28:40 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c4397560d7d..d68d0d8ccf5 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -480,7 +480,7 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { } } -fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { +fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { block.stmts.first().map_or( block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)), |stmt| match &stmt.kind { diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 5426e14ead5..fbd2eeacc6e 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -136,7 +136,7 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn same_mutex(&self, cx: &LateContext<'_, '_>, op_mutex: &Expr<'_>) -> bool { + fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { self.found_mutex .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 2e5f5f10f4b..c8aa98d3489 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -86,18 +86,12 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt if args.len() != 2 { return None; } - constant_simple(cx, cx.tables, &args[0]).map_or_else( - || { - if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { - Some((m, c, &args[0])) - } else { - None - } - }, + constant_simple(cx, cx.tables(), &args[0]).map_or_else( + || constant_simple(cx, cx.tables(), &args[1]).map(|c| (m, c, &args[0])), |c| { - if constant_simple(cx, cx.tables, &args[1]).is_none() { + if constant_simple(cx, cx.tables(), &args[1]).is_none() { // otherwise ignore - Some((c, &args[1])) + Some((m, c, &args[1])) } else { None } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index ab9ea76a838..8dbe58763bf 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -70,9 +70,9 @@ declare_clippy_lint! { declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` -fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { +fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { - path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) + path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables().expr_ty(&receiver), &paths::RESULT) } else { false } @@ -157,7 +157,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { /// If this is the else body of an if/else expression, then we need to wrap /// it in curcly braces. Otherwise, we don't. -fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { +fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if let Some(Expr { kind: @@ -181,7 +181,7 @@ fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } -fn format_option_in_sugg(cx: &LateContext<'_, '_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { +fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", Sugg::hir(cx, cond_expr, "..").maybe_par(), @@ -198,7 +198,7 @@ fn format_option_in_sugg(cx: &LateContext<'_, '_>, cond_expr: &Expr<'_>, as_ref: /// If this expression is the option if let/else construct we're detecting, then /// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { +fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; @@ -242,8 +242,8 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { +impl<'a> LateLintPass<'a> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) { if let Some(detection) = detect_option_if_let_else(cx, expr) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index f16db2df3a9..4cdff63f118 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -164,7 +164,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & } fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { - let var_ty = cx.tables.node_type_opt(pat_id); + let var_ty = cx.tables().node_type_opt(pat_id); var_ty.map_or(false, |var_ty| match var_ty.kind { ty::Adt(..) => false, _ => true, -- cgit 1.4.1-3-g733a5 From a6f1af75d71fcf8e029b78142370e7563798c503 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Fri, 3 Apr 2020 13:58:52 -0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 4efd0689267..e3ee4296119 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,6 +1,6 @@ use crate::consts::{ constant, constant_simple, Constant, - Constant::{F32, F64}, + Constant::{Int, F32, F64}, }; use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use if_chain::if_chain; @@ -293,6 +293,30 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } +fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + // Check argument + if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + let (lint, help, suggestion) = match value { + Int(2) => ( + IMPRECISE_FLOPS, + "square can be computed more accurately", + format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), + ), + _ => return, + }; + + span_lint_and_sugg( + cx, + lint, + expr.span, + help, + "consider using", + suggestion, + Applicability::MachineApplicable, + ); + } +} + // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -489,6 +513,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { "ln" => check_ln1p(cx, expr, args), "log" => check_log_base(cx, expr, args), "powf" => check_powf(cx, expr, args), + "powi" => check_powi(cx, expr, args), _ => {}, } } diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 42c5e5d2bae..7dc7ee94aff 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -25,11 +25,11 @@ fn check_ln1p() { let _ = 2.0f32.ln_1p(); let _ = x.ln_1p(); let _ = (x / 2.0).ln_1p(); - let _ = x.powi(2).ln_1p(); - let _ = (x.powi(2) / 2.0).ln_1p(); + let _ = x.powi(3).ln_1p(); + let _ = (x.powi(3) / 2.0).ln_1p(); let _ = ((std::f32::consts::E - 1.0)).ln_1p(); let _ = x.ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); let _ = (x / 2.0).ln_1p(); // Cases where the lint shouldn't be applied @@ -43,9 +43,9 @@ fn check_ln1p() { let _ = 2.0f64.ln_1p(); let _ = x.ln_1p(); let _ = (x / 2.0).ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = x.ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); let _ = (x / 2.0).ln_1p(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 8be0d9ad56f..01181484e7d 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -25,11 +25,11 @@ fn check_ln1p() { let _ = (1f32 + 2.0).ln(); let _ = (1.0 + x).ln(); let _ = (1.0 + x / 2.0).ln(); - let _ = (1.0 + x.powi(2)).ln(); - let _ = (1.0 + x.powi(2) / 2.0).ln(); + let _ = (1.0 + x.powi(3)).ln(); + let _ = (1.0 + x.powi(3) / 2.0).ln(); let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); let _ = (x + 1.0).ln(); - let _ = (x.powi(2) + 1.0).ln(); + let _ = (x.powi(3) + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln(); // Cases where the lint shouldn't be applied @@ -43,9 +43,9 @@ fn check_ln1p() { let _ = (1f64 + 2.0).ln(); let _ = (1.0 + x).ln(); let _ = (1.0 + x / 2.0).ln(); - let _ = (1.0 + x.powi(2)).ln(); + let _ = (1.0 + x.powi(3)).ln(); let _ = (x + 1.0).ln(); - let _ = (x.powi(2) + 1.0).ln(); + let _ = (x.powi(3) + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index 943fbdb0b83..900dc2b7933 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -77,14 +77,14 @@ LL | let _ = (1.0 + x / 2.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:28:13 | -LL | let _ = (1.0 + x.powi(2)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (1.0 + x.powi(3)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:29:13 | -LL | let _ = (1.0 + x.powi(2) / 2.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()` +LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:30:13 @@ -101,8 +101,8 @@ LL | let _ = (x + 1.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:32:13 | -LL | let _ = (x.powi(2) + 1.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (x.powi(3) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:33:13 @@ -143,8 +143,8 @@ LL | let _ = (1.0 + x / 2.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:46:13 | -LL | let _ = (1.0 + x.powi(2)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (1.0 + x.powi(3)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:47:13 @@ -155,8 +155,8 @@ LL | let _ = (x + 1.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:48:13 | -LL | let _ = (x.powi(2) + 1.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (x.powi(3) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:49:13 diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index 78a9d44829b..b0641a100cd 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -11,7 +11,7 @@ fn main() { let _ = (-3.1f32).exp(); let _ = x.sqrt(); let _ = x.cbrt(); - let _ = x.powi(2); + let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(16_777_215); let _ = x.powi(-16_777_215); @@ -30,7 +30,7 @@ fn main() { let _ = (-3.1f64).exp(); let _ = x.sqrt(); let _ = x.cbrt(); - let _ = x.powi(2); + let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(-2_147_483_648); let _ = x.powi(2_147_483_647); diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index dbc1cac5cb4..a0a2c973900 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -11,7 +11,7 @@ fn main() { let _ = std::f32::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); - let _ = x.powf(2.0); + let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(16_777_215.0); let _ = x.powf(-16_777_215.0); @@ -30,7 +30,7 @@ fn main() { let _ = std::f64::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); - let _ = x.powf(2.0); + let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(-2_147_483_648.0); let _ = x.powf(2_147_483_647.0); diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index ad5163f0079..2422eb911e9 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -53,8 +53,8 @@ LL | let _ = x.powf(1.0 / 3.0); error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:14:13 | -LL | let _ = x.powf(2.0); - | ^^^^^^^^^^^ help: consider using: `x.powi(2)` +LL | let _ = x.powf(3.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:15:13 @@ -125,8 +125,8 @@ LL | let _ = x.powf(1.0 / 3.0); error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:33:13 | -LL | let _ = x.powf(2.0); - | ^^^^^^^^^^^ help: consider using: `x.powi(2)` +LL | let _ = x.powf(3.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:34:13 diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed new file mode 100644 index 00000000000..0ce6f72535d --- /dev/null +++ b/tests/ui/floating_point_powi.fixed @@ -0,0 +1,12 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let one = 1; + let x = 3f32; + let _ = x * x; + let _ = x * x; + // Cases where the lint shouldn't be applied + let _ = x.powi(3); + let _ = x.powi(one + 1); +} diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs new file mode 100644 index 00000000000..c87e836bedd --- /dev/null +++ b/tests/ui/floating_point_powi.rs @@ -0,0 +1,12 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let one = 1; + let x = 3f32; + let _ = x.powi(2); + let _ = x.powi(1 + 1); + // Cases where the lint shouldn't be applied + let _ = x.powi(3); + let _ = x.powi(one + 1); +} diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr new file mode 100644 index 00000000000..ae7bbaa4473 --- /dev/null +++ b/tests/ui/floating_point_powi.stderr @@ -0,0 +1,16 @@ +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:7:13 + | +LL | let _ = x.powi(2); + | ^^^^^^^^^ help: consider using: `x * x` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:8:13 + | +LL | let _ = x.powi(1 + 1); + | ^^^^^^^^^^^^^ help: consider using: `x * x` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From f62798454c8a7f9f2c3e87e0a913b3bd79b6d2ed Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 25 May 2020 13:54:39 -0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index e3ee4296119..3b2e46a9a85 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,10 +2,10 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -296,6 +296,17 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + // TODO: need more specific check. this is too wide. remember also to include tests + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(grandparent) = get_parent_expr(cx, parent) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } + } + } + } + let (lint, help, suggestion) = match value { Int(2) => ( IMPRECISE_FLOPS, @@ -317,6 +328,57 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } +fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref add_lhs, + ref add_rhs, + ) = args[0].kind + { + // check if expression of the form x * x + y * y + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; + if are_exprs_equal(cx, lmul_lhs, lmul_rhs); + if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + then { + return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); + } + } + + // check if expression of the form x.powi(2) + y.powi(2) + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind; + if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; + if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]); + if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]); + if Int(2) == lvalue && Int(2) == rvalue; + then { + return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); + } + } + } + + None +} + +fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + if let Some(message) = detect_hypot(cx, args) { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "hypotenuse can be computed more accurately", + "consider using", + message, + Applicability::MachineApplicable, + ); + } +} + // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -368,6 +430,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind { + if let Some(parent) = get_parent_expr(cx, expr) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } + } + } + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { (inner_lhs, inner_rhs, rhs) } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { @@ -514,6 +584,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { "log" => check_log_base(cx, expr, args), "powf" => check_powf(cx, expr, args), "powi" => check_powi(cx, expr, args), + "sqrt" => check_hypot(cx, expr, args), _ => {}, } } diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed new file mode 100644 index 00000000000..f90695bc3fe --- /dev/null +++ b/tests/ui/floating_point_hypot.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let y = 4f32; + let _ = x.hypot(y); + let _ = (x + 1f32).hypot(y); + let _ = x.hypot(y); + // Cases where the lint shouldn't be applied + let _ = x.mul_add(x, y * y).sqrt(); + let _ = x.mul_add(4f32, y * y).sqrt(); +} diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs new file mode 100644 index 00000000000..e7b048e262f --- /dev/null +++ b/tests/ui/floating_point_hypot.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let y = 4f32; + let _ = (x * x + y * y).sqrt(); + let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); + let _ = (x.powi(2) + y.powi(2)).sqrt(); + // Cases where the lint shouldn't be applied + let _ = x.mul_add(x, y * y).sqrt(); + let _ = (x * 4f32 + y * y).sqrt(); +} diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr new file mode 100644 index 00000000000..fe1dfc7a451 --- /dev/null +++ b/tests/ui/floating_point_hypot.stderr @@ -0,0 +1,30 @@ +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:7:13 + | +LL | let _ = (x * x + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:8:13 + | +LL | let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:9:13 + | +LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_hypot.rs:12:13 + | +LL | let _ = (x * 4f32 + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(4f32, y * y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index e343c37740d..911700bab00 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -18,4 +18,9 @@ fn main() { let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); + + let _ = a.mul_add(a, b).sqrt(); + + // Cases where the lint shouldn't be applied + let _ = (a * a + b * b).sqrt(); } diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 810f929c856..d202385fc8a 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -18,4 +18,9 @@ fn main() { let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; + + let _ = (a * a + b).sqrt(); + + // Cases where the lint shouldn't be applied + let _ = (a * a + b * b).sqrt(); } diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 2dfbf562d15..ac8d0c0cae0 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: aborting due to 9 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:22:13 + | +LL | let _ = (a * a + b).sqrt(); + | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 0ce6f72535d..98766e68aaf 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,12 +1,17 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let one = 1; let x = 3f32; let _ = x * x; let _ = x * x; + + let y = 4f32; + let _ = (x * x + y).sqrt(); + let _ = (x + y * y).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); + let _ = x.hypot(y); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index c87e836bedd..3c4b636a3d8 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,12 +1,17 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let one = 1; let x = 3f32; let _ = x.powi(2); let _ = x.powi(1 + 1); + + let y = 4f32; + let _ = (x.powi(2) + y).sqrt(); + let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); + let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index ae7bbaa4473..f370e24bf05 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -12,5 +12,23 @@ error: square can be computed more accurately LL | let _ = x.powi(1 + 1); | ^^^^^^^^^^^^^ help: consider using: `x * x` -error: aborting due to 2 previous errors +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:11:14 + | +LL | let _ = (x.powi(2) + y).sqrt(); + | ^^^^^^^^^ help: consider using: `x * x` + +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:12:18 + | +LL | let _ = (x + y.powi(2)).sqrt(); + | ^^^^^^^^^ help: consider using: `y * y` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_powi.rs:16:13 + | +LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 2e8a1be444afc6a9b5137d3e7e4fdcfcb1f89e0d Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 Jul 2020 22:10:59 +0200 Subject: new lint: match_like_matches_macro --- CHANGELOG.md | 1 + clippy_lints/src/matches.rs | 395 ++++++++++++++++++++- clippy_lints/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 +-- .../redundant_pattern_matching_const_result.fixed | 2 +- .../ui/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..6261ca4879a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1513,6 +1513,7 @@ Released 2018-09-13 [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b754a45aa40..34aa2981535 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -13,14 +13,14 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - QPath, RangeEnd, + Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat, + PatKind, QPath, RangeEnd, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; +use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; use std::collections::Bound; @@ -409,6 +409,67 @@ declare_clippy_lint! { "a match on a struct that binds all fields but still uses the wildcard pattern" } +declare_clippy_lint! { + /// **What it does:** Lint for redundant pattern matching over `Result` or + /// `Option` + /// + /// **Why is this bad?** It's more concise and clear to just use the proper + /// utility function + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// if let Ok(_) = Ok::(42) {} + /// if let Err(_) = Err::(42) {} + /// if let None = None::<()> {} + /// if let Some(_) = Some(42) {} + /// match Ok::(42) { + /// Ok(_) => true, + /// Err(_) => false, + /// }; + /// ``` + /// + /// The more idiomatic use would be: + /// + /// ```rust + /// if Ok::(42).is_ok() {} + /// if Err::(42).is_err() {} + /// if None::<()>.is_none() {} + /// if Some(42).is_some() {} + /// Ok::(42).is_ok(); + /// ``` + pub REDUNDANT_PATTERN_MATCHING, + style, + "use the proper utility function avoiding an `if let`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `match` expressions producing a `bool` that could be written using `matches!` + /// + /// **Why is this bad?** Readability and needless complexity. + /// + /// **Known problems:** This can turn an intentionally exhaustive match into a non-exhaustive one. + /// + /// **Example:** + /// ```rust + /// let x = Some(5); + /// + /// // Bad + /// let a = match x { + /// Some(0) => true, + /// _ => false, + /// }; + /// + /// // Good + /// let a = matches!(x, Some(5)); + /// ``` + pub MATCH_LIKE_MATCHES_MACRO, + style, + "a match that could be written with the matches! macro" +} + #[derive(Default)] pub struct Matches { infallible_destructuring_match_linted: bool, @@ -427,7 +488,9 @@ impl_lint_pass!(Matches => [ WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, INFALLIBLE_DESTRUCTURING_MATCH, - REST_PAT_IN_FULLY_BOUND_STRUCTS + REST_PAT_IN_FULLY_BOUND_STRUCTS, + REDUNDANT_PATTERN_MATCHING, + MATCH_LIKE_MATCHES_MACRO ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -435,6 +498,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if in_external_macro(cx.sess(), expr.span) { return; } + + if !redundant_pattern_match::check(cx, expr) { + check_match_like_matches(cx, expr); + } + if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr); @@ -802,13 +870,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // Some simple checks for exhaustive patterns. // There is a room for improvements to detect more cases, // but it can be more expensive to do so. - let is_pattern_exhaustive = |pat: &&Pat<'_>| { - if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { - true - } else { - false - } - }; + let is_pattern_exhaustive = + |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); if patterns.iter().all(is_pattern_exhaustive) { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } @@ -989,6 +1052,78 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { } } +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), + MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), + _ => return, + } + } +} + +/// Lint a `match` or desugared `if let` for replacement by `matches!` +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) { + if_chain! { + if arms.len() == 2; + if cx.tables().expr_ty(expr).is_bool(); + if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); + if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); + if first != second; + then { + let mut applicability = Applicability::MachineApplicable; + + let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard { + format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability)) + } else { + format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability)) + }; + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }), + "try this", + format!( + "{}matches!({}, {})", + if first { "" } else { "!" }, + snippet_with_applicability(cx, ex.span, "..", &mut applicability), + pat_and_guard, + ), + applicability, + ) + } + } +} + +/// Extract a `bool` or `{ bool }` +fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { + match ex { + ExprKind::Lit(Spanned { + node: LitKind::Bool(b), .. + }) => Some(*b), + ExprKind::Block( + rustc_hir::Block { + stmts: &[], + expr: Some(exp), + .. + }, + _, + ) if desugared => { + if let ExprKind::Lit(Spanned { + node: LitKind::Bool(b), .. + }) = exp.kind + { + Some(b) + } else { + None + } + }, + _ => None, + } +} + fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; @@ -1179,10 +1314,7 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool { // Checks if arm has the form `None => None` fn is_none_arm(arm: &Arm<'_>) -> bool { - match arm.pat.kind { - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true, - _ => false, - } + matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE)) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) @@ -1293,6 +1425,239 @@ where None } +mod redundant_pattern_match { + use super::REDUNDANT_PATTERN_MATCHING; + use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; + use if_chain::if_chain; + use rustc_ast::ast::LitKind; + use rustc_errors::Applicability; + use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; + use rustc_lint::LateContext; + use rustc_middle::ty; + use rustc_mir::const_eval::is_const_fn; + use rustc_span::source_map::Symbol; + + pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), + MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), + _ => false, + } + } else { + false + } + } + + fn find_sugg_for_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + keyword: &'static str, + ) -> bool { + fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { + if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { + return Some("is_ok()"); + } + if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { + return Some("is_err()"); + } + if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { + return Some("is_some()"); + } + if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { + return Some("is_none()"); + } + None + } + + let hir_id = expr.hir_id; + let good_method = match arms[0].pat.kind { + PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + if let PatKind::Wild = patterns[0].kind { + find_suggestion(cx, hir_id, path) + } else { + None + } + }, + PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), + _ => None, + }; + let good_method = match good_method { + Some(method) => method, + None => return false, + }; + + // check that `while_let_on_iterator` lint does not trigger + if_chain! { + if keyword == "while"; + if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; + if method_path.ident.name == sym!(next); + if match_trait_method(cx, op, &paths::ITERATOR); + then { + return false; + } + } + + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + arms[0].pat.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let expr_span = expr.span; + + // while let ... = ... { ... } + // ^^^ + let op_span = op.span.source_callsite(); + + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^ + let span = expr_span.until(op_span.shrink_to_hi()); + diag.span_suggestion( + span, + "try this", + format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), + Applicability::MachineApplicable, // snippet + ); + }, + ); + true + } + + fn find_sugg_for_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + ) -> bool { + if arms.len() == 2 { + let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + + let hir_id = expr.hir_id; + let found_good_method = match node_pair { + ( + PatKind::TupleStruct(ref path_left, ref patterns_left, _), + PatKind::TupleStruct(ref path_right, ref patterns_right, _), + ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::RESULT_OK, + &paths::RESULT_ERR, + "is_ok()", + "is_err()", + || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), + || can_suggest(cx, hir_id, sym!(result_type), "is_err"), + ) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) + if patterns.len() == 1 => + { + if let PatKind::Wild = patterns[0].kind { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::OPTION_SOME, + &paths::OPTION_NONE, + "is_some()", + "is_none()", + || can_suggest(cx, hir_id, sym!(option_type), "is_some"), + || can_suggest(cx, hir_id, sym!(option_type), "is_none"), + ) + } else { + None + } + }, + _ => None, + }; + + if let Some(good_method) = found_good_method { + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + expr.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + let span = expr.span.to(op.span); + diag.span_suggestion( + span, + "try this", + format!("{}.{}", snippet(cx, op.span, "_"), good_method), + Applicability::MaybeIncorrect, // snippet + ); + }, + ); + return true; + } + } + false + } + + #[allow(clippy::too_many_arguments)] + fn find_good_method_for_match<'a>( + arms: &[Arm<'_>], + path_left: &QPath<'_>, + path_right: &QPath<'_>, + expected_left: &[&str], + expected_right: &[&str], + should_be_left: &'a str, + should_be_right: &'a str, + can_suggest_left: impl Fn() -> bool, + can_suggest_right: impl Fn() -> bool, + ) -> Option<&'a str> { + let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + (&(*arms[0].body).kind, &(*arms[1].body).kind) + } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + (&(*arms[1].body).kind, &(*arms[0].body).kind) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), + _ => None, + }, + _ => None, + } + } + + fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { + if !in_constant(cx, hir_id) { + return true; + } + + // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. + cx.tcx + .get_diagnostic_item(diag_item) + .and_then(|def_id| { + cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .find_map(|item| match item.kind { + ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), + _ => None, + }) + }) + }) + .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) + } +} + #[test] fn test_overlapping() { use rustc_span::source_map::DUMMY_SP; diff --git a/clippy_lints/src/redundant_pattern_matching.rs b/clippy_lints/src/redundant_pattern_matching.rs deleted file mode 100644 index d8d16efb978..00000000000 --- a/clippy_lints/src/redundant_pattern_matching.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_mir::const_eval::is_const_fn; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Symbol; - -declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result` or - /// `Option` - /// - /// **Why is this bad?** It's more concise and clear to just use the proper - /// utility function - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// if let Ok(_) = Ok::(42) {} - /// if let Err(_) = Err::(42) {} - /// if let None = None::<()> {} - /// if let Some(_) = Some(42) {} - /// match Ok::(42) { - /// Ok(_) => true, - /// Err(_) => false, - /// }; - /// ``` - /// - /// The more idiomatic use would be: - /// - /// ```rust - /// if Ok::(42).is_ok() {} - /// if Err::(42).is_err() {} - /// if None::<()>.is_none() {} - /// if Some(42).is_some() {} - /// Ok::(42).is_ok(); - /// ``` - pub REDUNDANT_PATTERN_MATCHING, - style, - "use the proper utility function avoiding an `if let`" -} - -declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]); - -impl<'tcx> LateLintPass<'tcx> for RedundantPatternMatching { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { - match match_source { - MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), - MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), - MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), - _ => return, - } - } - } -} - -fn find_sugg_for_if_let<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], - keyword: &'static str, -) { - fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { - if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { - return Some("is_ok()"); - } - if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { - return Some("is_err()"); - } - if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { - return Some("is_some()"); - } - if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { - return Some("is_none()"); - } - None - } - - let hir_id = expr.hir_id; - let good_method = match arms[0].pat.kind { - PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { - find_suggestion(cx, hir_id, path) - } else { - None - } - }, - PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), - _ => None, - }; - let good_method = match good_method { - Some(method) => method, - None => return, - }; - - // check that `while_let_on_iterator` lint does not trigger - if_chain! { - if keyword == "while"; - if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; - if method_path.ident.name == sym!(next); - if match_trait_method(cx, op, &paths::ITERATOR); - then { - return; - } - } - - span_lint_and_then( - cx, - REDUNDANT_PATTERN_MATCHING, - arms[0].pat.span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |diag| { - // while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - let expr_span = expr.span; - - // while let ... = ... { ... } - // ^^^ - let op_span = op.span.source_callsite(); - - // while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^ - let span = expr_span.until(op_span.shrink_to_hi()); - diag.span_suggestion( - span, - "try this", - format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), - Applicability::MachineApplicable, // snippet - ); - }, - ); -} - -fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - - let hir_id = expr.hir_id; - let found_good_method = match node_pair { - ( - PatKind::TupleStruct(ref path_left, ref patterns_left, _), - PatKind::TupleStruct(ref path_right, ref patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { - find_good_method_for_match( - arms, - path_left, - path_right, - &paths::RESULT_OK, - &paths::RESULT_ERR, - "is_ok()", - "is_err()", - || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), - || can_suggest(cx, hir_id, sym!(result_type), "is_err"), - ) - } else { - None - } - }, - (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) - if patterns.len() == 1 => - { - if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( - arms, - path_left, - path_right, - &paths::OPTION_SOME, - &paths::OPTION_NONE, - "is_some()", - "is_none()", - || can_suggest(cx, hir_id, sym!(option_type), "is_some"), - || can_suggest(cx, hir_id, sym!(option_type), "is_none"), - ) - } else { - None - } - }, - _ => None, - }; - - if let Some(good_method) = found_good_method { - span_lint_and_then( - cx, - REDUNDANT_PATTERN_MATCHING, - expr.span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |diag| { - let span = expr.span.to(op.span); - diag.span_suggestion( - span, - "try this", - format!("{}.{}", snippet(cx, op.span, "_"), good_method), - Applicability::MaybeIncorrect, // snippet - ); - }, - ); - } - } -} - -#[allow(clippy::too_many_arguments)] -fn find_good_method_for_match<'a>( - arms: &[Arm<'_>], - path_left: &QPath<'_>, - path_right: &QPath<'_>, - expected_left: &[&str], - expected_right: &[&str], - should_be_left: &'a str, - should_be_right: &'a str, - can_suggest_left: impl Fn() -> bool, - can_suggest_right: impl Fn() -> bool, -) -> Option<&'a str> { - let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { - (&(*arms[0].body).kind, &(*arms[1].body).kind) - } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { - (&(*arms[1].body).kind, &(*arms[0].body).kind) - } else { - return None; - }; - - match body_node_pair { - (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { - (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), - (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), - _ => None, - }, - _ => None, - } -} - -fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { - if !in_constant(cx, hir_id) { - return true; - } - - // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. - cx.tcx - .get_diagnostic_item(diag_item) - .and_then(|def_id| { - cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { - cx.tcx - .associated_items(*imp) - .in_definition_order() - .find_map(|item| match item.kind { - ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), - _ => None, - }) - }) - }) - .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..888b4755484 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1179,6 +1179,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, + Lint { + name: "match_like_matches_macro", + group: "style", + desc: "a match that could be written with the matches! macro", + deprecation: None, + module: "matches", + }, Lint { name: "match_on_vec_items", group: "pedantic", @@ -1856,7 +1863,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "use the proper utility function avoiding an `if let`", deprecation: None, - module: "redundant_pattern_matching", + module: "matches", }, Lint { name: "redundant_pub_crate", diff --git a/tests/ui/find_map.rs b/tests/ui/find_map.rs index c28cca144ca..88d3b0e7490 100644 --- a/tests/ui/find_map.rs +++ b/tests/ui/find_map.rs @@ -19,6 +19,7 @@ fn main() { let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); + #[allow(clippy::match_like_matches_macro)] let _: Option = desserts_of_the_week .iter() .find(|dessert| match *dessert { diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr index 92f40fe6f1f..f279850fef8 100644 --- a/tests/ui/find_map.stderr +++ b/tests/ui/find_map.stderr @@ -8,7 +8,7 @@ LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s = help: this is more succinctly expressed by calling `.find_map(..)` instead error: called `find(p).map(q)` on an `Iterator` - --> $DIR/find_map.rs:22:29 + --> $DIR/find_map.rs:23:29 | LL | let _: Option = desserts_of_the_week | _____________________________^ diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed new file mode 100644 index 00000000000..2d1ac8836d6 --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -0,0 +1,32 @@ +// run-rustfix + +#![warn(clippy::match_like_matches_macro)] + +fn main() { + let x = Some(5); + + // Lint + let _y = matches!(x, Some(0)); + + // Turn into is_none + let _z = x.is_none(); + + // Lint + let _z = !matches!(x, Some(r) if r == 0); + + // Lint + let _zz = matches!(x, Some(5)); + + // No lint + let _a = match x { + Some(_) => false, + None => false, + }; + + // No lint + let _a = match x { + Some(0) => false, + Some(_) => true, + None => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs new file mode 100644 index 00000000000..376abf9244e --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::match_like_matches_macro)] + +fn main() { + let x = Some(5); + + // Lint + let _y = match x { + Some(0) => true, + _ => false, + }; + + // Turn into is_none + let _z = match x { + Some(_) => false, + None => true, + }; + + // Lint + let _z = match x { + Some(r) if r == 0 => false, + _ => true, + }; + + // Lint + let _zz = if let Some(5) = x { true } else { false }; + + // No lint + let _a = match x { + Some(_) => false, + None => false, + }; + + // No lint + let _a = match x { + Some(0) => false, + Some(_) => true, + None => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr new file mode 100644 index 00000000000..0b32af039a8 --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -0,0 +1,42 @@ +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:9:14 + | +LL | let _y = match x { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(x, Some(0))` + | + = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_expr_like_matches_macro.rs:15:14 + | +LL | let _z = match x { + | ______________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `x.is_none()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:21:14 + | +LL | let _z = match x { + | ______________^ +LL | | Some(r) if r == 0 => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` + +error: if let .. else expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:27:15 + | +LL | let _zz = if let Some(5) = x { true } else { false }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/neg_cmp_op_on_partial_ord.rs b/tests/ui/neg_cmp_op_on_partial_ord.rs index ca70e3b7148..0cee0a28fc7 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.rs +++ b/tests/ui/neg_cmp_op_on_partial_ord.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; -#[allow(clippy::unnested_or_patterns)] +#[allow(clippy::unnested_or_patterns, clippy::match_like_matches_macro)] #[warn(clippy::neg_cmp_op_on_partial_ord)] fn main() { let a_value = 1.0; diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 11dff94a288..bd13cf1bdfa 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -23,10 +23,7 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - match *self { - SeemsOption::None => true, - SeemsOption::Some(_) => false, - } + matches!(*self, SeemsOption::None) } } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 1d0ee82b4f7..94479e68555 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -25,10 +25,7 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - match *self { - SeemsOption::None => true, - SeemsOption::Some(_) => false, - } + matches!(*self, SeemsOption::None) } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 502615fb175..be323035d6c 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:50:9 + --> $DIR/question_mark.rs:47:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:54:9 + --> $DIR/question_mark.rs:51:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:58:17 + --> $DIR/question_mark.rs:55:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:64:17 + --> $DIR/question_mark.rs:61:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:81:9 + --> $DIR/question_mark.rs:78:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:89:9 + --> $DIR/question_mark.rs:86:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:97:9 + --> $DIR/question_mark.rs:94:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:104:26 + --> $DIR/question_mark.rs:101:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:114:17 + --> $DIR/question_mark.rs:111:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:129:5 + --> $DIR/question_mark.rs:126:5 | LL | / if f().is_none() { LL | | return None; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 8b4e2d21331..ce8582d2b22 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -2,7 +2,13 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] fn main() { if Ok::(42).is_ok() {} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index b0904e41b6f..a3a9aa40e3b 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -2,7 +2,13 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] fn main() { if let Ok(_) = Ok::(42) {} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 51a6f4350d3..25d1476062e 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:8:12 + --> $DIR/redundant_pattern_matching.rs:14:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` @@ -7,67 +7,67 @@ LL | if let Ok(_) = Ok::(42) {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:10:12 + --> $DIR/redundant_pattern_matching.rs:16:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:12:12 + --> $DIR/redundant_pattern_matching.rs:18:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:20:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:22:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:22:15 + --> $DIR/redundant_pattern_matching.rs:28:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:24:15 + --> $DIR/redundant_pattern_matching.rs:30:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:26:15 + --> $DIR/redundant_pattern_matching.rs:32:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:34:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:36:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:33:15 + --> $DIR/redundant_pattern_matching.rs:39:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 + --> $DIR/redundant_pattern_matching.rs:55:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:54:5 + --> $DIR/redundant_pattern_matching.rs:60:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +85,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:59:5 + --> $DIR/redundant_pattern_matching.rs:65:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:64:5 + --> $DIR/redundant_pattern_matching.rs:70:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +103,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:69:5 + --> $DIR/redundant_pattern_matching.rs:75:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:74:5 + --> $DIR/redundant_pattern_matching.rs:80:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:79:13 + --> $DIR/redundant_pattern_matching.rs:85:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,61 +131,61 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:84:20 + --> $DIR/redundant_pattern_matching.rs:90:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:87:20 + --> $DIR/redundant_pattern_matching.rs:93:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:99:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:95:19 + --> $DIR/redundant_pattern_matching.rs:101:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:97:19 + --> $DIR/redundant_pattern_matching.rs:103:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:99:19 + --> $DIR/redundant_pattern_matching.rs:105:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:132:19 + --> $DIR/redundant_pattern_matching.rs:138:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:133:16 + --> $DIR/redundant_pattern_matching.rs:139:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:12 + --> $DIR/redundant_pattern_matching.rs:145:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:140:15 + --> $DIR/redundant_pattern_matching.rs:146:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` diff --git a/tests/ui/redundant_pattern_matching_const_result.fixed b/tests/ui/redundant_pattern_matching_const_result.fixed index 8a81e92f04a..de3fe00d5fa 100644 --- a/tests/ui/redundant_pattern_matching_const_result.fixed +++ b/tests/ui/redundant_pattern_matching_const_result.fixed @@ -2,7 +2,7 @@ #![feature(const_result)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused)] +#![allow(clippy::match_like_matches_macro, unused)] // Test that results are linted with the feature enabled. diff --git a/tests/ui/redundant_pattern_matching_const_result.rs b/tests/ui/redundant_pattern_matching_const_result.rs index 1cd515441d1..b77969d53d9 100644 --- a/tests/ui/redundant_pattern_matching_const_result.rs +++ b/tests/ui/redundant_pattern_matching_const_result.rs @@ -2,7 +2,7 @@ #![feature(const_result)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused)] +#![allow(clippy::match_like_matches_macro, unused)] // Test that results are linted with the feature enabled. -- cgit 1.4.1-3-g733a5 From 0c8afa39ce8b2e7f0f9be7fc33fe3993d9bc3c53 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 1 Jun 2020 18:32:52 -0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3b2e46a9a85..9f241c2c3a2 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -293,13 +293,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } -fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument - if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) { // TODO: need more specific check. this is too wide. remember also to include tests if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { return; } @@ -328,7 +328,7 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } -fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { +fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if let ExprKind::Binary( Spanned { node: BinOpKind::Add, .. @@ -350,11 +350,11 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { // check if expression of the form x.powi(2) + y.powi(2) if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind; - if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs, _) = add_lhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs, _) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]); - if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]); + if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]); + if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]); if Int(2) == lvalue && Int(2) == rvalue; then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); @@ -365,7 +365,7 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { None } -fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { if let Some(message) = detect_hypot(cx, args) { span_lint_and_sugg( cx, @@ -431,7 +431,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { ) = &expr.kind { if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind { if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { return; } @@ -573,6 +573,50 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind; + then { + return method_name_a.as_str() == method_name_b.as_str() && + args_a.len() == args_b.len() && + ( + ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || + method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + ); + } + } + + false +} + +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { + // check if expression of the form x.logN() / y.logN() + if_chain! { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + lhs, + rhs, + ) = &expr.kind; + if are_same_base_logs(cx, lhs, rhs); + if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind; + if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind; + then { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "division of logarithms can be calculated more efficiently and accurately", + "consider using", + format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), + Applicability::MachineApplicable, + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { @@ -592,6 +636,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { check_expm1(cx, expr); check_mul_add(cx, expr); check_custom_abs(cx, expr); + check_log_division(cx, expr); } } } diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed new file mode 100644 index 00000000000..13962a272d4 --- /dev/null +++ b/tests/ui/floating_point_logbase.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let x = 3f32; + let y = 5f32; + let _ = x.log(y); + let _ = x.log(y); + let _ = x.log(y); + let _ = x.log(y); + // Cases where the lint shouldn't be applied + let _ = x.ln() / y.powf(3.2); + let _ = x.powf(3.2) / y.powf(3.2); + let _ = x.powf(3.2) / y.ln(); + let _ = x.log(5f32) / y.log(7f32); +} diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs new file mode 100644 index 00000000000..26bc20d5370 --- /dev/null +++ b/tests/ui/floating_point_logbase.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let x = 3f32; + let y = 5f32; + let _ = x.ln() / y.ln(); + let _ = x.log2() / y.log2(); + let _ = x.log10() / y.log10(); + let _ = x.log(5f32) / y.log(5f32); + // Cases where the lint shouldn't be applied + let _ = x.ln() / y.powf(3.2); + let _ = x.powf(3.2) / y.powf(3.2); + let _ = x.powf(3.2) / y.ln(); + let _ = x.log(5f32) / y.log(7f32); +} diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr new file mode 100644 index 00000000000..fa956b9139e --- /dev/null +++ b/tests/ui/floating_point_logbase.stderr @@ -0,0 +1,28 @@ +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:7:13 + | +LL | let _ = x.ln() / y.ln(); + | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:8:13 + | +LL | let _ = x.log2() / y.log2(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:9:13 + | +LL | let _ = x.log10() / y.log10(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:10:13 + | +LL | let _ = x.log(5f32) / y.log(5f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 1740dda76386aff7205b2a709a32c95e8cbc0d57 Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 Jul 2020 22:11:19 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index bc6e868823f..3d48bf739eb 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -237,6 +237,7 @@ fn is_commutative(op: hir::BinOpKind) -> bool { use rustc_hir::BinOpKind::{ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, }; + #[allow(clippy::match_like_matches_macro)] match op { Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true, Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false, diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 26476af4cb6..25ccabc1c88 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -122,8 +122,5 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } fn kind_is_cmp(kind: BinOpKind) -> bool { - match kind { - BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true, - _ => false, - } + matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index ca921dcfdfe..7839908fe4c 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -214,20 +214,20 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } fn is_valid_operator(op: BinOp) -> bool { - match op.node { + matches!( + op.node, BinOpKind::Sub - | BinOpKind::Div - | BinOpKind::Eq - | BinOpKind::Lt - | BinOpKind::Le - | BinOpKind::Gt - | BinOpKind::Ge - | BinOpKind::Ne - | BinOpKind::And - | BinOpKind::Or - | BinOpKind::BitXor - | BinOpKind::BitAnd - | BinOpKind::BitOr => true, - _ => false, - } + | BinOpKind::Div + | BinOpKind::Eq + | BinOpKind::Lt + | BinOpKind::Le + | BinOpKind::Gt + | BinOpKind::Ge + | BinOpKind::Ne + | BinOpKind::And + | BinOpKind::Or + | BinOpKind::BitXor + | BinOpKind::BitAnd + | BinOpKind::BitOr + ) } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index d40cdfcca9f..32fc01149d8 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -105,10 +105,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { _ => return false, } - match map.find(map.get_parent_node(id)) { - Some(Node::Param(_)) => true, - _ => false, - } + matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_))) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index ceed6a74c4f..fb26b9fc27d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -175,10 +175,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { match (&lhs.kind, &rhs.kind) { (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), - (l, r) => match (l, r) { - (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false, - (_, _) => true, - }, + (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 156246fb8bb..1bd16e6cce5 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -305,18 +305,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { } fn is_block(expr: &Expr) -> bool { - if let ExprKind::Block(..) = expr.kind { - true - } else { - false - } + matches!(expr.kind, ExprKind::Block(..)) } /// Check if the expression is an `if` or `if let` fn is_if(expr: &Expr) -> bool { - if let ExprKind::If(..) = expr.kind { - true - } else { - false - } + matches!(expr.kind, ExprKind::If(..)) } diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 3f030dd8422..63133a4872a 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -645,13 +645,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { use hir::ExprKind::{Field, Index, Path}; match e.kind { - Path(ref qpath) => { - if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) { - false - } else { - true - } - }, + Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)), Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner), _ => false, } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..c4f1af8f4e4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -277,7 +277,6 @@ mod question_mark; mod ranges; mod redundant_clone; mod redundant_field_names; -mod redundant_pattern_matching; mod redundant_pub_crate; mod redundant_static_lifetimes; mod reference; @@ -623,11 +622,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::INFALLIBLE_DESTRUCTURING_MATCH, &matches::MATCH_AS_REF, &matches::MATCH_BOOL, + &matches::MATCH_LIKE_MATCHES_MACRO, &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, &matches::MATCH_SINGLE_BINDING, &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, + &matches::REDUNDANT_PATTERN_MATCHING, &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, &matches::SINGLE_MATCH, &matches::SINGLE_MATCH_ELSE, @@ -757,7 +758,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_field_names::REDUNDANT_FIELD_NAMES, - &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, &reference::DEREF_ADDROF, @@ -956,7 +956,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); store.register_late_pass(|| box if_let_some_result::OkIfLet); - store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); let enum_variant_size_threshold = conf.enum_variant_size_threshold; @@ -1295,9 +1294,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_AS_REF), + LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_SINGLE_BINDING), + LintId::of(&matches::REDUNDANT_PATTERN_MATCHING), LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), @@ -1387,7 +1388,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), @@ -1488,8 +1488,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), + LintId::of(&matches::REDUNDANT_PATTERN_MATCHING), LintId::of(&matches::SINGLE_MATCH), LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), @@ -1526,7 +1528,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::PTR_ARG), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index a79f94855bd..168f9f953e4 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -129,10 +129,10 @@ fn check_fn_inner<'tcx>( } let mut bounds_lts = Vec::new(); - let types = generics.params.iter().filter(|param| match param.kind { - GenericParamKind::Type { .. } => true, - _ => false, - }); + let types = generics + .params + .iter() + .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { for bound in typ.bounds { let mut visitor = RefVisitor::new(cx); @@ -337,10 +337,10 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { if let Some(ref last_path_segment) = last_path_segment(qpath).args { if !last_path_segment.parenthesized - && !last_path_segment.args.iter().any(|arg| match arg { - GenericArg::Lifetime(_) => true, - _ => false, - }) + && !last_path_segment + .args + .iter() + .any(|arg| matches!(arg, GenericArg::Lifetime(_))) { let hir_id = ty.hir_id; match self.cx.qpath_res(qpath, hir_id) { diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index b803d753b6d..396bb659109 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2091,17 +2091,11 @@ fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } fn is_loop(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Loop(..) => true, - _ => false, - } + matches!(expr.kind, ExprKind::Loop(..)) } fn is_conditional(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Match(..) => true, - _ => false, - } + matches!(expr.kind, ExprKind::Match(..)) } fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f1c8894c0ee..4c595029ff7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1844,10 +1844,10 @@ fn lint_expect_fun_call( ty::Ref(ty::ReStatic, ..) ) }), - hir::ExprKind::Path(ref p) => match cx.qpath_res(p, arg.hir_id) { - hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true, - _ => false, - }, + hir::ExprKind::Path(ref p) => matches!( + cx.qpath_res(p, arg.hir_id), + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) + ), _ => false, } } @@ -2028,13 +2028,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp .tables() .expr_adjustments(arg) .iter() - .filter(|adj| { - if let ty::adjustment::Adjust::Deref(_) = adj.kind { - true - } else { - false - } - }) + .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) .count(); let derefs: String = iter::repeat('*').take(deref_count).collect(); snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 3d4225f36a7..400f4b609af 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -694,12 +694,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::MacroKind; if expr.span.from_expansion() { let data = expr.span.ctxt().outer_expn_data(); - - if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind { - true - } else { - false - } + matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _)) } else { false } diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index ad39e59d067..b84a1a3fe24 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -641,28 +641,22 @@ fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { ); } - #[allow(clippy::trivially_copy_pass_by_ref)] - fn is_wild>(pat: &&P) -> bool { - if let PatKind::Wild = pat.kind { - true - } else { - false - } - } - if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { if let Some((left_index, left_pat)) = patterns[..rest_index] .iter() .rev() - .take_while(is_wild) + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) .enumerate() .last() { span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); } - if let Some((right_index, right_pat)) = - patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last() + if let Some((right_index, right_pat)) = patterns[rest_index + 1..] + .iter() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() { span_lint( cx, diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index bf80b62afe6..9c962673537 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -71,10 +71,11 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp fn is_executable(cx: &LateContext<'_>) -> bool { use rustc_session::config::CrateType; - cx.tcx.sess.crate_types().iter().any(|t: &CrateType| match t { - CrateType::Executable => true, - _ => false, - }) + cx.tcx + .sess + .crate_types() + .iter() + .any(|t: &CrateType| matches!(t, CrateType::Executable)) } declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2597f5f6f17..621ebdef2f0 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -80,10 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // can't be implemented for unsafe new return; } - if impl_item.generics.params.iter().any(|gen| match gen.kind { - hir::GenericParamKind::Type { .. } => true, - _ => false, - }) { + if impl_item + .generics + .params + .iter() + .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. })) + { // when the result of `new()` depends on a type parameter we should not require // an // impl of `Default` diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index c11a2ff9ee0..a3521c31a6b 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -238,10 +238,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let ty = if needs_check_adjustment { let adjustments = cx.tables().expr_adjustments(dereferenced_expr); - if let Some(i) = adjustments.iter().position(|adj| match adj.kind { - Adjust::Borrow(_) | Adjust::Deref(_) => true, - _ => false, - }) { + if let Some(i) = adjustments + .iter() + .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_))) + { if i == 0 { cx.tables().expr_ty(dereferenced_expr) } else { diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 7dce23dd223..04be96aa64c 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -148,17 +148,11 @@ fn is_arith_expr(expr: &Expr) -> bool { #[must_use] fn is_bit_op(op: BinOpKind) -> bool { use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr}; - match op { - BitXor | BitAnd | BitOr | Shl | Shr => true, - _ => false, - } + matches!(op, BitXor | BitAnd | BitOr | Shl | Shr) } #[must_use] fn is_arith_op(op: BinOpKind) -> bool { use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub}; - match op { - Add | Sub | Mul | Div | Rem => true, - _ => false, - } + matches!(op, Add | Sub | Mul | Div | Rem) } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index d8c8eff2c85..f204a0ffb2c 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -99,12 +99,7 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { use regex_syntax::hir::Anchor::{EndText, StartText}; use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal}; - let is_literal = |e: &[regex_syntax::hir::Hir]| { - e.iter().all(|e| match *e.kind() { - Literal(_) => true, - _ => false, - }) - }; + let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_))); match *s.kind() { Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"), diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 4cdff63f118..194786c5c41 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -165,10 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { let var_ty = cx.tables().node_type_opt(pat_id); - var_ty.map_or(false, |var_ty| match var_ty.kind { - ty::Adt(..) => false, - _ => true, - }) + var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..))) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 509bbfd27c1..1aeff1baa36 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -25,13 +25,7 @@ declare_clippy_lint! { fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match &expr.kind { ExprKind::Struct(..) | ExprKind::Tup(..) => true, - ExprKind::Path(qpath) => { - if let Res::Def(DefKind::Const, ..) = cx.qpath_res(qpath, expr.hir_id) { - true - } else { - false - } - }, + ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)), _ => false, } } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index d6f31a99bb3..71207caecf5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -775,11 +775,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { .iter() .filter(|arg| { if is_unit(cx.tables().expr_ty(arg)) && !is_unit_literal(arg) { - if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind { - false - } else { - true - } + !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) } else { false } @@ -899,17 +895,11 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { } fn is_unit(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Tuple(slice) if slice.is_empty() => true, - _ => false, - } + matches!(ty.kind, ty::Tuple(slice) if slice.is_empty()) } fn is_unit_literal(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Tup(ref slice) if slice.is_empty() => true, - _ => false, - } + matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) } declare_clippy_lint! { @@ -1154,10 +1144,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { } fn is_isize_or_usize(typ: Ty<'_>) -> bool { - match typ.kind { - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true, - _ => false, - } + matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { @@ -1737,10 +1724,10 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::TraitObject(ref param_bounds, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { - bound.bound_generic_params.iter().any(|gen| match gen.kind { - GenericParamKind::Lifetime { .. } => true, - _ => false, - }) + bound + .bound_generic_params + .iter() + .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) }); if has_lifetime_parameters { // complex trait bounds like A<'a, 'b> diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index b9aa202b328..25d136e564d 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -58,10 +58,10 @@ declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COM impl LateLintPass<'_> for UnnamedAddress { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_comparison(binop: BinOpKind) -> bool { - match binop { - BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true, - _ => false, - } + matches!( + binop, + BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt + ) } fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { @@ -72,11 +72,7 @@ impl LateLintPass<'_> for UnnamedAddress { } fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ty::FnDef(..) = cx.tables().expr_ty(expr).kind { - true - } else { - false - } + matches!(cx.tables().expr_ty(expr).kind, ty::FnDef(..)) } if_chain! { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 39a8c020872..776c6bc57ca 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -169,10 +169,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; let should_check = parameters.as_ref().map_or( true, - |params| !params.parenthesized && !params.args.iter().any(|arg| match arg { - GenericArg::Lifetime(_) => true, - _ => false, - }) + |params| !params.parenthesized + &&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) ); if should_check { diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index e19a79dd8da..58c1103da9f 100755 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -387,10 +387,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { - match (l, r) { - (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true, - _ => false, - } + matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))) } pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a3b79925ff..0b4cba3fc42 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -102,11 +102,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { #[must_use] pub fn in_macro(span: Span) -> bool { if span.from_expansion() { - if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind { - false - } else { - true - } + !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) } else { false } @@ -127,10 +123,7 @@ pub fn is_present_in_source(cx: &T, span: Span) -> bool { /// Checks if given pattern is a wildcard (`_`) pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { - match pat.kind { - PatKind::Wild => true, - _ => false, - } + matches!(pat.kind, PatKind::Wild) } /// Checks if type is struct, enum or union type with the given def path. diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 7a79741b30b..87cb454f654 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -51,7 +51,7 @@ impl<'a> NumericLiteral<'a> { pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) { let (unsuffixed, suffix) = split_suffix(&src, lit_kind); - let float = if let LitKind::Float(..) = lit_kind { true } else { false }; + let float = matches!(lit_kind, LitKind::Float(..)); Some(NumericLiteral::new(unsuffixed, suffix, float)) } else { None diff --git a/src/driver.rs b/src/driver.rs index decd3a79cce..47315fa64cd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -382,13 +382,8 @@ pub fn main() { let should_describe_lints = || { let args: Vec<_> = env::args().collect(); - args.windows(2).any(|args| { - args[1] == "help" - && match args[0].as_str() { - "-W" | "-A" | "-D" | "-F" => true, - _ => false, - } - }) + args.windows(2) + .any(|args| args[1] == "help" && matches!(args[0].as_str(), "-W" | "-A" | "-D" | "-F")) }; if !wrapper_mode && should_describe_lints() { -- cgit 1.4.1-3-g733a5 From 076ec872ce122403f3c75f20c773c64b194a5891 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Wed, 10 Jun 2020 13:52:00 -0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 9f241c2c3a2..d88e47f396c 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -350,8 +350,18 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { // check if expression of the form x.powi(2) + y.powi(2) if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs, _) = add_lhs.kind; - if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs, _) = add_rhs.kind; + if let ExprKind::MethodCall( + PathSegment { ident: lmethod_name, .. }, + ref _lspan, + ref largs, + _ + ) = add_lhs.kind; + if let ExprKind::MethodCall( + PathSegment { ident: rmethod_name, .. }, + ref _rspan, + ref rargs, + _ + ) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]); if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]); @@ -617,6 +627,55 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + div_lhs, + div_rhs, + ) = &expr.kind; + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + mul_lhs, + mul_rhs, + ) = &div_lhs.kind; + if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs); + if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs); + then { + if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && + (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "conversion to degrees can be done more accurately", + "consider using", + format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")), + Applicability::MachineApplicable, + ); + } else if + (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && + (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "conversion to radians can be done more accurately", + "consider using", + format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { @@ -637,6 +696,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { check_mul_add(cx, expr); check_custom_abs(cx, expr); check_log_division(cx, expr); + check_radians(cx, expr); } } } diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed new file mode 100644 index 00000000000..64461417a6a --- /dev/null +++ b/tests/ui/floating_point_rad.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x.to_degrees(); + let _ = x.to_radians(); + // Cases where the lint shouldn't be applied + let _ = x * 90f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 90f32; + let _ = x * 180f32 / std::f32::consts::E; + let _ = x * std::f32::consts::E / 180f32; +} diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs new file mode 100644 index 00000000000..9046f184b3e --- /dev/null +++ b/tests/ui/floating_point_rad.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x * 180f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 180f32; + // Cases where the lint shouldn't be applied + let _ = x * 90f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 90f32; + let _ = x * 180f32 / std::f32::consts::E; + let _ = x * std::f32::consts::E / 180f32; +} diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr new file mode 100644 index 00000000000..81e81821513 --- /dev/null +++ b/tests/ui/floating_point_rad.stderr @@ -0,0 +1,16 @@ +error: conversion to degrees can be done more accurately + --> $DIR/floating_point_rad.rs:6:13 + | +LL | let _ = x * 180f32 / std::f32::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:7:13 + | +LL | let _ = x * std::f32::consts::PI / 180f32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From f5596826fa59035e6c57d8968df0d61f69c2537b Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 15 Jun 2020 13:55:12 -0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d88e47f396c..b1e258f4b16 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -618,7 +618,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "division of logarithms can be calculated more efficiently and accurately", + "log base can be expressed more clearly", "consider using", format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), Applicability::MachineApplicable, diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr index fa956b9139e..78354c2f62d 100644 --- a/tests/ui/floating_point_logbase.stderr +++ b/tests/ui/floating_point_logbase.stderr @@ -1,4 +1,4 @@ -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:7:13 | LL | let _ = x.ln() / y.ln(); @@ -6,19 +6,19 @@ LL | let _ = x.ln() / y.ln(); | = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:8:13 | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:9:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log(5f32) / y.log(5f32); -- cgit 1.4.1-3-g733a5 From db7bc6b3bd0e58c8fbf7507713a4214299f39c36 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 15 Jun 2020 13:59:44 -0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index b1e258f4b16..beb0b234408 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { { span_lint_and_sugg( cx, - IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS, expr.span, "conversion to degrees can be done more accurately", "consider using", @@ -664,7 +664,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { { span_lint_and_sugg( cx, - IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS, expr.span, "conversion to radians can be done more accurately", "consider using", diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed index 64461417a6a..92480c5db8b 100644 --- a/tests/ui/floating_point_rad.fixed +++ b/tests/ui/floating_point_rad.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs index 9046f184b3e..062e7c3fdc1 100644 --- a/tests/ui/floating_point_rad.rs +++ b/tests/ui/floating_point_rad.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr index 81e81821513..a6ffdca64ee 100644 --- a/tests/ui/floating_point_rad.stderr +++ b/tests/ui/floating_point_rad.stderr @@ -4,7 +4,7 @@ error: conversion to degrees can be done more accurately LL | let _ = x * 180f32 / std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` | - = note: `-D clippy::imprecise-flops` implied by `-D warnings` + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: conversion to radians can be done more accurately --> $DIR/floating_point_rad.rs:7:13 -- cgit 1.4.1-3-g733a5 From 6be9491eace540d341f3b8dbf775a10e25f6431a Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 22 Jun 2020 14:16:27 -0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index beb0b234408..11bd0ae23aa 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -294,37 +294,56 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { - // Check argument if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) { - // TODO: need more specific check. this is too wide. remember also to include tests - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { - if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { - return; + if value == Int(2) { + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(grandparent) = get_parent_expr(cx, parent) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } } } + + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref lhs, + ref rhs, + ) = parent.kind + { + let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + parent.span, + "square can be computed more efficiently", + "consider using", + format!( + "{}.mul_add({}, {})", + Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &other_addend, ".."), + ), + Applicability::MachineApplicable, + ); + + return; + } } - } - let (lint, help, suggestion) = match value { - Int(2) => ( - IMPRECISE_FLOPS, - "square can be computed more accurately", + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "square can be computed more efficiently", + "consider using", format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), - ), - _ => return, - }; - - span_lint_and_sugg( - cx, - lint, - expr.span, - help, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 98766e68aaf..56762400593 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let one = 1; @@ -8,10 +8,12 @@ fn main() { let _ = x * x; let y = 4f32; - let _ = (x * x + y).sqrt(); - let _ = (x + y * y).sqrt(); + let _ = x.mul_add(x, y); + let _ = y.mul_add(y, x); + let _ = x.mul_add(x, y).sqrt(); + let _ = y.mul_add(y, x).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); - let _ = x.hypot(y); + let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 3c4b636a3d8..1f800e4628d 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let one = 1; @@ -8,6 +8,8 @@ fn main() { let _ = x.powi(1 + 1); let y = 4f32; + let _ = x.powi(2) + y; + let _ = x + y.powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index f370e24bf05..d5a5f1bcca1 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,34 +1,40 @@ -error: square can be computed more accurately +error: square can be computed more efficiently --> $DIR/floating_point_powi.rs:7:13 | LL | let _ = x.powi(2); | ^^^^^^^^^ help: consider using: `x * x` | - = note: `-D clippy::imprecise-flops` implied by `-D warnings` + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: square can be computed more accurately +error: square can be computed more efficiently --> $DIR/floating_point_powi.rs:8:13 | LL | let _ = x.powi(1 + 1); | ^^^^^^^^^^^^^ help: consider using: `x * x` -error: square can be computed more accurately - --> $DIR/floating_point_powi.rs:11:14 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:11:13 | -LL | let _ = (x.powi(2) + y).sqrt(); - | ^^^^^^^^^ help: consider using: `x * x` +LL | let _ = x.powi(2) + y; + | ^^^^^^^^^^^^^ 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:12:13 | -LL | let _ = (x + y.powi(2)).sqrt(); - | ^^^^^^^^^ help: consider using: `y * y` +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.mul_add(x, y)` -error: hypotenuse can be computed more accurately - --> $DIR/floating_point_powi.rs:16:13 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:14:13 | -LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` +LL | let _ = (x + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 3065201eb3a0c4976de7ef5b5b924afde1b18325 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 6 Jul 2020 13:14:52 -0300 Subject: Includes TODO for constants equivalent to π/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(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 11bd0ae23aa..3087d6a940a 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -665,6 +665,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs); if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs); then { + // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) { -- cgit 1.4.1-3-g733a5 From 5307cb5614e7498f069bb634ab293e176e63c67f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 4 Jul 2020 22:50:03 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..c5bbaac0df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1616,6 +1616,7 @@ Released 2018-09-13 [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..0f362dbf86b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -282,6 +282,7 @@ mod redundant_pub_crate; mod redundant_static_lifetimes; mod reference; mod regex; +mod repeat_once; mod returns; mod serde_api; mod shadow; @@ -764,6 +765,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, + &repeat_once::REPEAT_ONCE, &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, @@ -1071,6 +1073,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1393,6 +1396,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), @@ -1602,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), + LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs new file mode 100644 index 00000000000..af3c948ec82 --- /dev/null +++ b/clippy_lints/src/repeat_once.rs @@ -0,0 +1,126 @@ +use crate::consts::{miri_to_const, Constant}; +use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. + /// - `.to_string()` for `str` + /// - `.clone()` for `String` + /// - `.to_vec()` for `slice` + /// + /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind thi, `clone()` should be used. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// let x = String::from("hello world").repeat(1); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// let x = String::from("hello world").clone(); + /// } + /// ``` + pub REPEAT_ONCE, + complexity, + "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` " +} + +declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); + +impl<'tcx> LateLintPass<'tcx> for RepeatOnce { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if path.ident.name == sym!(repeat); + if is_once(cx, &args[1]) && !in_macro(args[0].span); + then { + let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0])); + if is_str(ty){ + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on str", + "consider using `.to_string()` instead", + format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_slice(ty) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on slice", + "consider using `.to_vec()` instead", + format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on a string literal", + "consider using `.clone()` instead", + format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } + } + } + } +} + +fn is_once<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { + match expr.kind { + ExprKind::Lit(ref lit) => { + if let LitKind::Int(ref lit_content, _) = lit.node { + *lit_content == 1 + } else { + false + } + }, + ExprKind::Path(rustc_hir::QPath::Resolved(None, path)) => { + if let Res::Def(DefKind::Const, def_id) = path.res { + let ty = cx.tcx.type_of(def_id); + let con = cx + .tcx + .const_eval_poly(def_id) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)) + .unwrap(); + let con = miri_to_const(con); + con == Some(Constant::Int(1)) + } else { + false + } + }, + _ => false, + } +} + +fn is_str(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Str => true, + _ => false, + } +} + +fn is_slice(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Slice(..) | ty::Array(..) => true, + _ => false, + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..078924d3f9b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1879,6 +1879,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "reference", }, + Lint { + name: "repeat_once", + group: "complexity", + desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ", + deprecation: None, + module: "repeat_once", + }, Lint { name: "rest_pat_in_fully_bound_structs", group: "restriction", diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed new file mode 100644 index 00000000000..a637c22fbcd --- /dev/null +++ b/tests/ui/repeat_once.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::repeat_once)] +#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +fn main() { + const N: usize = 1; + let s = "str"; + let string = "String".to_string(); + let slice = [1; 5]; + + let a = [1; 5].to_vec(); + let b = slice.to_vec(); + let c = "hello".to_string(); + let d = "hi".to_string(); + let e = s.to_string(); + let f = string.clone(); +} diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs new file mode 100644 index 00000000000..d99ca1b5b55 --- /dev/null +++ b/tests/ui/repeat_once.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::repeat_once)] +#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +fn main() { + const N: usize = 1; + let s = "str"; + let string = "String".to_string(); + let slice = [1; 5]; + + let a = [1; 5].repeat(1); + let b = slice.repeat(1); + let c = "hello".repeat(N); + let d = "hi".repeat(1); + let e = s.repeat(1); + let f = string.repeat(1); +} diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr new file mode 100644 index 00000000000..915eea3bfc6 --- /dev/null +++ b/tests/ui/repeat_once.stderr @@ -0,0 +1,40 @@ +error: calling `repeat(1)` on slice + --> $DIR/repeat_once.rs:10:13 + | +LL | let a = [1; 5].repeat(1); + | ^^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `[1; 5].to_vec()` + | + = note: `-D clippy::repeat-once` implied by `-D warnings` + +error: calling `repeat(1)` on slice + --> $DIR/repeat_once.rs:11:13 + | +LL | let b = slice.repeat(1); + | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:12:13 + | +LL | let c = "hello".repeat(N); + | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:13:13 + | +LL | let d = "hi".repeat(1); + | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:14:13 + | +LL | let e = s.repeat(1); + | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` + +error: calling `repeat(1)` on a string literal + --> $DIR/repeat_once.rs:15:13 + | +LL | let f = string.repeat(1); + | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 37d75da266443dd4253ceedebd692ba77dd72e03 Mon Sep 17 00:00:00 2001 From: robojumper Date: Wed, 8 Jul 2020 18:04:51 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 3d48bf739eb..bc6e868823f 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -237,7 +237,6 @@ fn is_commutative(op: hir::BinOpKind) -> bool { use rustc_hir::BinOpKind::{ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, }; - #[allow(clippy::match_like_matches_macro)] match op { Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true, Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 34aa2981535..aeabb99a30d 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -446,11 +446,12 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `match` expressions producing a `bool` that could be written using `matches!` + /// **What it does:** Checks for `match` or `if let` expressions producing a + /// `bool` that could be written using `matches!` /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** This can turn an intentionally exhaustive match into a non-exhaustive one. + /// **Known problems:** None /// /// **Example:** /// ```rust @@ -462,8 +463,14 @@ declare_clippy_lint! { /// _ => false, /// }; /// + /// let a = if let Some(0) = x { + /// true + /// } else { + /// false + /// }; + /// /// // Good - /// let a = matches!(x, Some(5)); + /// let a = matches!(x, Some(0)); /// ``` pub MATCH_LIKE_MATCHES_MACRO, style, @@ -499,9 +506,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { return; } - if !redundant_pattern_match::check(cx, expr) { - check_match_like_matches(cx, expr); - } + redundant_pattern_match::check(cx, expr); + check_match_like_matches(cx, expr); if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); @@ -1068,6 +1074,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr if_chain! { if arms.len() == 2; if cx.tables().expr_ty(expr).is_bool(); + if is_wild(&arms[1].pat); if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); if first != second; @@ -1437,16 +1444,14 @@ mod redundant_pattern_match { use rustc_mir::const_eval::is_const_fn; use rustc_span::source_map::Symbol; - pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), - _ => false, + _ => {}, } - } else { - false } } @@ -1456,7 +1461,7 @@ mod redundant_pattern_match { op: &Expr<'_>, arms: &[Arm<'_>], keyword: &'static str, - ) -> bool { + ) { fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { return Some("is_ok()"); @@ -1487,7 +1492,7 @@ mod redundant_pattern_match { }; let good_method = match good_method { Some(method) => method, - None => return false, + None => return, }; // check that `while_let_on_iterator` lint does not trigger @@ -1497,7 +1502,7 @@ mod redundant_pattern_match { if method_path.ident.name == sym!(next); if match_trait_method(cx, op, &paths::ITERATOR); then { - return false; + return; } } @@ -1526,15 +1531,9 @@ mod redundant_pattern_match { ); }, ); - true } - fn find_sugg_for_match<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], - ) -> bool { + fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { if arms.len() == 2 { let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); @@ -1599,10 +1598,8 @@ mod redundant_pattern_match { ); }, ); - return true; } } - false } #[allow(clippy::too_many_arguments)] diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 2d1ac8836d6..f3e19092480 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] +#![allow(unreachable_patterns)] fn main() { let x = Some(5); @@ -8,25 +9,28 @@ fn main() { // Lint let _y = matches!(x, Some(0)); + // Lint + let _w = matches!(x, Some(_)); + // Turn into is_none let _z = x.is_none(); // Lint - let _z = !matches!(x, Some(r) if r == 0); + let _zz = !matches!(x, Some(r) if r == 0); // Lint - let _zz = matches!(x, Some(5)); + let _zzz = matches!(x, Some(5)); // No lint let _a = match x { Some(_) => false, - None => false, + _ => false, }; // No lint - let _a = match x { + let _ab = match x { Some(0) => false, - Some(_) => true, + _ => true, None => false, }; } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 376abf9244e..fbae7c18b92 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] +#![allow(unreachable_patterns)] fn main() { let x = Some(5); @@ -11,6 +12,12 @@ fn main() { _ => false, }; + // Lint + let _w = match x { + Some(_) => true, + _ => false, + }; + // Turn into is_none let _z = match x { Some(_) => false, @@ -18,24 +25,24 @@ fn main() { }; // Lint - let _z = match x { + let _zz = match x { Some(r) if r == 0 => false, _ => true, }; // Lint - let _zz = if let Some(5) = x { true } else { false }; + let _zzz = if let Some(5) = x { true } else { false }; // No lint let _a = match x { Some(_) => false, - None => false, + _ => false, }; // No lint - let _a = match x { + let _ab = match x { Some(0) => false, - Some(_) => true, + _ => true, None => false, }; } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 0b32af039a8..4668f8565a6 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:9:14 + --> $DIR/match_expr_like_matches_macro.rs:10:14 | LL | let _y = match x { | ______________^ @@ -10,8 +10,18 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:16:14 + | +LL | let _w = match x { + | ______________^ +LL | | Some(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(x, Some(_))` + error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:15:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _z = match x { | ______________^ @@ -23,20 +33,20 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:21:14 + --> $DIR/match_expr_like_matches_macro.rs:28:15 | -LL | let _z = match x { - | ______________^ +LL | let _zz = match x { + | _______________^ LL | | Some(r) if r == 0 => false, LL | | _ => true, LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:27:15 + --> $DIR/match_expr_like_matches_macro.rs:34:16 | -LL | let _zz = if let Some(5) = x { true } else { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` +LL | let _zzz = if let Some(5) = x { true } else { false }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index bd13cf1bdfa..11dff94a288 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -23,7 +23,10 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - matches!(*self, SeemsOption::None) + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } } } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 94479e68555..1d0ee82b4f7 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -25,7 +25,10 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - matches!(*self, SeemsOption::None) + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index be323035d6c..502615fb175 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:47:9 + --> $DIR/question_mark.rs:50:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:51:9 + --> $DIR/question_mark.rs:54:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:55:17 + --> $DIR/question_mark.rs:58:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:61:17 + --> $DIR/question_mark.rs:64:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:78:9 + --> $DIR/question_mark.rs:81:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:86:9 + --> $DIR/question_mark.rs:89:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:94:9 + --> $DIR/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:101:26 + --> $DIR/question_mark.rs:104:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:111:17 + --> $DIR/question_mark.rs:114:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:126:5 + --> $DIR/question_mark.rs:129:5 | LL | / if f().is_none() { LL | | return None; -- cgit 1.4.1-3-g733a5 From db1c946aaa02e1192d271dbcfe4598d726806108 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 30 Jun 2020 21:48:34 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs index ddc41f89f8d..fa560ffb980 100644 --- a/clippy_lints/src/let_and_return.rs +++ b/clippy_lints/src/let_and_return.rs @@ -1,6 +1,5 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently @@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> { borrows: bool, } -impl BorrowVisitor<'_, '_> { - fn fn_def_id(&self, expr: &Expr<'_>) -> Option { - match &expr.kind { - ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id), - ExprKind::Call( - Expr { - kind: ExprKind::Path(qpath), - .. - }, - .., - ) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(), - _ => None, - } - } -} - impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { type Map = Map<'tcx>; @@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { return; } - if let Some(def_id) = self.fn_def_id(expr) { + if let Some(def_id) = fn_def_id(self.cx, expr) { self.borrows = self .cx .tcx diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index d940776817c..91c1789a2ff 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -5,24 +5,23 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** - /// Detects when people use `Vec::sort_by` and pass in a function + /// Detects uses of `Vec::sort_by` passing in a closure /// which compares the two arguments, either directly or indirectly. /// /// **Why is this bad?** /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if - /// possible) than to use `Vec::sort_by` and and a more complicated + /// possible) than to use `Vec::sort_by` and a more complicated /// closure. /// /// **Known problems:** - /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't - /// imported by a use statement in the current frame, then a `use` - /// statement that imports it will need to be added (which this lint - /// can't do). + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already + /// imported by a use statement, then it will need to be added manually. /// /// **Example:** /// @@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { }; let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; + if_chain! { if let ExprKind::Path(QPath::Resolved(_, Path { segments: [PathSegment { ident: left_name, .. }], .. })) = &left_expr.kind; if left_name == left_ident; then { - Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } - else { - Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } else { + if !key_returns_borrow(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) + } } } - } else { - None } } + + None +} + +fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(def_id) = utils::fn_def_id(cx, expr) { + let output = cx.tcx.fn_sig(def_id).output(); + let ty = output.skip_binder(); + return matches!(ty.kind, ty::Ref(..)) + || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + false } impl LateLintPass<'_> for UnnecessarySortBy { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a3b79925ff..93075b9f0b5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1363,6 +1363,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { ) } +/// Returns the `DefId` of the callee if the given expression is a function or method call. +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } +} + pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { lints.iter().any(|lint| { matches!( diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 779fd57707a..c017d1cf9a4 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -2,11 +2,11 @@ use std::cmp::Reverse; -fn id(x: isize) -> isize { - x -} +fn unnecessary_sort_by() { + fn id(x: isize) -> isize { + x + } -fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort(); @@ -24,3 +24,41 @@ fn main() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); } + +// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +mod issue_5754 { + struct Test(String); + + #[derive(PartialOrd, Ord, PartialEq, Eq)] + struct Wrapper<'a>(&'a str); + + impl Test { + fn name(&self) -> &str { + &self.0 + } + + fn wrapped(&self) -> Wrapper<'_> { + Wrapper(&self.0) + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(b.name())); + args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped())); + args.sort_unstable_by(|a, b| a.name().cmp(b.name())); + args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped())); + // Reverse + args.sort_by(|a, b| b.name().cmp(a.name())); + args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped())); + args.sort_unstable_by(|a, b| b.name().cmp(a.name())); + args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped())); + } +} + +fn main() { + unnecessary_sort_by(); + issue_5754::test(); +} diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 0485a5630af..1929c72b2f2 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -2,11 +2,11 @@ use std::cmp::Reverse; -fn id(x: isize) -> isize { - x -} +fn unnecessary_sort_by() { + fn id(x: isize) -> isize { + x + } -fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort_by(|a, b| a.cmp(b)); @@ -24,3 +24,41 @@ fn main() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); } + +// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +mod issue_5754 { + struct Test(String); + + #[derive(PartialOrd, Ord, PartialEq, Eq)] + struct Wrapper<'a>(&'a str); + + impl Test { + fn name(&self) -> &str { + &self.0 + } + + fn wrapped(&self) -> Wrapper<'_> { + Wrapper(&self.0) + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(b.name())); + args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped())); + args.sort_unstable_by(|a, b| a.name().cmp(b.name())); + args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped())); + // Reverse + args.sort_by(|a, b| b.name().cmp(a.name())); + args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped())); + args.sort_unstable_by(|a, b| b.name().cmp(a.name())); + args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped())); + } +} + +fn main() { + unnecessary_sort_by(); + issue_5754::test(); +} -- cgit 1.4.1-3-g733a5 From dac19e3afccc63ff976bcf0a5ee385bdd0e075d5 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 7 Jul 2020 00:35:51 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b754a45aa40..a6cc1097441 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -530,16 +530,22 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp // the lint noisy in unnecessary situations return; } - let els = remove_blocks(&arms[1].body); - let els = if is_unit_expr(els) { + let els = arms[1].body; + let els = if is_unit_expr(remove_blocks(els)) { None - } else if let ExprKind::Block(_, _) = els.kind { - // matches with blocks that contain statements are prettier as `if let + else` - Some(els) + } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind { + if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { + // single statement/expr "else" block, don't lint + return; + } else { + // block with 2+ statements or 1 expr and 1+ statement + Some(els) + } } else { - // allow match arms with just expressions - return; + // not a block, don't lint + return; }; + let ty = cx.tables().expr_ty(ex); if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { check_single_match_single_pattern(cx, ex, arms, expr, els); diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 34193be0b75..b624a41a29b 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,4 +1,6 @@ #![warn(clippy::single_match_else)] +#![allow(clippy::needless_return)] +#![allow(clippy::no_effect)] enum ExprNode { ExprAddrOf, @@ -30,6 +32,55 @@ macro_rules! unwrap_addr { }; } +#[rustfmt::skip] fn main() { unwrap_addr!(ExprNode::Unicorns); + + // + // don't lint single exprs/statements + // + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => return, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return + }, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return; + }, + } + + // + // lint multiple exprs/statements "else" blocks + // + + // lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + println!("else block"); + return + }, + } + + // lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + println!("else block"); + return; + }, + } } diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 59861d46eb3..3a07c2ec542 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:12:5 + --> $DIR/single_match_else.rs:14:5 | LL | / match ExprNode::Butterflies { LL | | ExprNode::ExprAddrOf => Some(&NODE), @@ -19,5 +19,45 @@ LL | None LL | } | -error: aborting due to previous error +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:70:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL | if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL | println!("else block"); +LL | return +LL | } + | + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:79:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return; +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL | if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL | println!("else block"); +LL | return; +LL | } + | + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 298a1fa3bd8ec04350b1bff6a6d92e34abf2e198 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 26 Jun 2020 17:03:10 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index c164ec9aaf1..dd608de5723 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -52,6 +52,11 @@ declare_clippy_lint! { /// exclusive ranges, because they essentially add an extra branch that /// LLVM may fail to hoist out of the loop. /// + /// This will cause a warning that cannot be fixed if the consumer of the + /// range only accepts a specific range type, instead of the generic + /// `RangeBounds` trait + /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). + /// /// **Example:** /// ```rust,ignore /// for x..(y+1) { .. } @@ -72,7 +77,10 @@ declare_clippy_lint! { /// **Why is this bad?** The code is more readable with an exclusive range /// like `x..y`. /// - /// **Known problems:** None. + /// **Known problems:** This will cause a warning that cannot be fixed if + /// the consumer of the range only accepts a specific range type, instead of + /// the generic `RangeBounds` trait + /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). /// /// **Example:** /// ```rust,ignore @@ -83,7 +91,7 @@ declare_clippy_lint! { /// for x..y { .. } /// ``` pub RANGE_MINUS_ONE, - complexity, + pedantic, "`x..=(y-1)` reads better as `x..y`" } -- cgit 1.4.1-3-g733a5 From ba2a85dadc61bbfeb483ca0d05ddfda213da1329 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 3 Jul 2020 21:09:32 +0200 Subject: Run update_lints --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..4d9776018cf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1162,6 +1162,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), + LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), @@ -1382,7 +1383,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::PTR_ARG), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), - LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), @@ -1598,7 +1598,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(&precedence::PRECEDENCE), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..c20793ecd3e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1783,7 +1783,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "range_minus_one", - group: "complexity", + group: "pedantic", desc: "`x..=(y-1)` reads better as `x..y`", deprecation: None, module: "ranges", -- cgit 1.4.1-3-g733a5 From 780a4c87de88d7f747c7847b71c90c8268fe4b66 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 10 Jul 2020 23:53:15 +0900 Subject: Fix typo --- clippy_lints/src/repeat_once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index af3c948ec82..436374d7c54 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -15,7 +15,7 @@ declare_clippy_lint! { /// - `.clone()` for `String` /// - `.to_vec()` for `slice` /// - /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind thi, `clone()` should be used. + /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used. /// /// **Known problems:** None. /// -- cgit 1.4.1-3-g733a5 From 1b3bc16533a3e701616648920603c10674eb653b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 11 Jul 2020 12:28:21 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 169a486d1eb..502bf0c4279 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -400,8 +400,8 @@ fn extend_with_matching( /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? fn eq_pre_post(ps1: &[P], ps2: &[P], idx: usize) -> bool { - ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. - && ps1.len() == ps2.len() + ps1.len() == ps2.len() + && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) } -- cgit 1.4.1-3-g733a5 From f2419b9d6299fb07a1163b47dc273f64e0444a6c Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 14 Jul 2020 00:11:10 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 436374d7c54..3a0b3b1c257 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,12 +1,9 @@ -use crate::consts::{miri_to_const, Constant}; +use crate::consts::{constant_context, Constant}; use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -44,10 +41,11 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if_chain! { if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; if path.ident.name == sym!(repeat); - if is_once(cx, &args[1]) && !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.tables()).expr(&args[1]); + if !in_macro(args[0].span); then { let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0])); - if is_str(ty){ + if ty.is_str() { span_lint_and_sugg( cx, REPEAT_ONCE, @@ -57,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_slice(ty) { + } else if let Some(_) = ty.builtin_index() { span_lint_and_sugg( cx, REPEAT_ONCE, @@ -82,45 +80,3 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { } } } - -fn is_once<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { - match expr.kind { - ExprKind::Lit(ref lit) => { - if let LitKind::Int(ref lit_content, _) = lit.node { - *lit_content == 1 - } else { - false - } - }, - ExprKind::Path(rustc_hir::QPath::Resolved(None, path)) => { - if let Res::Def(DefKind::Const, def_id) = path.res { - let ty = cx.tcx.type_of(def_id); - let con = cx - .tcx - .const_eval_poly(def_id) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)) - .unwrap(); - let con = miri_to_const(con); - con == Some(Constant::Int(1)) - } else { - false - } - }, - _ => false, - } -} - -fn is_str(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Str => true, - _ => false, - } -} - -fn is_slice(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Slice(..) | ty::Array(..) => true, - _ => false, - } -} -- cgit 1.4.1-3-g733a5 From ff796b6d7082d5b5c073797aba17f454eebe3359 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 19 Jun 2020 11:44:03 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 8090f4673aa..42bff564de0 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -115,7 +115,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { COLLAPSIBLE_IF, block.span, "this `else { if .. }` block can be collapsed", - "try", + "collapse nested if block", snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), applicability, ); @@ -142,7 +142,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & let rhs = Sugg::ast(cx, check_inner, ".."); diag.span_suggestion( expr.span, - "try", + "collapse nested if block", format!( "if {} {}", lhs.and(&rhs), diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 28048999e8e..3d1c458879e 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -10,7 +10,7 @@ LL | | } | |_____^ | = note: `-D clippy::collapsible-if` implied by `-D warnings` -help: try +help: collapse nested if block | LL | } else if y == "world" { LL | println!("world!") @@ -28,7 +28,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world!") @@ -48,7 +48,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if y == "world" { LL | println!("world") @@ -71,7 +71,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") @@ -94,7 +94,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") @@ -117,7 +117,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if x == "hello" { LL | println!("world") @@ -140,7 +140,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 6440ff41be8..f56dd65b9dd 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::collapsible-if` implied by `-D warnings` -help: try +help: collapse nested if block | LL | if x == "hello" && y == "world" { LL | println!("Hello world!"); @@ -26,7 +26,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { LL | println!("Hello world!"); @@ -43,7 +43,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { LL | println!("Hello world!"); @@ -60,7 +60,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { LL | println!("Hello world!"); @@ -77,7 +77,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { LL | println!("Hello world!"); @@ -94,7 +94,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if 42 == 1337 && 'a' != 'A' { LL | println!("world!") @@ -111,7 +111,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && y == "world" { // Collapsible LL | println!("Hello world!"); -- cgit 1.4.1-3-g733a5 From 201999ccfd18a9debe1f186f30f40659ebc6b933 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Mon, 13 Jul 2020 10:33:25 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4c595029ff7..565a08f1292 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2348,8 +2348,8 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar cx, ITER_NTH_ZERO, expr.span, - "called `.nth(0)` on a `std::iter::Iterator`", - "try calling", + "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", + "try calling .next() instead of .nth(0)", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_nth_zero.stderr b/tests/ui/iter_nth_zero.stderr index 2b20a4ceb4a..6c4200a7905 100644 --- a/tests/ui/iter_nth_zero.stderr +++ b/tests/ui/iter_nth_zero.stderr @@ -1,22 +1,22 @@ -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling: `iter.next()` + | ^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter.next()` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling: `iter2.next()` + | ^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter2.next()` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From b4091032abbadf586ea8c77bc547ec2ac403ef0a Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 14 Jul 2020 08:08:13 +0900 Subject: Use `.is_some()` not `Some(_)` --- clippy_lints/src/repeat_once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 3a0b3b1c257..a3af369e41e 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), Applicability::MachineApplicable, ); - } else if let Some(_) = ty.builtin_index() { + } else if ty.builtin_index().is_some() { span_lint_and_sugg( cx, REPEAT_ONCE, -- cgit 1.4.1-3-g733a5 From 126790999a128a880ca276c49afd2927a66ffbbe Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 30 Mar 2020 11:02:14 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d08b44ba40..1c927b5f83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1679,6 +1679,7 @@ Released 2018-09-13 [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 32e79317f82..7a4ca3902b3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -300,6 +300,7 @@ mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; +mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; mod unnested_or_patterns; @@ -826,6 +827,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, &unicode::ZERO_WIDTH_SPACE, + &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, @@ -891,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); + store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); @@ -1436,6 +1439,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), @@ -1692,6 +1696,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs new file mode 100644 index 00000000000..fceb885516b --- /dev/null +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -0,0 +1,177 @@ +use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that expect closures of type + /// Fn(...) -> Ord where the implemented closure returns the unit type. + /// The lint also suggests to remove the semi-colon at the end of the statement if present. + /// + /// **Why is this bad?** Likely, returning the unit type is unintentional, and + /// could simply be caused by an extra semi-colon. Since () implements Ord + /// it doesn't cause a compilation error. + /// This is the same reasoning behind the unit_cmp lint. + /// + /// **Known problems:** If returning unit is intentional, then there is no + /// way of specifying this without triggering needless_return lint + /// + /// **Example:** + /// + /// ```rust + /// let mut twins = vec!((1,1), (2,2)); + /// twins.sort_by_key(|x| { x.1; }); + /// ``` + pub UNIT_RETURN_EXPECTING_ORD, + correctness, + "fn arguments of type Fn(...) -> Ord returning the unit type ()." +} + +declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]); + +fn get_trait_predicates_for_trait_id<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + trait_id: Option, +) -> Vec> { + let mut preds = Vec::new(); + for (pred, _) in generics.predicates { + if_chain! { + if let PredicateKind::Trait(poly_trait_pred, _) = pred.kind(); + let trait_pred = cx.tcx.erase_late_bound_regions(&poly_trait_pred); + if let Some(trait_def_id) = trait_id; + if trait_def_id == trait_pred.trait_ref.def_id; + then { + preds.push(trait_pred); + } + } + } + preds +} + +fn get_projection_pred<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + pred: TraitPredicate<'tcx>, +) -> Option> { + generics.predicates.iter().find_map(|(proj_pred, _)| { + if let PredicateKind::Projection(proj_pred) = proj_pred.kind() { + let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred); + if projection_pred.projection_ty.substs == pred.trait_ref.substs { + return Some(projection_pred); + } + } + None + }) +} + +fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> { + let mut args_to_check = Vec::new(); + if let Some(def_id) = cx.tables().type_dependent_def_id(expr.hir_id) { + let fn_sig = cx.tcx.fn_sig(def_id); + let generics = cx.tcx.predicates_of(def_id); + let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let partial_ord_preds = + get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); + // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error + // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for `&[&rustc::ty::TyS<'_>]` + let inputs_output = cx.tcx.erase_late_bound_regions(&fn_sig.inputs_and_output()); + inputs_output + .iter() + .rev() + .skip(1) + .rev() + .enumerate() + .for_each(|(i, inp)| { + for trait_pred in &fn_mut_preds { + if_chain! { + if trait_pred.self_ty() == inp; + if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); + then { + if ord_preds.iter().any(|ord| ord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "Ord".to_string())); + } else if partial_ord_preds.iter().any(|pord| pord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "PartialOrd".to_string())); + } + } + } + } + }); + } + args_to_check +} + +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { + if_chain! { + if let ExprKind::Closure(_, _fn_decl, body_id, span, _) = arg.kind; + if let ty::Closure(_def_id, substs) = &cx.tables().node_type(arg.hir_id).kind; + let ret_ty = substs.as_closure().sig().output(); + let ty = cx.tcx.erase_late_bound_regions(&ret_ty); + if ty.is_unit(); + then { + if_chain! { + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = body.value.kind; + if block.expr.is_none(); + if let Some(stmt) = block.stmts.last(); + if let StmtKind::Semi(_) = stmt.kind; + then { + let data = stmt.span.data(); + // Make a span out of the semicolon for the help message + Some((span, Some(Span::new(data.hi-BytePos(1), data.hi, data.ctxt)))) + } else { + Some((span, None)) + } + } + } else { + None + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + let arg_indices = get_args_to_check(cx, expr); + for (i, trait_name) in arg_indices { + if i < args.len() { + match check_arg(cx, &args[i]) { + Some((span, None)) => { + span_lint( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + ); + }, + Some((span, Some(last_semi))) => { + span_lint_and_help( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + Some(last_semi), + &"probably caused by this trailing semicolon".to_string(), + ); + }, + None => {}, + } + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b89a8712862..96b004904aa 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2292,6 +2292,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "unit_return_expecting_ord", + group: "correctness", + desc: "fn arguments of type Fn(...) -> Ord returning the unit type ().", + deprecation: None, + module: "unit_return_expecting_ord", + }, Lint { name: "unknown_clippy_lints", group: "style", diff --git a/tests/ui/unit_return_expecting_ord.rs b/tests/ui/unit_return_expecting_ord.rs new file mode 100644 index 00000000000..bdb4710cc69 --- /dev/null +++ b/tests/ui/unit_return_expecting_ord.rs @@ -0,0 +1,36 @@ +#![warn(clippy::unit_return_expecting_ord)] +#![allow(clippy::needless_return)] +#![allow(clippy::unused_unit)] +#![feature(is_sorted)] + +struct Struct { + field: isize, +} + +fn double(i: isize) -> isize { + i * 2 +} + +fn unit(_i: isize) {} + +fn main() { + let mut structs = vec![Struct { field: 2 }, Struct { field: 1 }]; + structs.sort_by_key(|s| { + double(s.field); + }); + structs.sort_by_key(|s| double(s.field)); + structs.is_sorted_by_key(|s| { + double(s.field); + }); + structs.is_sorted_by_key(|s| { + if s.field > 0 { + () + } else { + return (); + } + }); + structs.sort_by_key(|s| { + return double(s.field); + }); + structs.sort_by_key(|s| unit(s.field)); +} diff --git a/tests/ui/unit_return_expecting_ord.stderr b/tests/ui/unit_return_expecting_ord.stderr new file mode 100644 index 00000000000..e63d5874609 --- /dev/null +++ b/tests/ui/unit_return_expecting_ord.stderr @@ -0,0 +1,39 @@ +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:18:25 + | +LL | structs.sort_by_key(|s| { + | ^^^ + | + = note: `-D clippy::unit-return-expecting-ord` implied by `-D warnings` +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:19:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:22:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + | +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:23:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:25:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:35:25 + | +LL | structs.sort_by_key(|s| unit(s.field)); + | ^^^ + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From e83b3eb9930ab527451edaaa6f524acd26c1bf1c Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 14 Jul 2020 09:20:19 -0700 Subject: formatting nits --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/iter_nth_zero.stderr | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 565a08f1292..69e985cc0a4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2349,7 +2349,7 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar ITER_NTH_ZERO, expr.span, "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", - "try calling .next() instead of .nth(0)", + "try calling `.next()` instead of `.nth(0)`", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_nth_zero.stderr b/tests/ui/iter_nth_zero.stderr index 6c4200a7905..29c56f3a94f 100644 --- a/tests/ui/iter_nth_zero.stderr +++ b/tests/ui/iter_nth_zero.stderr @@ -2,7 +2,7 @@ error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` @@ -10,13 +10,13 @@ error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter.next()` + | ^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter.next()` error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter2.next()` + | ^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter2.next()` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From a0640457a912720a5473d7feff40576a2f97df1e Mon Sep 17 00:00:00 2001 From: Leo Meira Vital Date: Mon, 13 Jul 2020 01:57:19 -0300 Subject: Removing snippet from SHADOW_UNRELATED message. --- clippy_lints/src/shadow.rs | 6 +----- tests/ui/shadow.stderr | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 194786c5c41..fab13c8c124 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -295,11 +295,7 @@ fn lint_shadow<'tcx>( cx, SHADOW_UNRELATED, pattern_span, - &format!( - "`{}` is shadowed by `{}`", - snippet(cx, pattern_span, "_"), - snippet(cx, expr.span, "..") - ), + &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")), |diag| { diag.span_note(expr.span, "initialization happens here"); diag.span_note(prev_span, "previous binding is here"); diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 7fa58cf7649..8a831375b41 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -104,7 +104,7 @@ note: previous binding is here LL | let x = (1, x); | ^ -error: `x` is shadowed by `y` +error: `x` is being shadowed --> $DIR/shadow.rs:34:9 | LL | let x = y; -- cgit 1.4.1-3-g733a5 From 70a41a92815a79c88dd9a2e8aa02503a3b95eae8 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 16 Jul 2020 16:51:12 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 10f4694640e..9944b4096ba 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -96,23 +96,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { if_chain! { if let ExprKind::Block(ref block, _) = expr.kind; if let Some(ref ex) = block.expr; - if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC); - if params.len() == 1; + if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC) + .or(match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); then { + let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNIMPLEMENTED, span, "`unimplemented` should not be present in production code"); } else if is_expn_of(expr.span, "todo").is_some() { - let span = get_outer_span(expr); span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNREACHABLE, span, "`unreachable` should not be present in production code"); } else if is_expn_of(expr.span, "panic").is_some() { - let span = get_outer_span(expr); span_lint(cx, PANIC, span, "`panic` should not be present in production code"); match_panic(params, expr, cx); diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index dabb695368d..f91ccfaed74 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -4,24 +4,32 @@ fn panic() { let a = 2; panic!(); + panic!("message"); + panic!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn todo() { let a = 2; todo!(); + todo!("message"); + todo!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unimplemented() { let a = 2; unimplemented!(); + unimplemented!("message"); + unimplemented!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unreachable() { let a = 2; unreachable!(); + unreachable!("message"); + unreachable!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 72319bc7e45..37c11d72a57 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -6,29 +6,83 @@ LL | panic!(); | = note: `-D clippy::panic` implied by `-D warnings` +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:7:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:8:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:12:5 + --> $DIR/panicking_macros.rs:14:5 | LL | todo!(); | ^^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:15:5 + | +LL | todo!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:16:5 + | +LL | todo!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:22:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: `unimplemented` should not be present in production code + --> $DIR/panicking_macros.rs:23:5 + | +LL | unimplemented!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 | +LL | unimplemented!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:30:5 + | LL | unreachable!(); | ^^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` -error: aborting due to 4 previous errors +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:31:5 + | +LL | unreachable!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:32:5 + | +LL | unreachable!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 07867fde592babd74ee9934726c4c7010dee149b Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 16 Jul 2020 16:58:21 -0700 Subject: Clean up dogfood fallout --- clippy_lints/src/panic_unimplemented.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 9944b4096ba..6379dffd22e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { if let ExprKind::Block(ref block, _) = expr.kind; if let Some(ref ex) = block.expr; if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC) - .or(match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); + .or_else(|| match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); then { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { -- cgit 1.4.1-3-g733a5 From 3618b97f59d9696c3e5ef83948269d0d7abfdc5b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 17 Jul 2020 01:58:41 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 818d8188a78..c17a0e83330 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -153,7 +153,7 @@ declare_deprecated_lint! { /// /// **Deprecation reason:** Associated-constants are now preferred. pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants" + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" } declare_deprecated_lint! { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 323cad7fa1a..7a3f35aca0a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -10,7 +10,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. /// - /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise, + /// **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise, /// when not part of a method chain. /// /// **Example:** diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index bd7ca038839..9fb10c7f627 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { .. } = item.kind { - // Remember for each inherent implementation encoutered its span and generics + // Remember for each inherent implementation encountered its span and generics // but filter out implementations that have generic params (type or lifetime) // or are derived from a macro if !in_macro(item.span) && generics.params.is_empty() { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7a4ca3902b3..823afdfd289 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -463,7 +463,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_removed( "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants", + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", ); store.register_removed( "clippy::regex_macro", diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index bdce1bf1521..1ad184dfc46 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { - // If any of the params are dropable, return true + // If any of the params are droppable, return true param_tys.iter().any(|hir_ty| { let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); has_drop(cx, ty_ty) diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 59ccc6333fd..5041af750ce 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; declare_clippy_lint! { - /// **What it does:** Checks for modulo arithemtic. + /// **What it does:** Checks for modulo arithmetic. /// /// **Why is this bad?** The results of modulo (%) operation might differ /// depending on the language, when negative numbers are involved. diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 8dbe58763bf..9922d906118 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { } } -/// A struct containing information about occurences of the +/// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. struct OptionIfLetElseOccurence { option: String, diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 87cb454f654..5e8800d38eb 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,7 +36,7 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent seperator (b'e' or b'E') and the exponent part. + /// The character used as exponent separator (b'e' or b'E') and the exponent part. pub exponent: Option<(char, &'a str)>, /// The type suffix, including preceding underscore if present. diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 6bb1032a172..27222cc0869 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -30,7 +30,7 @@ async fn already_async() -> impl Future { struct S {} impl S { async fn inh_fut() -> i32 { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index d50c919188b..6a0f1b26c88 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -37,7 +37,7 @@ struct S {} impl S { fn inh_fut() -> impl Future { async { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index f278ee41aa3..a1904c904d0 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -57,7 +57,7 @@ LL | async fn inh_fut() -> i32 { help: move the body of the async block to the enclosing function | LL | fn inh_fut() -> impl Future { -LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | // NOTE: this code is here just to check that the indentation is correct in the suggested fix LL | let a = 42; LL | let b = 21; LL | if a < b { diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index cbc4ca39161..2770eb2b2ab 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -166,7 +166,7 @@ pub fn test14() { } } -// Issue #1991: the outter loop should not warn. +// Issue #1991: the outer loop should not warn. pub fn test15() { 'label: loop { while false { diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 17b1f1bd0bf..4d284ae1319 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d0891fd3c2..2d08e82f349 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs index 0263e2f5f20..7ed27439ec6 100644 --- a/tests/ui/vec_resize_to_zero.rs +++ b/tests/ui/vec_resize_to_zero.rs @@ -7,7 +7,7 @@ fn main() { // not applicable vec![1, 2, 3, 4, 5].resize(2, 5); - // applicable here, but only implemented for integer litterals for now + // applicable here, but only implemented for integer literals for now vec!["foo", "bar", "baz"].resize(0, "bar"); // not applicable -- cgit 1.4.1-3-g733a5 From e5105e82d31b254a81f38fbf38a8043ba41bee3a Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 17 Jul 2020 22:51:57 +0900 Subject: Fix typo --- clippy_lints/src/vec_resize_to_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs index cc5e21a7ca6..7f90aedf161 100644 --- a/clippy_lints/src/vec_resize_to_zero.rs +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -11,7 +11,7 @@ use rustc_ast::ast::LitKind; use rustc_hir as hir; declare_clippy_lint! { - /// **What it does:** Finds occurences of `Vec::resize(0, an_int)` + /// **What it does:** Finds occurrences of `Vec::resize(0, an_int)` /// /// **Why is this bad?** This is probably an argument inversion mistake. /// -- cgit 1.4.1-3-g733a5 From e85b590936863e88da4ccd73af9f5da0787b9609 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 17 Jul 2020 10:40:01 -0700 Subject: Fix bug in lint --- clippy_lints/src/matches.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index bd474c20807..fe00a48adef 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1512,6 +1512,10 @@ mod redundant_pattern_match { } } + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, @@ -1524,7 +1528,7 @@ mod redundant_pattern_match { // while let ... = ... { ... } // ^^^ - let op_span = op.span.source_callsite(); + let op_span = result_expr.span.source_callsite(); // while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^ @@ -1589,17 +1593,21 @@ mod redundant_pattern_match { }; if let Some(good_method) = found_good_method { + let span = expr.span.to(op.span); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, expr.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { - let span = expr.span.to(op.span); diag.span_suggestion( span, "try this", - format!("{}.{}", snippet(cx, op.span, "_"), good_method), + format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method), Applicability::MaybeIncorrect, // snippet ); }, -- cgit 1.4.1-3-g733a5 From 3d3a13d8719893972e2146cde86672151e7d5476 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 17 Jul 2020 21:39:05 +0200 Subject: Fix sync fallout (fmt) --- clippy_lints/src/non_expressive_names.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 1d4772bb3d6..48ab98418e4 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -218,12 +218,16 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { let mut split_at = None; match existing_name.len.cmp(&count) { Ordering::Greater => { - if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) { + if existing_name.len - count != 1 + || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) + { continue; } }, Ordering::Less => { - if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) { + if count - existing_name.len != 1 + || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) + { continue; } }, -- cgit 1.4.1-3-g733a5 From 442c8ae23b90874485468b3becfc011f8c9d40bb Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 18 Jul 2020 23:59:34 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 6d1d083fa8d..502fffc5e6c 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -64,26 +64,22 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { | hir::BinOpKind::Gt => return, _ => {}, } - // Check if the binary expression is part of another bi/unary expression - // or operator assignment as a child node - let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id); - while parent_expr != hir::CRATE_HIR_ID { - if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) { - match e.kind { - hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => return, - _ => {}, + + // Check for more than one binary operation in the implemented function + // Linting when multiple operations are involved can result in false positives + if_chain! { + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); + if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); + if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; + let body = cx.tcx.hir().body(body_id); + let mut visitor = BinaryExprVisitor { nb_binops: 0 }; + + then { + walk_expr(&mut visitor, &body.value); + if visitor.nb_binops > 1 { + return; } } - parent_expr = cx.tcx.hir().get_parent_node(parent_expr); - } - // as a parent node - let mut visitor = BinaryExprVisitor { in_binary_expr: false }; - walk_expr(&mut visitor, expr); - - if visitor.in_binary_expr { - return; } if let Some(impl_trait) = check_binop( @@ -181,7 +177,7 @@ fn check_binop( } struct BinaryExprVisitor { - in_binary_expr: bool, + nb_binops: u32, } impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { @@ -191,12 +187,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { match expr.kind { hir::ExprKind::Binary(..) | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true, + | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, _ => {}, } walk_expr(self, expr); } + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs index 1f5b9811887..60c2f3ec9b6 100644 --- a/tests/ui/suspicious_arithmetic_impl.rs +++ b/tests/ui/suspicious_arithmetic_impl.rs @@ -88,3 +88,33 @@ fn main() {} fn do_nothing(x: u32) -> u32 { x } + +struct MultipleBinops(u32); + +impl Add for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `add` impl + fn add(self, other: Self) -> Self::Output { + let mut result = self.0 + other.0; + if result >= u32::max_value() { + result -= u32::max_value(); + } + MultipleBinops(result) + } +} + +impl Mul for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `mul` impl + fn mul(self, other: Self) -> Self::Output { + let mut result: u32 = 0; + let size = std::cmp::max(self.0, other.0) as usize; + let mut v = vec![0; size + 1]; + for i in 0..size + 1 { + result *= i as u32; + } + MultipleBinops(result) + } +} -- cgit 1.4.1-3-g733a5 From c720d823e1e633cccfd24e1df76cc04a628e83b0 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 14 Jul 2020 20:27:25 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index b84a1a3fe24..125df226ceb 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -14,6 +14,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -493,6 +494,29 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + fn count_closure_usage(block: &Block, ident: &Ident) -> usize { + struct ClosureUsageCount<'ast> { + ident: &'ast Ident, + count: usize, + }; + impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> { + fn visit_expr(&mut self, expr: &'ast Expr) { + if_chain! { + if let ExprKind::Call(ref closure, _) = expr.kind; + if let ExprKind::Path(_, ref path) = closure.kind; + if self.ident == &path.segments[0].ident; + then { + self.count += 1; + } + } + walk_expr(self, expr); + } + } + let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + for w in block.stmts.windows(2) { if_chain! { if let StmtKind::Local(ref local) = w[0].kind; @@ -503,15 +527,15 @@ impl EarlyLintPass for MiscEarlyLints { if let ExprKind::Assign(_, ref call, _) = second.kind; if let ExprKind::Call(ref closure, _) = call.kind; if let ExprKind::Path(_, ref path) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(block, &ident) == 1; then { - if ident == path.segments[0].ident { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); } } } diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call.rs index bacd67db7c3..0f2ba4a075d 100644 --- a/tests/ui/redundant_closure_call.rs +++ b/tests/ui/redundant_closure_call.rs @@ -8,14 +8,21 @@ fn main() { k = (|a, b| a * b)(1, 5); - let closure = || 32; - i = closure(); - + // don't lint here, the closure is used more than once let closure = |i| i + 1; i = closure(3); - i = closure(4); + // lint here + let redun_closure = || 1; + i = redun_closure(); + + // the lint is applicable here but the lint doesn't support redefinition + let redefined_closure = || 1; + i = redefined_closure(); + let redefined_closure = || 2; + i = redefined_closure(); + #[allow(clippy::needless_return)] (|| return 2)(); (|| -> Option { None? })(); diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call.stderr index 68c1416bb6b..d5e0664319b 100644 --- a/tests/ui/redundant_closure_call.stderr +++ b/tests/ui/redundant_closure_call.stderr @@ -1,17 +1,11 @@ error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:12:5 + --> $DIR/redundant_closure_call.rs:18:5 | -LL | i = closure(); - | ^^^^^^^^^^^^^ +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:15:5 - | -LL | i = closure(3); - | ^^^^^^^^^^^^^^ - error: Try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call.rs:7:17 | @@ -24,5 +18,5 @@ error: Try not to call a closure in the expression where it is declared. LL | k = (|a, b| a * b)(1, 5); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 0fecaf1abcbe8d4ba1a9c532b69aab6fab3097c8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 19 Jul 2020 00:33:54 +0200 Subject: 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.rs | 30 ------ tests/ui/redundant_closure_call.stderr | 22 ---- tests/ui/redundant_closure_call_early.rs | 19 ++++ tests/ui/redundant_closure_call_early.stderr | 16 +++ tests/ui/redundant_closure_call_late.rs | 22 ++++ tests/ui/redundant_closure_call_late.stderr | 10 ++ 10 files changed, 229 insertions(+), 184 deletions(-) create mode 100644 clippy_lints/src/redundant_closure_call.rs delete mode 100644 tests/ui/redundant_closure_call.rs delete mode 100644 tests/ui/redundant_closure_call.stderr create mode 100644 tests/ui/redundant_closure_call_early.rs create mode 100644 tests/ui/redundant_closure_call_early.stderr create mode 100644 tests/ui/redundant_closure_call_late.rs create mode 100644 tests/ui/redundant_closure_call_late.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 823afdfd289..f371942dbee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -276,6 +276,7 @@ mod ptr_offset_with_cast; mod question_mark; mod ranges; mod redundant_clone; +mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -702,7 +703,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &misc_early::DOUBLE_NEG, &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, &misc_early::MIXED_CASE_HEX_LITERALS, - &misc_early::REDUNDANT_CLOSURE_CALL, &misc_early::REDUNDANT_PATTERN, &misc_early::UNNEEDED_FIELD_PATTERN, &misc_early::UNNEEDED_WILDCARD_PATTERN, @@ -759,6 +759,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_ZIP_WITH_LEN, &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, + &redundant_closure_call::REDUNDANT_CLOSURE_CALL, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -1018,6 +1019,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box int_plus_one::IntPlusOne); store.register_early_pass(|| box formatting::Formatting); store.register_early_pass(|| box misc_early::MiscEarlyLints); + store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); + store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_early_pass(|| box returns::Return); store.register_late_pass(|| box let_and_return::LetReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); @@ -1359,7 +1362,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::DOUBLE_NEG), LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), @@ -1393,6 +1395,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), + LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), @@ -1593,7 +1596,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::USELESS_ASREF), LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&needless_bool::BOOL_COMPARISON), @@ -1608,6 +1610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&precedence::PRECEDENCE), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 125df226ceb..29aba7c1218 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -1,20 +1,15 @@ -use crate::utils::{ - constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, -}; -use if_chain::if_chain; +use crate::utils::{constants, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::{ - BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, StmtKind, UnOp, + BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, + NodeId, Pat, PatKind, UnOp, }; -use rustc_ast::visit::{walk_expr, FnKind, Visitor}; +use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -71,28 +66,6 @@ declare_clippy_lint! { "function arguments having names which only differ by an underscore" } -declare_clippy_lint! { - /// **What it does:** Detects closures called in the same expression where they - /// are defined. - /// - /// **Why is this bad?** It is unnecessarily adding to the expression's - /// complexity. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// // Bad - /// let a = (|| 42)() - /// - /// // Good - /// let a = 42 - /// ``` - pub REDUNDANT_CLOSURE_CALL, - complexity, - "throwaway closures called in the expression they are defined" -} - declare_clippy_lint! { /// **What it does:** Detects expressions of the form `--x`. /// @@ -279,7 +252,6 @@ declare_clippy_lint! { declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, - REDUNDANT_CLOSURE_CALL, DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX, @@ -289,30 +261,6 @@ declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_WILDCARD_PATTERN, ]); -// Used to find `return` statements or equivalents e.g., `?` -struct ReturnVisitor { - found_return: bool, -} - -impl ReturnVisitor { - #[must_use] - fn new() -> Self { - Self { found_return: false } - } -} - -impl<'ast> Visitor<'ast> for ReturnVisitor { - fn visit_expr(&mut self, ex: &'ast Expr) { - if let ExprKind::Ret(_) = ex.kind { - self.found_return = true; - } else if let ExprKind::Try(_) = ex.kind { - self.found_return = true; - } - - walk_expr(self, ex) - } -} - impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { @@ -454,30 +402,6 @@ impl EarlyLintPass for MiscEarlyLints { return; } match expr.kind { - ExprKind::Call(ref paren, _) => { - if let ExprKind::Paren(ref closure) = paren.kind { - if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind { - let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); - if !visitor.found_return { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_CALL, - expr.span, - "Try not to call a closure in the expression where it is declared.", - |diag| { - if decl.inputs.is_empty() { - let mut app = Applicability::MachineApplicable; - let hint = - snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); - } - }, - ); - } - } - } - }, ExprKind::Unary(UnOp::Neg, ref inner) => { if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { span_lint( @@ -492,54 +416,6 @@ impl EarlyLintPass for MiscEarlyLints { _ => (), } } - - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { - fn count_closure_usage(block: &Block, ident: &Ident) -> usize { - struct ClosureUsageCount<'ast> { - ident: &'ast Ident, - count: usize, - }; - impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> { - fn visit_expr(&mut self, expr: &'ast Expr) { - if_chain! { - if let ExprKind::Call(ref closure, _) = expr.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - if self.ident == &path.segments[0].ident; - then { - self.count += 1; - } - } - walk_expr(self, expr); - } - } - let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; - closure_usage_count.visit_block(block); - closure_usage_count.count - } - - for w in block.stmts.windows(2) { - if_chain! { - if let StmtKind::Local(ref local) = w[0].kind; - if let Option::Some(ref t) = local.init; - if let ExprKind::Closure(..) = t.kind; - if let PatKind::Ident(_, ident, _) = local.pat.kind; - if let StmtKind::Semi(ref second) = w[1].kind; - if let ExprKind::Assign(_, ref call, _) = second.kind; - if let ExprKind::Call(ref closure, _) = call.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - if ident == path.segments[0].ident; - if count_closure_usage(block, &ident) == 1; - then { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } - } - } - } } impl MiscEarlyLints { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs new file mode 100644 index 00000000000..391d70af159 --- /dev/null +++ b/clippy_lints/src/redundant_closure_call.rs @@ -0,0 +1,151 @@ +use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit as ast_visit; +use rustc_ast::visit::Visitor as AstVisitor; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit as hir_visit; +use rustc_hir::intravisit::Visitor as HirVisitor; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** Detects closures called in the same expression where they + /// are defined. + /// + /// **Why is this bad?** It is unnecessarily adding to the expression's + /// complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 + /// ``` + pub REDUNDANT_CLOSURE_CALL, + complexity, + "throwaway closures called in the expression they are defined" +} + +declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]); + +// Used to find `return` statements or equivalents e.g., `?` +struct ReturnVisitor { + found_return: bool, +} + +impl ReturnVisitor { + #[must_use] + fn new() -> Self { + Self { found_return: false } + } +} + +impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { + fn visit_expr(&mut self, ex: &'ast ast::Expr) { + if let ast::ExprKind::Ret(_) = ex.kind { + self.found_return = true; + } else if let ast::ExprKind::Try(_) = ex.kind { + self.found_return = true; + } + + ast_visit::walk_expr(self, ex) + } +} + +impl EarlyLintPass for RedundantClosureCall { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if_chain! { + if let ast::ExprKind::Call(ref paren, _) = expr.kind; + if let ast::ExprKind::Paren(ref closure) = paren.kind; + if let ast::ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind; + then { + let mut visitor = ReturnVisitor::new(); + visitor.visit_expr(block); + if !visitor.found_return { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_CALL, + expr.span, + "Try not to call a closure in the expression where it is declared.", + |diag| { + if decl.inputs.is_empty() { + let mut app = Applicability::MachineApplicable; + let hint = + snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); + diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + } + }, + ); + } + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, ident: &'tcx Ident) -> usize { + struct ClosureUsageCount<'tcx> { + ident: &'tcx Ident, + count: usize, + }; + impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if_chain! { + if let hir::ExprKind::Call(ref closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if self.ident == &path.segments[0].ident; + then { + self.count += 1; + } + } + hir_visit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::None + } + }; + let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + + for w in block.stmts.windows(2) { + if_chain! { + if let hir::StmtKind::Local(ref local) = w[0].kind; + if let Option::Some(ref t) = local.init; + if let hir::ExprKind::Closure(..) = t.kind; + if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; + if let hir::StmtKind::Semi(ref second) = w[1].kind; + if let hir::ExprKind::Assign(_, ref call, _) = second.kind; + if let hir::ExprKind::Call(ref closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(block, &ident) == 1; + then { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96b004904aa..1879aae77fb 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1835,7 +1835,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "complexity", desc: "throwaway closures called in the expression they are defined", deprecation: None, - module: "misc_early", + module: "redundant_closure_call", }, Lint { name: "redundant_closure_for_method_calls", diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call.rs deleted file mode 100644 index 0f2ba4a075d..00000000000 --- a/tests/ui/redundant_closure_call.rs +++ /dev/null @@ -1,30 +0,0 @@ -// non rustfixable, see redundant_closure_call_fixable.rs - -#![warn(clippy::redundant_closure_call)] - -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; - 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? })(); - (|| -> Result { Err(2)? })(); -} diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call.stderr deleted file mode 100644 index d5e0664319b..00000000000 --- a/tests/ui/redundant_closure_call.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:18:5 - | -LL | i = redun_closure(); - | ^^^^^^^^^^^^^^^^^^^ - | - = 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 - | -LL | k = (|a, b| a * b)(1, 5); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/redundant_closure_call_early.rs b/tests/ui/redundant_closure_call_early.rs new file mode 100644 index 00000000000..3dd365620cc --- /dev/null +++ b/tests/ui/redundant_closure_call_early.rs @@ -0,0 +1,19 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // lint here + let mut k = (|m| m + 1)(i); + + // lint here + k = (|a, b| a * b)(1, 5); + + // don't lint these + #[allow(clippy::needless_return)] + (|| return 2)(); + (|| -> Option { None? })(); + (|| -> Result { Err(2)? })(); +} diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr new file mode 100644 index 00000000000..0ac97ae25d0 --- /dev/null +++ b/tests/ui/redundant_closure_call_early.stderr @@ -0,0 +1,16 @@ +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); + | ^^^^^^^^^^^^^^ + | + = 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_early.rs:12:9 + | +LL | k = (|a, b| a * b)(1, 5); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs new file mode 100644 index 00000000000..21ee58e5b44 --- /dev/null +++ b/tests/ui/redundant_closure_call_late.rs @@ -0,0 +1,22 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // 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(); +} diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr new file mode 100644 index 00000000000..0aebc3c419e --- /dev/null +++ b/tests/ui/redundant_closure_call_late.stderr @@ -0,0 +1,10 @@ +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:15:5 + | +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 9603d9652b9d172842c77f566121437768bc962f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 19 Jul 2020 11:47:32 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 391d70af159..9477e79f567 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -11,7 +11,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintCon use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Detects closures called in the same expression where they @@ -96,9 +95,9 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, ident: &'tcx Ident) -> usize { + fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { struct ClosureUsageCount<'tcx> { - ident: &'tcx Ident, + path: &'tcx hir::Path<'tcx>, count: usize, }; impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { @@ -108,7 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if_chain! { if let hir::ExprKind::Call(ref closure, _) = expr.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; - if self.ident == &path.segments[0].ident; + if self.path.segments[0].ident == path.segments[0].ident + && self.path.res == path.res; then { self.count += 1; } @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { hir_visit::NestedVisitorMap::None } }; - let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, &ident) == 1; + if count_closure_usage(block, path) == 1; then { span_lint( cx, diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index 21ee58e5b44..e29a1dce0c7 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -14,9 +14,14 @@ fn main() { let redun_closure = || 1; i = redun_closure(); - // the lint is applicable here but the lint doesn't support redefinition - let redefined_closure = || 1; - i = redefined_closure(); - let redefined_closure = || 2; - i = redefined_closure(); + // shadowed closures are supported, lint here + let shadowed_closure = || 1; + i = shadowed_closure(); + let shadowed_closure = || 2; + i = shadowed_closure(); + + // don't lint here + let shadowed_closure = || 2; + i = shadowed_closure(); + i = shadowed_closure(); } diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index 0aebc3c419e..0e000fee85a 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -6,5 +6,17 @@ LL | i = redun_closure(); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: aborting due to previous error +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:19:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:21:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 1ac8b85c9fd38ff0a14061cae0b4d31e9bfafd56 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 20 Jul 2020 00:36:31 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 9477e79f567..8aa478ea2d6 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,13 +77,13 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "Try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared.", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; let hint = snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + diag.span_suggestion(expr.span, "try doing something like", hint, app); } }, ); @@ -136,13 +136,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(block, path) == 1; then { span_lint( cx, REDUNDANT_CLOSURE_CALL, second.span, - "Closure called just once immediately after it was declared", + "closure called just once immediately after it was declared", ); } } diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 0ac97ae25d0..79f27663461 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index e7737f9dd85..644161d9f5d 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,8 +1,8 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); - | ^^^^^^^^^ help: Try doing something like: : `42` + | ^^^^^^^^^ help: try doing something like: `42` | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index 0e000fee85a..7c8865f1bd3 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -1,4 +1,4 @@ -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:15:5 | LL | i = redun_closure(); @@ -6,13 +6,13 @@ LL | i = redun_closure(); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:19:5 | LL | i = shadowed_closure(); | ^^^^^^^^^^^^^^^^^^^^^^ -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:21:5 | LL | i = shadowed_closure(); -- cgit 1.4.1-3-g733a5 From a5cdd4aeb11fad6b0bf73d342398700a27c4484b Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Mon, 20 Jul 2020 00:00:00 +0000 Subject: 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 | 20 ++++++++++---------- 5 files changed, 47 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index fda7480194d..7932be0d4b1 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -124,6 +124,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } + if let ty::Adt(ref def, _) = arg_ty.kind { + if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { + continue; + } + } + // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }` let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 4c3462802e9..a515ee29c82 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -59,6 +59,7 @@ pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "Link pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; +pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"]; pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 764c10a6d39..cdeefda4c23 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 839747b131d..acb7ffb305f 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index eced198283c..89b39254299 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -108,61 +108,61 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:61:22 + --> $DIR/redundant_clone.rs:62:22 | LL | (a.clone(), a.clone()) | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:61:21 + --> $DIR/redundant_clone.rs:62:21 | LL | (a.clone(), a.clone()) | ^ error: redundant clone - --> $DIR/redundant_clone.rs:121:15 + --> $DIR/redundant_clone.rs:122:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:121:14 + --> $DIR/redundant_clone.rs:122:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:122:15 + --> $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:122:14 + --> $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"); | ^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 142a273441d1cf2039bd2857ee57cfa402e2a3f7 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 22 Jul 2020 05:23:55 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index fc10e5077b8..26a1c32b6b3 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -99,7 +99,9 @@ declare_clippy_lint! { /// if y != x {} // where both are floats /// /// // Good - /// let error = 0.01f64; // Use an epsilon for comparison + /// let error = f64::EPSILON; // Use an epsilon for comparison + /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. + /// // let error = std::f64::EPSILON; /// if (y - 1.23f64).abs() < error { } /// if (y - x).abs() > error { } /// ``` @@ -237,10 +239,12 @@ declare_clippy_lint! { /// const ONE: f64 = 1.00; /// /// // Bad - /// if x == ONE { } // where both are floats + /// if x == ONE { } // where both are floats /// /// // Good - /// let error = 0.1f64; // Use an epsilon for comparison + /// let error = f64::EPSILON; // Use an epsilon for comparison + /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. + /// // let error = std::f64::EPSILON; /// if (x - ONE).abs() < error { } /// ``` pub FLOAT_CMP_CONST, -- cgit 1.4.1-3-g733a5 From b375f1dd20bf07175ec06d13e1e9dc8b20287cd3 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 25 Jul 2020 17:09:44 +0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 97cc58023f5..56686f6d24f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1408,7 +1408,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), - ["next", "skip"] => lint_iter_skip_next(cx, expr), + ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), @@ -2436,17 +2436,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: ); } -fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { +fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) { // lint if caller of skip is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - span_lint_and_help( - cx, - ITER_SKIP_NEXT, - expr.span, - "called `skip(x).next()` on an iterator", - None, - "this is more succinctly expressed by calling `nth(x)`", - ); + if let [caller, n] = skip_args { + let hint = format!(".nth({})", snippet(cx, n.span, "..")); + span_lint_and_sugg( + cx, + ITER_SKIP_NEXT, + expr.span.trim_start(caller.span).unwrap(), + "called `skip(x).next()` on an iterator", + "use `nth` instead", + hint, + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed new file mode 100644 index 00000000000..928b6acb951 --- /dev/null +++ b/tests/ui/iter_skip_next.fixed @@ -0,0 +1,22 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_skip_next)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn main() { + let some_vec = vec![0, 1, 2, 3]; + let _ = some_vec.iter().nth(42); + let _ = some_vec.iter().cycle().nth(42); + let _ = (1..10).nth(10); + let _ = &some_vec[..].iter().nth(3); + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip(42).next(); + let _ = foo.filter().skip(42).next(); +} diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs index a65ca3bbb13..7075e2598eb 100644 --- a/tests/ui/iter_skip_next.rs +++ b/tests/ui/iter_skip_next.rs @@ -1,15 +1,17 @@ +// run-rustfix // aux-build:option_helpers.rs #![warn(clippy::iter_skip_next)] #![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; /// Checks implementation of `ITER_SKIP_NEXT` lint -fn iter_skip_next() { - let mut some_vec = vec![0, 1, 2, 3]; +fn main() { + let some_vec = vec![0, 1, 2, 3]; let _ = some_vec.iter().skip(42).next(); let _ = some_vec.iter().cycle().skip(42).next(); let _ = (1..10).skip(10).next(); @@ -18,5 +20,3 @@ fn iter_skip_next() { let _ = foo.skip(42).next(); let _ = foo.filter().skip(42).next(); } - -fn main() {} diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index 5709f335529..feedc2f288a 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,35 +1,28 @@ error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:13:13 + --> $DIR/iter_skip_next.rs:15:28 | LL | let _ = some_vec.iter().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` | = note: `-D clippy::iter-skip-next` implied by `-D warnings` - = help: this is more succinctly expressed by calling `nth(x)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:14:13 + --> $DIR/iter_skip_next.rs:16:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:15:13 + --> $DIR/iter_skip_next.rs:17:20 | LL | let _ = (1..10).skip(10).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:14 + --> $DIR/iter_skip_next.rs:18:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From c81bbd05b95ae03da9af4e7e25e3784edd039465 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 25 Jul 2020 23:58:22 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a48ad3185e9..1bf37632e32 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,6 +1,6 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, - span_lint_and_help, span_lint_and_sugg, + get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, + snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -79,6 +79,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if let Some(parent_expr) = get_parent_expr(cx, e) { + if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if &*parent_name.ident.as_str() != "into_iter" { + return; + } + } + } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); if TyS::same_type(a, b) { diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index fdd4bc581f3..813cdaecaa9 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 4cae745e7c0..540fea23b36 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 84ec5370278..b958b035452 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -23,43 +23,43 @@ LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:51:21 + --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:52:21 + --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:53:13 + --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:54:13 + --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:55:13 + --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:56:13 + --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:57:21 + --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -- cgit 1.4.1-3-g733a5 From 5a644964fc05752a1283dab238b81de7583f7d03 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 20:40:57 -0600 Subject: run cargo dev new_lint specifically: cargo dev new_lint --name derive_ord_xor_partial_ord --category correctness --pass late --- CHANGELOG.md | 1 + clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..a1780725044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1454,6 +1454,7 @@ Released 2018-09-13 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive_ord_xor_partial_ord.rs new file mode 100644 index 00000000000..7913aab6f24 --- /dev/null +++ b/clippy_lints/src/derive_ord_xor_partial_ord.rs @@ -0,0 +1,28 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "default lint description" +} + +declare_lint_pass!(DeriveOrdXorPartialOrd => [DERIVE_ORD_XOR_PARTIAL_ORD]); + +impl LateLintPass<'_, '_> for DeriveOrdXorPartialOrd {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..6d6dd06cc21 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,6 +173,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod derive_ord_xor_partial_ord; mod doc; mod double_comparison; mod double_parens; @@ -515,6 +516,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_HASH_XOR_EQ, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1230,6 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&double_comparison::DOUBLE_COMPARISONS), @@ -1648,6 +1651,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&drop_bounds::DROP_BOUNDS), LintId::of(&drop_forget_ref::DROP_COPY), LintId::of(&drop_forget_ref::DROP_REF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..00d3df8f94f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -360,6 +360,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "derive_ord_xor_partial_ord", + group: "correctness", + desc: "default lint description", + deprecation: None, + module: "derive_ord_xor_partial_ord", + }, Lint { name: "diverging_sub_expression", group: "complexity", diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs new file mode 100644 index 00000000000..63687e7b3db --- /dev/null +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -0,0 +1,5 @@ +#![warn(clippy::derive_ord_xor_partial_ord)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From fc20ee63a105c0df78113126e8749f5958d7dc47 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 20:54:04 -0600 Subject: 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 ++++++++++++++++++++- clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 59c62f1ae94..627475ee1d9 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -43,6 +43,27 @@ declare_clippy_lint! { "deriving `Hash` but implementing `PartialEq` explicitly" } +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "default lint description" +} + declare_clippy_lint! { /// **What it does:** Checks for explicit `Clone` implementations for `Copy` /// types. @@ -103,7 +124,7 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]); +declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE]); impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { diff --git a/clippy_lints/src/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive_ord_xor_partial_ord.rs deleted file mode 100644 index 7913aab6f24..00000000000 --- a/clippy_lints/src/derive_ord_xor_partial_ord.rs +++ /dev/null @@ -1,28 +0,0 @@ -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; - -declare_clippy_lint! { - /// **What it does:** - /// - /// **Why is this bad?** - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub DERIVE_ORD_XOR_PARTIAL_ORD, - correctness, - "default lint description" -} - -declare_lint_pass!(DeriveOrdXorPartialOrd => [DERIVE_ORD_XOR_PARTIAL_ORD]); - -impl LateLintPass<'_, '_> for DeriveOrdXorPartialOrd {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6d6dd06cc21..996aad31d3e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,7 +173,6 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; -mod derive_ord_xor_partial_ord; mod doc; mod double_comparison; mod double_parens; @@ -514,9 +513,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, &derive::DERIVE_HASH_XOR_EQ, + &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, - &derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1232,7 +1231,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&double_comparison::DOUBLE_COMPARISONS), @@ -1651,7 +1650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&drop_bounds::DROP_BOUNDS), LintId::of(&drop_forget_ref::DROP_COPY), LintId::of(&drop_forget_ref::DROP_REF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 00d3df8f94f..011504710e1 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -365,7 +365,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "correctness", desc: "default lint description", deprecation: None, - module: "derive_ord_xor_partial_ord", + module: "derive", }, Lint { name: "diverging_sub_expression", -- cgit 1.4.1-3-g733a5 From 068acbd27b19a4a7be3a9d00954ecfad8a0e6553 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 22:04:46 -0600 Subject: 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(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 627475ee1d9..4f69c2d7af7 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -137,6 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let is_automatically_derived = is_automatically_derived(&*item.attrs); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); + check_ord_pord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); @@ -201,6 +202,60 @@ fn check_hash_peq<'tcx>( } } +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +fn check_ord_pord<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if_chain! { + if match_path(&trait_ref.path, &paths::ORD); + if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); + if let Some(def_id) = &trait_ref.trait_def_id(); + if !def_id.is_local(); + then { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(pord_trait_def_id, ty, |impl_id| { + let pord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + + if pord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.substs.type_at(1) == ty { + let mess = if pord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then( + cx, + DERIVE_ORD_XOR_PARTIAL_ORD, + span, + mess, + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + diag.span_note( + cx.tcx.hir().span(hir_id), + "`PartialOrd` implemented here" + ); + } + } + ); + } + }); + } + } +} + /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { if match_path(&trait_ref.path, &paths::CLONE_TRAIT) { -- cgit 1.4.1-3-g733a5 From a8d6eda93049f0077c1515bec35fe0359ea43f96 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:04:04 -0600 Subject: use get_trait_def_id to check for Ord trait --- clippy_lints/src/derive.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 4f69c2d7af7..04395621e9e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -211,9 +211,10 @@ fn check_ord_pord<'tcx>( ord_is_automatically_derived: bool, ) { if_chain! { - if match_path(&trait_ref.path, &paths::ORD); + if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD); if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); if let Some(def_id) = &trait_ref.trait_def_id(); + if *def_id == ord_trait_def_id; if !def_id.is_local(); then { // Look for the PartialOrd implementations for `ty` -- cgit 1.4.1-3-g733a5 From 7dc974815ec8736f026dc10a070137e0d4601d52 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:06:36 -0600 Subject: remove is_local check since getting the def_id directly makes it unnecessary --- clippy_lints/src/derive.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 04395621e9e..ab001f7773e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -215,7 +215,6 @@ fn check_ord_pord<'tcx>( if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); if let Some(def_id) = &trait_ref.trait_def_id(); if *def_id == ord_trait_def_id; - if !def_id.is_local(); then { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(pord_trait_def_id, ty, |impl_id| { -- cgit 1.4.1-3-g733a5 From 431924ccf69bc4d4f0597f12749e8b1bcb285710 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:15:36 -0600 Subject: add description for derive_ord_xor_partial_ord --- clippy_lints/src/derive.rs | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index ab001f7773e..84566252abd 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -44,20 +44,50 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** + /// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` + /// explicitly or vice versa. + /// + /// **Why is this bad?** 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`: /// - /// **Why is this bad?** + /// ```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, -- cgit 1.4.1-3-g733a5 From 668b7474b47791c8c9af10130356b681b3bf3a84 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:30:00 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 84566252abd..16a6f0c20e1 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, + span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -154,7 +155,12 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE]); +declare_lint_pass!(Derive => [ + EXPL_IMPL_CLONE_ON_COPY, + DERIVE_HASH_XOR_EQ, + DERIVE_ORD_XOR_PARTIAL_ORD, + UNSAFE_DERIVE_DESERIALIZE +]); impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 15f66b7a9c5..b82dc518a3b 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -65,4 +65,4 @@ mod use_ord { } } -fn main() {} \ No newline at end of file +fn main() {} -- cgit 1.4.1-3-g733a5 From ca03f2b650a022d06df6c02c8947a74944815381 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Mon, 27 Jul 2020 00:21:11 -0600 Subject: s/pord/partial_ord/ to fix dogfood failure --- clippy_lints/src/derive.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 16a6f0c20e1..820ce85cff2 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -173,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let is_automatically_derived = is_automatically_derived(&*item.attrs); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); - check_ord_pord(cx, item.span, trait_ref, ty, is_automatically_derived); + check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); @@ -239,7 +239,7 @@ fn check_hash_peq<'tcx>( } /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. -fn check_ord_pord<'tcx>( +fn check_ord_partial_ord<'tcx>( cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, @@ -248,15 +248,15 @@ fn check_ord_pord<'tcx>( ) { if_chain! { if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD); - if let Some(pord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); + if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); if let Some(def_id) = &trait_ref.trait_def_id(); if *def_id == ord_trait_def_id; then { // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(pord_trait_def_id, ty, |impl_id| { - let pord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); - if pord_is_automatically_derived == ord_is_automatically_derived { + if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; } @@ -265,7 +265,7 @@ fn check_ord_pord<'tcx>( // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] if trait_ref.substs.type_at(1) == ty { - let mess = if pord_is_automatically_derived { + let mess = if partial_ord_is_automatically_derived { "you are implementing `Ord` explicitly but have derived `PartialOrd`" } else { "you are deriving `Ord` but have implemented `PartialOrd` explicitly" -- cgit 1.4.1-3-g733a5 From 12a6eee045f30785a1eb7572a4cfea3c5cec8a4c Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Mon, 27 Jul 2020 00:22:39 -0600 Subject: fill in lint description for DERIVE_ORD_XOR_PARTIAL_ORD --- clippy_lints/src/derive.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 820ce85cff2..cdb748de0c0 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,6 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, - span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -92,7 +91,7 @@ declare_clippy_lint! { /// ``` pub DERIVE_ORD_XOR_PARTIAL_ORD, correctness, - "default lint description" + "deriving `Ord` but implementing `PartialOrd` explicitly" } declare_clippy_lint! { -- cgit 1.4.1-3-g733a5 From 94b10a6e5ab003a03b6c7b60ffe5a3b366e0529a Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Mon, 27 Jul 2020 00:31:09 -0600 Subject: run cargo dev update_lints --- clippy_lints/src/derive.rs | 3 ++- src/lintlist/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index cdb748de0c0..08d8100a885 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, + span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 011504710e1..119908b3cc4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -363,7 +363,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "derive_ord_xor_partial_ord", group: "correctness", - desc: "default lint description", + desc: "deriving `Ord` but implementing `PartialOrd` explicitly", deprecation: None, module: "derive", }, -- cgit 1.4.1-3-g733a5 From 3a9ccffed8c5329e3fda67c2e310086ba261e15f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 27 Jul 2020 22:27:54 +0900 Subject: `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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs old mode 100755 new mode 100644 -- cgit 1.4.1-3-g733a5 From 94c50bc8c913ef58eba0f4f10b682dcf6d6e0991 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 28 Jul 2020 16:23:47 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..0ca4d88ed38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1723,6 +1723,7 @@ Released 2018-09-13 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..07ef087c2b0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -786,6 +786,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, &transmute::TRANSMUTE_BYTES_TO_STR, @@ -1174,6 +1175,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), + LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 0ef70311fb1..6bfdac37180 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_ use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{GenericBound, Generics, WherePredicate}; +use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds @@ -29,6 +30,35 @@ declare_clippy_lint! { "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } +declare_clippy_lint! { + /// **What it does:** Checks for cases where generics are being used and multiple + /// syntax specifications for trait bounds are used simultaneously. + /// + /// **Why is this bad?** Duplicate bounds makes the code + /// less readable than specifing them only once. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// fn func(arg: T) {} + /// ``` + /// or + /// /// + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + pub TRAIT_DUPLICATION_IN_BOUNDS, + pedantic, + "Check if the same trait bounds are specifed twice during a function declaration" +} + #[derive(Copy, Clone)] pub struct TraitBounds { max_trait_bounds: u64, @@ -41,10 +71,25 @@ impl TraitBounds { } } -impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + self.check_type_repetition(cx, gen); + check_trait_bound_duplication(cx, gen); + } +} + +fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { + if let GenericBound::Trait(t, _) = bound { + Some((t.trait_ref.path.res, t.span)) + } else { + None + } +} + +impl TraitBounds { + fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if in_macro(gen.span) { return; } @@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } } + +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + if in_macro(gen.span) { + return; + } + + let mut map = FxHashMap::default(); + for param in gen.params { + if let ParamName::Plain(ref ident) = param.name { + let res = param + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .collect::>(); + map.insert(*ident, res); + } + } + + for predicate in gen.where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !in_macro(bound_predicate.span); + if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind; + if let QPath::Resolved(_, Path { ref segments, .. }) = path; + if let Some(segment) = segments.first(); + if let Some(trait_resolutions_direct) = map.get(&segment.ident); + then { + for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { + if let Some((_, span_direct)) = trait_resolutions_direct + .iter() + .find(|(res_direct, _)| *res_direct == res_where) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + *span_direct, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); + } + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..9fb3dfc96ec 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "trait_duplication_in_bounds", + group: "pedantic", + desc: "Check if the same trait bounds are specifed twice during a function declaration", + deprecation: None, + module: "trait_bounds", + }, Lint { name: "transmute_bytes_to_str", group: "complexity", diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs new file mode 100644 index 00000000000..cb2b0054e35 --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -0,0 +1,31 @@ +#![deny(clippy::trait_duplication_in_bounds)] + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +fn bad_foo(arg0: T, arg1: Z) +where + T: Clone, + T: Default, +{ + unimplemented!(); +} + +fn good_bar(arg: T) { + unimplemented!(); +} + +fn good_foo(arg: T) +where + T: Clone + Default, +{ + unimplemented!(); +} + +fn good_foobar(arg: T) +where + T: Clone, +{ + unimplemented!(); +} + +fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr new file mode 100644 index 00000000000..027e1c75204 --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -0,0 +1,23 @@ +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:15 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/trait_duplication_in_bounds.rs:1:9 + | +LL | #![deny(clippy::trait_duplication_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:23 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 2b7fde6a4b18ee837342f5b50a4c4941e919177f Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 29 Jul 2020 16:10:15 +0200 Subject: typo fix --- clippy_lints/src/trait_bounds.rs | 2 +- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 6bfdac37180..10811374875 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { /// ``` pub TRAIT_DUPLICATION_IN_BOUNDS, pedantic, - "Check if the same trait bounds are specifed twice during a function declaration" + "Check if the same trait bounds are specified twice during a function declaration" } #[derive(Copy, Clone)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 9fb3dfc96ec..197eab759f1 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2169,7 +2169,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "trait_duplication_in_bounds", group: "pedantic", - desc: "Check if the same trait bounds are specifed twice during a function declaration", + desc: "Check if the same trait bounds are specified twice during a function declaration", deprecation: None, module: "trait_bounds", }, -- cgit 1.4.1-3-g733a5 From a427c99f3d2a0b2c55d19af73bcad81f1dc761ab Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 25 Jul 2020 20:04:59 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9edcdd979ff..3f62a3cab1c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2569,17 +2569,35 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; + let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); + let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind { + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + } else { + false + }; + + let method_to_use = if is_map_to_option { + // `(...).map(...)` has type `impl Iterator> + "filter_map" + } else { + // `(...).map(...)` has type `impl Iterator> + "flat_map" + }; + let msg = &format!( + "called `map(..).flatten()` on an `Iterator`. \ + This is more succinctly expressed by calling `.{}(..)`", + method_to_use + ); let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); + let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, expr.span, msg, - "try using `flat_map` instead", + &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, ); diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 4171d80f48a..684a28aebcb 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 16a0fd090ad..05789ee5232 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 00bc41c15e9..d2d15362a6c 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,16 +1,22 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)` --> $DIR/map_flatten.rs:8:21 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))` | = note: `-D clippy::map-flatten` implied by `-D warnings` +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` + --> $DIR/map_flatten.rs:9:21 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:9:24 + --> $DIR/map_flatten.rs:10:24 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From d4ba561aafb501972f581c1f8e6d1885959f9306 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Thu, 30 Jul 2020 22:20:31 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3f62a3cab1c..9217324b18c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2570,11 +2570,16 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); - let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind { - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) - } else { - false + let is_map_to_option = match map_closure_ty.kind { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + }, + _ => false, }; let method_to_use = if is_map_to_option { @@ -2584,19 +2589,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // `(...).map(...)` has type `impl Iterator> "flat_map" }; - let msg = &format!( - "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.{}(..)`", - method_to_use - ); - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet); + let hint = format!(".{0}({1})", method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Iterator`", &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, @@ -2605,16 +2604,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Option if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { - let msg = "called `map(..).flatten()` on an `Option`. \ - This is more succinctly expressed by calling `.and_then(..)`"; - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.and_then({1})", self_snippet, func_snippet); + let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Option`", "try using `and_then` instead", hint, Applicability::MachineApplicable, diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 684a28aebcb..a5fdf7df613 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,7 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 05789ee5232..abbc4e16e56 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,7 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index d2d15362a6c..b6479cd69ea 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,22 +1,40 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)` - --> $DIR/map_flatten.rs:8:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:14:46 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` | = note: `-D clippy::map-flatten` implied by `-D warnings` -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` - --> $DIR/map_flatten.rs:9:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:15:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:16:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:17:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:20:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` -error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:10:24 +error: called `map(..).flatten()` on an `Option` + --> $DIR/map_flatten.rs:23:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From cb00cdf0d77e6a21cd64558f1e373e696f31d301 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 2 Aug 2020 11:25:03 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c29432bf933..ed02763397a 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,6 +1,5 @@ //! checks for attributes -use crate::reexport::Name; use crate::utils::{ first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, without_block_comments, @@ -514,7 +513,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: & } } -fn check_attrs(cx: &LateContext<'_>, span: Span, name: Name, attrs: &[Attribute]) { +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { if span.from_expansion() { return; } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7a4ca3902b3..b336de37c61 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -321,10 +321,6 @@ mod zero_div_zero; pub use crate::utils::conf::Conf; -mod reexport { - pub use rustc_span::Symbol as Name; -} - /// Register all pre expansion lints /// /// Pre-expansion lints run before any macro expansion has happened. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 168f9f953e4..1b3639a02a0 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,9 +13,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::kw; +use rustc_span::symbol::{Symbol, kw}; -use crate::reexport::Name; use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { @@ -113,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { enum RefLt { Unnamed, Static, - Named(Name), + Named(Symbol), } fn check_fn_inner<'tcx>( @@ -456,7 +455,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl } struct LifetimeChecker { - map: FxHashMap, + map: FxHashMap, } impl<'tcx> Visitor<'tcx> for LifetimeChecker { diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 396bb659109..d0d6c2e04a0 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,5 +1,4 @@ use crate::consts::constant; -use crate::reexport::Name; use crate::utils::paths; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ @@ -1184,7 +1183,7 @@ fn check_for_loop_range<'tcx>( } } -fn is_len_call(expr: &Expr<'_>, var: Name) -> bool { +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { if_chain! { if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind; if len_args.len() == 1; @@ -1632,15 +1631,15 @@ struct VarVisitor<'a, 'tcx> { /// var name to look for as index var: HirId, /// indexed variables that are used mutably - indexed_mut: FxHashSet, + indexed_mut: FxHashSet, /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global - indexed_indirectly: FxHashMap>, + indexed_indirectly: FxHashMap>, /// subset of `indexed` of vars that are indexed directly: `v[i]` /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` - indexed_directly: FxHashMap, Ty<'tcx>)>, + indexed_directly: FxHashMap, Ty<'tcx>)>, /// Any names that are used outside an index operation. /// Used to detect things like `&mut vec` used together with `vec[i]` - referenced: FxHashSet, + referenced: FxHashSet, /// has the loop variable been used in expressions other than the index of /// an index op? nonindex: bool, @@ -1996,7 +1995,7 @@ struct InitializeVisitor<'a, 'tcx> { end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, state: VarState, - name: Option, + name: Option, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2159,7 +2158,7 @@ use self::Nesting::{LookFurther, RuledOut, Unknown}; struct LoopNestVisitor { hir_id: HirId, - iterator: Name, + iterator: Symbol, nesting: Nesting, } @@ -2210,7 +2209,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { } } -fn path_name(e: &Expr<'_>) -> Option { +fn path_name(e: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind { let segments = &path.segments; if segments.len() == 1 { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index fab13c8c124..97bd52e517c 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,4 +1,3 @@ -use crate::reexport::Name; use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -10,6 +9,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; declare_clippy_lint! { /// **What it does:** Checks for bindings that shadow other bindings already in @@ -123,7 +123,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo check_expr(cx, &body.value, &mut bindings); } -fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) { let len = bindings.len(); for stmt in block.stmts { match stmt.kind { @@ -138,7 +138,7 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: & bindings.truncate(len); } -fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) { if in_external_macro(cx.sess(), local.span) { return; } @@ -173,7 +173,7 @@ fn check_pat<'tcx>( pat: &'tcx Pat<'_>, init: Option<&'tcx Expr<'_>>, span: Span, - bindings: &mut Vec<(Name, Span)>, + bindings: &mut Vec<(Symbol, Span)>, ) { // TODO: match more stuff / destructuring match pat.kind { @@ -254,7 +254,7 @@ fn check_pat<'tcx>( fn lint_shadow<'tcx>( cx: &LateContext<'tcx>, - name: Name, + name: Symbol, span: Span, pattern_span: Span, init: Option<&'tcx Expr<'_>>, @@ -315,7 +315,7 @@ fn lint_shadow<'tcx>( } } -fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) { if in_external_macro(cx.sess(), expr.span) { return; } @@ -351,7 +351,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut } } -fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) { match ty.kind { TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), TyKind::Array(ref fty, ref anon_const) => { @@ -371,7 +371,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( } } -fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { +fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner), ExprKind::Block(ref block, _) => { @@ -383,6 +383,6 @@ fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { } } -fn path_eq_name(name: Name, path: &Path<'_>) -> bool { +fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool { !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str() } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4b7a1c2b537..39317ecc82e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -52,7 +52,6 @@ use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; use crate::consts::{constant, Constant}; -use crate::reexport::Name; /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). @@ -150,7 +149,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) } /// Checks if an expression references a variable of the given name. -pub fn match_var(expr: &Expr<'_>, var: Name) -> bool { +pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { if let [p] = path.segments { return p.ident.name == var; @@ -422,7 +421,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool { } /// Gets the name of the item the expression is in, if available. -pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); match cx.tcx.hir().find(parent_id) { Some( @@ -435,7 +434,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } /// Gets the name of a `Pat`, if any. -pub fn get_pat_name(pat: &Pat<'_>) -> Option { +pub fn get_pat_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ref spname, _) => Some(spname.name), PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), @@ -445,14 +444,14 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option { } struct ContainsName { - name: Name, + name: Symbol, result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName { type Map = Map<'tcx>; - fn visit_name(&mut self, _: Span, name: Name) { + fn visit_name(&mut self, _: Span, name: Symbol) { if self.name == name { self.result = true; } @@ -463,7 +462,7 @@ impl<'tcx> Visitor<'tcx> for ContainsName { } /// Checks if an `Expr` contains a certain name. -pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool { +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { let mut cn = ContainsName { name, result: false }; cn.visit_expr(expr); cn.result @@ -1029,7 +1028,7 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow } -pub fn get_arg_name(pat: &Pat<'_>) -> Option { +pub fn get_arg_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ident, None) => Some(ident.name), PatKind::Ref(ref subpat, _) => get_arg_name(subpat), -- cgit 1.4.1-3-g733a5 From bb6e857980748b000ba3b88d125c6b29aced0693 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 2 Aug 2020 14:22:54 +0200 Subject: fmt --- clippy_lints/src/lifetimes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 1b3639a02a0..4df6827d77f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,7 +13,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::{Symbol, kw}; +use rustc_span::symbol::{kw, Symbol}; use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; -- cgit 1.4.1-3-g733a5 From e336fe80d2f991a170b98190683039035b53c6ba Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 3 Aug 2020 00:36:28 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index c19fb148cda..864d1ea87f5 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -4,8 +4,8 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, - ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, + IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -27,8 +27,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// use std::future::Future; - /// /// async fn foo() -> i32 { 42 } /// ``` pub MANUAL_ASYNC_FN, @@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let IsAsync::NotAsync = header.asyncness; // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; - if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); if let Some(output) = future_output_ty(trait_ref); + if captures_all_lifetimes(decl.inputs, &output_lifetimes); // Check that the body of the function consists of one async block if let ExprKind::Block(block, _) = body.value.kind; if block.stmts.is_empty(); @@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { } } -fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { +fn future_trait_ref<'tcx>( + cx: &LateContext<'tcx>, + ty: &'tcx Ty<'tcx>, +) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { if_chain! { - if let TyKind::OpaqueDef(item_id, _) = ty.kind; + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind; let item = cx.tcx.hir().item(item_id.id); if let ItemKind::OpaqueTy(opaque) = &item.kind; - if opaque.bounds.len() == 1; - if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); + if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { + if let GenericBound::Trait(poly, _) = bound { + Some(&poly.trait_ref) + } else { + None + } + }); + if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { - return Some(&poly.trait_ref); + let output_lifetimes = bounds + .iter() + .filter_map(|bound| { + if let GenericArg::Lifetime(lt) = bound { + Some(lt.name) + } else { + None + } + }) + .collect(); + + return Some((trait_ref, output_lifetimes)); } } @@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t None } +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool { + let input_lifetimes: Vec = inputs + .iter() + .filter_map(|ty| { + if let TyKind::Rptr(lt, _) = ty.kind { + Some(lt.name) + } else { + None + } + }) + .collect(); + + // The lint should trigger in one of these cases: + // - There are no input lifetimes + // - There's only one output lifetime bound using `+ '_` + // - All input lifetimes are explicitly bound to the output + input_lifetimes.is_empty() + || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || input_lifetimes + .iter() + .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) +} + fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs index 5c1fdd83efb..0458950edee 100644 --- a/tests/ui/await_holding_lock.rs +++ b/tests/ui/await_holding_lock.rs @@ -47,6 +47,7 @@ async fn not_good(x: &Mutex) -> u32 { first + second + third } +#[allow(clippy::manual_async_fn)] fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 8c47cb37d8c..21bf49d16f0 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -46,13 +46,13 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:52:13 + --> $DIR/await_holding_lock.rs:53:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:52:9 + --> $DIR/await_holding_lock.rs:53:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 27222cc0869..4f551690c43 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -43,10 +43,6 @@ impl S { 42 } - async fn meth_fut(&self) -> i32 { 42 } - - async fn empty_fut(&self) {} - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -64,4 +60,40 @@ impl S { } } +// Tests related to lifetime capture + +async fn elided(_: &i32) -> i32 { 42 } + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index 6a0f1b26c88..6ed60309947 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -51,14 +51,6 @@ impl S { } } - fn meth_fut(&self) -> impl Future { - async { 42 } - } - - fn empty_fut(&self) -> impl Future { - async {} - } - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -76,4 +68,44 @@ impl S { } } +// Tests related to lifetime capture + +fn elided(_: &i32) -> impl Future + '_ { + async { 42 } +} + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + async { 42 } +} + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index a1904c904d0..ccd82867427 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -65,34 +65,34 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:54:5 + --> $DIR/manual_async_fn.rs:73:1 | -LL | fn meth_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn meth_fut(&self) -> i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn elided(_: &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn meth_fut(&self) -> impl Future { 42 } - | ^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { 42 } + | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:58:5 + --> $DIR/manual_async_fn.rs:82:1 | -LL | fn empty_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type +help: make the function `async` and return the output of the future directly | -LL | async fn empty_fut(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn empty_fut(&self) -> impl Future {} - | ^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } + | ^^^^^^ error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 3ee61373fe056efb46b6b1b243b31cec0d7e6099 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 22 Jul 2020 22:46:23 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7e3876ff49b..231c440463d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,14 +1,15 @@ use crate::consts::constant; use crate::reexport::Name; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var, - multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, SpanlessEq, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_path, + match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -17,7 +18,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; use rustc_hir::{ def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, - LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, + Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -27,7 +28,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -2358,6 +2359,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + // Check for direct, immediate usage if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2423,6 +2425,99 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } } + // Check for collecting it and then turning it back into an iterator later + if let ExprKind::Block(ref block, _) = expr.kind { + for ref stmt in block.stmts { + if_chain! { + // TODO also work for assignments to an existing variable + if let StmtKind::Local( + Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + init: Some(ref init_expr), .. } + ) = stmt.kind; + if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR); + if let Some(ref generic_args) = method_name.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + if let ty = cx.typeck_results().node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || + match_type(cx, ty, &paths::LINKED_LIST); + if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); + if iter_calls.len() == 1; + then { + // Suggest replacing iter_call with iter_replacement, and removing stmt + span_lint_and_then( + cx, + NEEDLESS_COLLECT, + stmt.span, + NEEDLESS_COLLECT_MSG, + |diag| { + let iter_replacement = Sugg::hir(cx, iter_source, "..").to_string(); + diag.multipart_suggestion( + "Use the original Iterator instead of collecting it and then producing a new one", + vec![ + (stmt.span, String::new()), + (iter_calls[0].span, iter_replacement) + ], + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } +} + +struct IntoIterVisitor<'tcx> { + iters: Vec<&'tcx Expr<'tcx>>, + seen_other: bool, + target: String, +} +impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::MethodCall( + method_name, + _, + &[Expr { + kind: ExprKind::Path(QPath::Resolved(_, ref path)), + .. + }], + _, + ) if match_path(path, &[&self.target]) => { + // TODO Check what method is being called, if it's called on target, and act + // accordingly + if method_name.ident.name == sym!(into_iter) { + self.iters.push(expr); + } else { + self.seen_other = true; + } + }, + _ => walk_expr(self, expr), + } + } + + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Detect the occurences of calls to `iter` or `into_iter` for the +/// given identifier +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option>> { + let mut visitor = IntoIterVisitor { + iters: Vec::new(), + target: identifier.name.to_ident_string(), + seen_other: false, + }; + visitor.visit_block(block); + if visitor.seen_other { + None + } else { + Some(visitor.iters) + } } fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index be37dc16b9a..60a3e206283 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -18,4 +18,15 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + let indirect_positive = sample.iter().collect::>(); + indirect_positive + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .iter() + .map(|x| (*x, *x + 1)) + .collect::>(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 1577e7a46ed..33a1ea36095 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -8,9 +8,6 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[allow(unused_variables, clippy::iter_cloned_collect)] fn main() { let sample = [1; 5]; - let indirect_with_into_iter = sample.iter().collect::>(); - let indirect_with_iter = sample.iter().collect::>();; - let indirect_negative = sample.iter().collect::>();; let len = sample.iter().collect::>().len(); if sample.iter().collect::>().is_empty() { // Empty @@ -21,8 +18,15 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - indirect_with_into_iter.into_iter().map(|x| (x, x+1)).collect::>(); - indirect_with_iter.iter().map(|x| (x, x+1)).collect::>(); - indirect_negative.iter().map(|x| (x, x+1)).collect::>(); - indirect_negative.iter().map(|x| (x, x+1)).collect::>(); + let indirect_positive = sample.iter().collect::>(); + indirect_positive + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .iter() + .map(|x| (*x, *x + 1)) + .collect::>(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 9113aad90dd..bb67bfa83e9 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,10 +1,21 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:21:5 + | +LL | let indirect_positive = sample.iter().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-collect` implied by `-D warnings` +help: Use the original Iterator instead of collecting it and then producing a new one + | +LL | +LL | sample.iter() + | + error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` - | - = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:12:15 @@ -24,5 +35,5 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 3657c92ac978f69667b9c8bb46e51bc602b3d7ee Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:14:10 -0700 Subject: Check for other things which can be used indirectly --- clippy_lints/src/loops.rs | 94 +++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 32 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 231c440463d..11a9b1e531c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2429,7 +2429,6 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if let ExprKind::Block(ref block, _) = expr.kind { for ref stmt in block.stmts { if_chain! { - // TODO also work for assignments to an existing variable if let StmtKind::Local( Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } @@ -2446,21 +2445,22 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if iter_calls.len() == 1; then { // Suggest replacing iter_call with iter_replacement, and removing stmt + let iter_call = &iter_calls[0]; span_lint_and_then( cx, NEEDLESS_COLLECT, - stmt.span, + stmt.span.until(iter_call.span), NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = Sugg::hir(cx, iter_source, "..").to_string(); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( - "Use the original Iterator instead of collecting it and then producing a new one", + iter_call.get_suggestion_text(), vec![ (stmt.span, String::new()), - (iter_calls[0].span, iter_replacement) + (iter_call.span, iter_replacement) ], - Applicability::MaybeIncorrect, - ); + Applicability::MachineApplicable,// MaybeIncorrect, + ).emit(); }, ); } @@ -2469,32 +2469,62 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } -struct IntoIterVisitor<'tcx> { - iters: Vec<&'tcx Expr<'tcx>>, +struct IterFunction { + func: IterFunctionKind, + span: Span, +} +impl IterFunction { + fn get_iter_method(&self, cx: &LateContext<'_>) -> String { + match &self.func { + IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::Len => String::from(".count()"), + IterFunctionKind::IsEmpty => String::from(".next().is_none()"), + IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")), + } + } + fn get_suggestion_text(&self) -> &'static str { + match &self.func { + IterFunctionKind::IntoIter => "Use the original Iterator instead of collecting it and then producing a new one", + IterFunctionKind::Len => "Take the original Iterator's count instead of collecting it and finding the length", + IterFunctionKind::IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty", + IterFunctionKind::Contains(_) => "Check if the original Iterator contains an element instead of collecting then checking", + } + } +} +enum IterFunctionKind { + IntoIter, + Len, + IsEmpty, + Contains(Span), +} + +struct IterFunctionVisitor { + uses: Vec, seen_other: bool, target: String, } -impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match &expr.kind { - ExprKind::MethodCall( - method_name, - _, - &[Expr { - kind: ExprKind::Path(QPath::Resolved(_, ref path)), - .. - }], - _, - ) if match_path(path, &[&self.target]) => { - // TODO Check what method is being called, if it's called on target, and act - // accordingly - if method_name.ident.name == sym!(into_iter) { - self.iters.push(expr); - } else { - self.seen_other = true; + if_chain! { + if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; + if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); + if match_path(path, &[&self.target]); + then { + let into_iter = sym!(into_iter); + let len = sym!(len); + let is_empty = sym!(is_empty); + let contains = sym!(contains); + match method_name.ident.name { + name if name == into_iter => self.uses.push(IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }), + name if name == len => self.uses.push(IterFunction { func: IterFunctionKind::Len, span: expr.span }), + name if name == is_empty => self.uses.push(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }), + name if name == contains => self.uses.push(IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }), + _ => self.seen_other = true, } - }, - _ => walk_expr(self, expr), + } + else { + walk_expr(self, expr); + } } } @@ -2506,9 +2536,9 @@ impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { /// Detect the occurences of calls to `iter` or `into_iter` for the /// given identifier -fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option>> { - let mut visitor = IntoIterVisitor { - iters: Vec::new(), +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { + let mut visitor = IterFunctionVisitor { + uses: Vec::new(), target: identifier.name.to_ident_string(), seen_other: false, }; @@ -2516,7 +2546,7 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) if visitor.seen_other { None } else { - Some(visitor.iters) + Some(visitor.uses) } } -- cgit 1.4.1-3-g733a5 From a84948329459e650af4eb2f8cff8f55282316ea5 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:44:44 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 11a9b1e531c..2181304f006 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2359,7 +2359,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - // Check for direct, immediate usage + check_needless_collect_direct_usage(expr, cx); + check_needless_collect_indirect_usage(expr, cx); +} +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2425,7 +2428,9 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } } - // Check for collecting it and then turning it back into an iterator later +} + +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if let ExprKind::Block(ref block, _) = expr.kind { for ref stmt in block.stmts { if_chain! { @@ -2484,10 +2489,18 @@ impl IterFunction { } fn get_suggestion_text(&self) -> &'static str { match &self.func { - IterFunctionKind::IntoIter => "Use the original Iterator instead of collecting it and then producing a new one", - IterFunctionKind::Len => "Take the original Iterator's count instead of collecting it and finding the length", - IterFunctionKind::IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty", - IterFunctionKind::Contains(_) => "Check if the original Iterator contains an element instead of collecting then checking", + IterFunctionKind::IntoIter => { + "Use the original Iterator instead of collecting it and then producing a new one" + }, + IterFunctionKind::Len => { + "Take the original Iterator's count instead of collecting it and finding the length" + }, + IterFunctionKind::IsEmpty => { + "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" + }, + IterFunctionKind::Contains(_) => { + "Check if the original Iterator contains an element instead of collecting then checking" + }, } } } @@ -2505,6 +2518,8 @@ struct IterFunctionVisitor { } impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + // TODO Check if the target identifier is being used in something other + // than a function call if_chain! { if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); @@ -2515,10 +2530,18 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { let is_empty = sym!(is_empty); let contains = sym!(contains); match method_name.ident.name { - name if name == into_iter => self.uses.push(IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }), - name if name == len => self.uses.push(IterFunction { func: IterFunctionKind::Len, span: expr.span }), - name if name == is_empty => self.uses.push(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }), - name if name == contains => self.uses.push(IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }), + name if name == into_iter => self.uses.push( + IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } + ), + name if name == len => self.uses.push( + IterFunction { func: IterFunctionKind::Len, span: expr.span } + ), + name if name == is_empty => self.uses.push( + IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span } + ), + name if name == contains => self.uses.push( + IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span } + ), _ => self.seen_other = true, } } diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed index 136af42a9fe..163eaf965dd 100644 --- a/tests/ui/needless_collect_indirect.fixed +++ b/tests/ui/needless_collect_indirect.fixed @@ -1,16 +1,12 @@ // run-rustfix #[allow(unused)] - use std::collections::{HashMap, VecDeque}; fn main() { let sample = [1; 5]; let indirect_iter = sample.iter().collect::>(); - indirect_iter - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); let indirect_len = sample.iter().collect::>(); indirect_len.len(); let indirect_empty = sample.iter().collect::>(); diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 136af42a9fe..163eaf965dd 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,16 +1,12 @@ // run-rustfix #[allow(unused)] - use std::collections::{HashMap, VecDeque}; fn main() { let sample = [1; 5]; let indirect_iter = sample.iter().collect::>(); - indirect_iter - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); let indirect_len = sample.iter().collect::>(); indirect_len.len(); let indirect_empty = sample.iter().collect::>(); diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 5058c171ac2..700c73b0b22 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,19 +1,19 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:5 + --> $DIR/needless_collect_indirect.rs:8:5 | LL | / let indirect_iter = sample.iter().collect::>(); -LL | | indirect_iter +LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); | |____^ | = note: `-D clippy::needless-collect` implied by `-D warnings` help: Use the original Iterator instead of collecting it and then producing a new one | LL | -LL | sample.iter() +LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:14:5 + --> $DIR/needless_collect_indirect.rs:10:5 | LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); @@ -26,7 +26,7 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:16:5 + --> $DIR/needless_collect_indirect.rs:12:5 | LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); @@ -39,7 +39,7 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:18:5 + --> $DIR/needless_collect_indirect.rs:14:5 | LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); -- cgit 1.4.1-3-g733a5 From bb2c14e92b5a0eafe9c9f43f3a02e33b544b3f91 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 10:07:51 -0700 Subject: Fix a bug causing it to be too trigger-happy --- clippy_lints/src/loops.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2181304f006..c931c212735 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,10 +5,9 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_path, - match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, - snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - SpanlessEq, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -2514,16 +2513,16 @@ enum IterFunctionKind { struct IterFunctionVisitor { uses: Vec, seen_other: bool, - target: String, + target: Ident, } impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - // TODO Check if the target identifier is being used in something other - // than a function call + // Check function calls on our collection if_chain! { if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); - if match_path(path, &[&self.target]); + if let &[name] = &path.segments; + if name.ident == self.target; then { let into_iter = sym!(into_iter); let len = sym!(len); @@ -2544,8 +2543,17 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { ), _ => self.seen_other = true, } + return } - else { + } + // Check if the collection is used for anything else + if_chain! { + if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr; + if let &[name] = &path.segments; + if name.ident == self.target; + then { + self.seen_other = true; + } else { walk_expr(self, expr); } } @@ -2562,7 +2570,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { let mut visitor = IterFunctionVisitor { uses: Vec::new(), - target: identifier.name.to_ident_string(), + target: identifier, seen_other: false, }; visitor.visit_block(block); -- cgit 1.4.1-3-g733a5 From 5e10b039a3f215b48a54ce7dd38f95115f92e55a Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 2 Aug 2020 21:46:18 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c931c212735..e9ce804320f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -2442,7 +2442,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(ref generic_args) = method_name.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); @@ -2524,12 +2524,11 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { if let &[name] = &path.segments; if name.ident == self.target; then { - let into_iter = sym!(into_iter); let len = sym!(len); let is_empty = sym!(is_empty); let contains = sym!(contains); match method_name.ident.name { - name if name == into_iter => self.uses.push( + sym::into_iter => self.uses.push( IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } ), name if name == len => self.uses.push( diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed deleted file mode 100644 index 163eaf965dd..00000000000 --- a/tests/ui/needless_collect_indirect.fixed +++ /dev/null @@ -1,22 +0,0 @@ -// run-rustfix - -#[allow(unused)] -use std::collections::{HashMap, VecDeque}; - -fn main() { - let sample = [1; 5]; - let indirect_iter = sample.iter().collect::>(); - indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); - let indirect_len = sample.iter().collect::>(); - indirect_len.len(); - let indirect_empty = sample.iter().collect::>(); - indirect_empty.is_empty(); - let indirect_contains = sample.iter().collect::>(); - indirect_contains.contains(&&5); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .into_iter() - .map(|x| (*x, *x + 1)) - .collect::>(); -} diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 163eaf965dd..4cf03e82035 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,6 +1,3 @@ -// run-rustfix - -#[allow(unused)] use std::collections::{HashMap, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 700c73b0b22..0c1e61d7496 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:8:5 + --> $DIR/needless_collect_indirect.rs:5:5 | LL | / let indirect_iter = sample.iter().collect::>(); LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); @@ -13,7 +13,7 @@ LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:10:5 + --> $DIR/needless_collect_indirect.rs:7:5 | LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); @@ -26,7 +26,7 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:12:5 + --> $DIR/needless_collect_indirect.rs:9:5 | LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); @@ -39,7 +39,7 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:14:5 + --> $DIR/needless_collect_indirect.rs:11:5 | LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); -- cgit 1.4.1-3-g733a5 From e521c67e5f29ddd84e0ce744dfc27a836d349514 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Mon, 3 Aug 2020 12:32:23 +0200 Subject: early return on empty parameters/where clause --- clippy_lints/src/trait_bounds.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 10811374875..06631f89f27 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -148,7 +148,7 @@ impl TraitBounds { } fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if in_macro(gen.span) { + if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() { return; } -- cgit 1.4.1-3-g733a5 From 25abd7ae76e2a708dda5487119c20af3be64edb7 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 8 Jul 2020 20:29:56 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..43a32e828d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1701,6 +1701,7 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..9fc07e07fd3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -288,6 +288,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod stable_sort_primitive; mod strings; mod suspicious_trait_impl; mod swap; @@ -776,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -1078,6 +1080,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1408,6 +1411,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1723,6 +1727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mutex_atomic::MUTEX_ATOMIC), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs new file mode 100644 index 00000000000..c48da004a60 --- /dev/null +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -0,0 +1,130 @@ +use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; + +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// When sorting primitive values (integers, bools, chars, as well + /// as arrays, slices, and tuples of such items), it is better to + /// use an unstable sort than a stable sort. + /// + /// **Why is this bad?** + /// Using a stable sort consumes more memory and cpu cycles. Because + /// values which compare equal are identical, preserving their + /// relative order (the guarantee that a stable sort provides) means + /// nothing, while the extra costs still apply. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort(); + /// ``` + /// Use instead: + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort_unstable(); + /// ``` + pub STABLE_SORT_PRIMITIVE, + perf, + "use of sort() when sort_unstable() is equivalent" +} + +declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); + +/// The three "kinds" of sorts +enum SortingKind { + Vanilla, + // The other kinds of lint are currently commented out because they + // can map distinct values to equal ones. If the key function is + // provably one-to-one, or if the Cmp function conserves equality, + // then they could be linted on, but I don't know if we can check + // for that. + + // ByKey, + // ByCmp, +} +impl SortingKind { + /// The name of the stable version of this kind of sort + fn stable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort", + // SortingKind::ByKey => "sort_by_key", + // SortingKind::ByCmp => "sort_by", + } + } + /// The name of the unstable version of this kind of sort + fn unstable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort_unstable", + // SortingKind::ByKey => "sort_unstable_by_key", + // SortingKind::ByCmp => "sort_unstable_by", + } + } + /// Takes the name of a function call and returns the kind of sort + /// that corresponds to that function name (or None if it isn't) + fn from_stable_name(name: &str) -> Option { + match name { + "sort" => Some(SortingKind::Vanilla), + // "sort_by" => Some(SortingKind::ByCmp), + // "sort_by_key" => Some(SortingKind::ByKey), + _ => None, + } + } +} + +/// A detected instance of this lint +struct LintDetection { + slice_name: String, + method: SortingKind, + method_args: String, +} + +fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(slice) = &args.get(0); + if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); + if is_slice_of_primitives(cx, slice); + then { + let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + } else { + None + } + } +} + +impl LateLintPass<'_> for StableSortPrimitive { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(detection) = detect_stable_sort_primitive(cx, expr) { + span_lint_and_sugg( + cx, + STABLE_SORT_PRIMITIVE, + expr.span, + format!( + "Use {} instead of {}", + detection.method.unstable_name(), + detection.method.stable_name() + ) + .as_str(), + "try", + format!( + "{}.{}({})", + detection.slice_name, + detection.method.unstable_name(), + detection.method_args + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 655b1133cf7..c75f8042907 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1378,6 +1378,36 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo }) } +/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point +/// number type, a str, or an array, slice, or tuple of those types). +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, + ty::Ref(_, inner, _) if inner.kind == ty::Str => true, + ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), + ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), + _ => false, + } +} + +/// Returns true iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function). +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty_adjusted(expr); + match expr_type.kind { + ty::Slice(ref element_type) + | ty::Ref( + _, + ty::TyS { + kind: ty::Slice(ref element_type), + .. + }, + _, + ) => is_recursively_primitive_type(element_type), + _ => false, + } +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..41d06a67881 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2026,6 +2026,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "stable_sort_primitive", + group: "perf", + desc: "use of sort() when sort_unstable() is equivalent", + deprecation: None, + module: "stable_sort_primitive", + }, Lint { name: "string_add", group: "restriction", diff --git a/tests/ui/stable_sort_primitive.fixed b/tests/ui/stable_sort_primitive.fixed new file mode 100644 index 00000000000..8f8f5665931 --- /dev/null +++ b/tests/ui/stable_sort_primitive.fixed @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort_unstable(); + let mut vec = vec![false, false, true]; + vec.sort_unstable(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort_unstable(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort_unstable(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort_unstable(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort_unstable(); + let mut arr = [1, 3, 2]; + arr.sort_unstable(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/tests/ui/stable_sort_primitive.rs b/tests/ui/stable_sort_primitive.rs new file mode 100644 index 00000000000..f9bd9779067 --- /dev/null +++ b/tests/ui/stable_sort_primitive.rs @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort(); + let mut vec = vec![false, false, true]; + vec.sort(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort(); + let mut arr = [1, 3, 2]; + arr.sort(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr new file mode 100644 index 00000000000..b0b729ede48 --- /dev/null +++ b/tests/ui/stable_sort_primitive.stderr @@ -0,0 +1,46 @@ +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:7:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:9:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:11:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:13:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:15:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:17:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:19:5 + | +LL | arr.sort(); + | ^^^^^^^^^^ help: try: `arr.sort_unstable()` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index c017d1cf9a4..31c2ba0f9c5 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 1929c72b2f2..a3c8ae468ed 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 903b6e5099c..70c6cf0a3b6 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,5 +1,5 @@ error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:12:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` @@ -7,37 +7,37 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:13:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:20:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:19:5 + --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` -- cgit 1.4.1-3-g733a5 From e9677105bf85a2b0c57e8d67d2ed22a286333033 Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 2 Aug 2020 00:00:00 +0000 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index d3b351f30ef..3bd73d9f21a 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,10 +1,13 @@ -use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg}; +use crate::utils::{ + is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; +use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let Some(ref err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; if match_qpath(err_fun_path, &paths::RESULT_ERR); - if let Some(return_type) = find_err_return_type(cx, &expr.kind); - + if let Some(return_ty) = find_return_type(cx, &expr.kind); then { - let err_type = cx.typeck_results().expr_ty(err_arg); + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let origin_snippet = if err_arg.span.from_expansion() { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") }; - let suggestion = if err_type == return_type { - format!("return Err({})", origin_snippet) + let suggestion = if err_ty == expr_err_ty { + format!("return {}{}{}", prefix, origin_snippet, suffix) } else { - format!("return Err({}.into())", origin_snippet) + format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; span_lint_and_sugg( @@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } } -// In order to determine whether to suggest `.into()` or not, we need to find the error type the -// function returns. To do that, we look for the From::from call (see tree above), and capture -// its output type. -fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { - arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty)) - } else { - None + for arm in arms.iter() { + if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } } + None } -// Check for From::from in one of the match arms. -fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option> { +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind; - if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind; - if let ExprKind::Path(ref from_error_fn) = from_error_path.kind; - if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR); - if let Some(from_error_arg) = from_error_args.get(0); + if let ty::Adt(_, subst) = ty.kind; + if is_type_diagnostic_item(cx, ty, sym!(result_type)); + let err_ty = subst.type_at(1); + then { + Some(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(cx.typeck_results().expr_ty(from_error_arg)) + Some(err_ty) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index a515ee29c82..923b319d777 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -80,6 +80,7 @@ pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; @@ -129,7 +130,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; -pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 29d9139d3e3..9e77dcd8731 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + } else if n == 1 { + return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))) + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into()))) + } + + Poll::Ready(None) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 5e85d091a2a..41bcb0a189e 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + Err(io::ErrorKind::WriteZero)? + } else if n == 1 { + Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + Err(io::ErrorKind::NotFound)? + } + + Poll::Ready(None) +} diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 21e9d4048a5..3f1cbc17e72 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:15:9 + --> $DIR/try_err.rs:18:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` @@ -11,28 +11,46 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:25:9 + --> $DIR/try_err.rs:28:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:45:17 + --> $DIR/try_err.rs:48:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:64:17 + --> $DIR/try_err.rs:67:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:103:9 + --> $DIR/try_err.rs:106:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` -error: aborting due to 5 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:113:9 + | +LL | Err(io::ErrorKind::WriteZero)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:115:9 + | +LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:123:9 + | +LL | Err(io::ErrorKind::NotFound)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 0ccdf2913a335c4f0b34098f84baeeb3fc852255 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 3 Aug 2020 16:23:20 -0500 Subject: Remove obsolete known problems unnecessary_fold --- clippy_lints/src/methods/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9edcdd979ff..ff8da6b4015 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1052,8 +1052,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability. /// - /// **Known problems:** False positive in pattern guards. Will be resolved once - /// non-lexical lifetimes are stable. + /// **Known problems:** None. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From 542740c2eceff2369b2ac44e891a37313dd1785c Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 4 Aug 2020 17:53:29 -0700 Subject: Run cargo dev fmt --- clippy_lints/src/stable_sort_primitive.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index c48da004a60..cd7056620a2 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -43,30 +43,30 @@ declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); /// The three "kinds" of sorts enum SortingKind { Vanilla, - // The other kinds of lint are currently commented out because they - // can map distinct values to equal ones. If the key function is - // provably one-to-one, or if the Cmp function conserves equality, - // then they could be linted on, but I don't know if we can check - // for that. + /* The other kinds of lint are currently commented out because they + * can map distinct values to equal ones. If the key function is + * provably one-to-one, or if the Cmp function conserves equality, + * then they could be linted on, but I don't know if we can check + * for that. */ - // ByKey, - // ByCmp, + /* ByKey, + * ByCmp, */ } impl SortingKind { /// The name of the stable version of this kind of sort fn stable_name(&self) -> &str { match self { SortingKind::Vanilla => "sort", - // SortingKind::ByKey => "sort_by_key", - // SortingKind::ByCmp => "sort_by", + /* SortingKind::ByKey => "sort_by_key", + * SortingKind::ByCmp => "sort_by", */ } } /// The name of the unstable version of this kind of sort fn unstable_name(&self) -> &str { match self { SortingKind::Vanilla => "sort_unstable", - // SortingKind::ByKey => "sort_unstable_by_key", - // SortingKind::ByCmp => "sort_unstable_by", + /* SortingKind::ByKey => "sort_unstable_by_key", + * SortingKind::ByCmp => "sort_unstable_by", */ } } /// Takes the name of a function call and returns the kind of sort -- cgit 1.4.1-3-g733a5 From 1e8ada3cab5470a4f2070c0aa9d1a94922476621 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 18 Jul 2020 23:28:31 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d83d978b8..fbc783f1c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1687,6 +1687,7 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26aff6af8cd..2bd5ae0ed98 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -610,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, + &loops::SAME_ITEM_PUSH, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, @@ -1405,6 +1406,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1543,6 +1545,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6359c20040c..48891a59c00 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -419,6 +419,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop is being used to push a constant + /// value into a Vec. + /// + /// **Why is this bad?** This kind of operation can be expressed more succinctly with + /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// have better performance. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = Vec::new(); + /// for _ in 0..20 { + /// vec.push(item1); + /// } + /// for _ in 0..30 { + /// vec.push(item2); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = vec![item1; 20]; + /// vec.resize(20 + 30, item2); + /// ``` + pub SAME_ITEM_PUSH, + style, + "the same item is pushed inside of a for loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -435,6 +468,7 @@ declare_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + SAME_ITEM_PUSH, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -740,6 +774,7 @@ fn check_for_loop<'tcx>( check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); detect_manual_memcpy(cx, pat, arg, body, expr); + detect_same_item_push(cx, pat, arg, body, expr); } fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { @@ -1016,6 +1051,236 @@ fn detect_manual_memcpy<'tcx>( } } +// Delegate that traverses expression and detects mutable variables being used +struct UsesMutableDelegate { + found_mutable: bool, +} + +impl<'tcx> Delegate<'tcx> for UsesMutableDelegate { + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} + + fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { + // Mutable variable is found + if let ty::BorrowKind::MutBorrow = bk { + self.found_mutable = true; + } + } + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>) {} +} + +// Uses UsesMutableDelegate to find mutable variables in an expression expr +fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + let mut delegate = UsesMutableDelegate { found_mutable: false }; + let def_id = expr.hir_id.owner.to_def_id(); + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new( + &mut delegate, + &infcx, + def_id.expect_local(), + cx.param_env, + cx.tables(), + ).walk_expr(expr); + }); + + delegate.found_mutable +} + +// Scans for the usage of the for loop pattern +struct ForPatternVisitor<'a, 'tcx> { + found_pattern: bool, + // Pattern that we are searching for + for_pattern: &'a Pat<'tcx>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // Recursively explore an expression until a ExprKind::Path is found + match &expr.kind { + ExprKind::Array(expr_list) | ExprKind::MethodCall(_, _, expr_list, _) | ExprKind::Tup(expr_list) => { + for expr in *expr_list { + self.visit_expr(expr) + } + }, + ExprKind::Binary(_, lhs_expr, rhs_expr) => { + self.visit_expr(lhs_expr); + self.visit_expr(rhs_expr); + }, + ExprKind::Box(expr) + | ExprKind::Unary(_, expr) + | ExprKind::Cast(expr, _) + | ExprKind::Type(expr, _) + | ExprKind::AddrOf(_, _, expr) + | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), + _ => { + // Exploration cannot continue ... calculate the hir_id of the current + // expr assuming it is a Path + if let Some(hir_id) = var_def_id(self.cx, &expr) { + // Pattern is found + if hir_id == self.for_pattern.hir_id { + self.found_pattern = true; + } + // If the for loop pattern is a tuple, determine whether the current + // expr is inside that tuple pattern + if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { + let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); + if hir_id_list.contains(&hir_id) { + self.found_pattern = true; + } + } + } + }, + } + } + + // This is triggered by walk_expr() for the case of vec.push(pat) + fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, _: HirId, _: Span) { + if_chain! { + if let QPath::Resolved(_, path) = qpath; + if let Res::Local(hir_id) = &path.res; + then { + if *hir_id == self.for_pattern.hir_id{ + self.found_pattern = true; + } + + if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { + let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); + if hir_id_list.contains(&hir_id) { + self.found_pattern = true; + } + } + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Scans the body of the for loop and determines whether lint should be given +struct SameItemPushVisitor<'a, 'tcx> { + should_lint: bool, + // this field holds the last vec push operation visited, which should be the only push seen + vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + // Non-determinism may occur ... don't give a lint + ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false, + ExprKind::Block(block, _) => self.visit_block(block), + _ => {}, + } + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + for stmt in b.stmts.iter() { + self.visit_stmt(stmt); + } + } + + fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) { + let vec_push_option = get_vec_push(self.cx, s); + if vec_push_option.is_none() { + // Current statement is not a push so visit inside + match &s.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr), + _ => {}, + } + } else { + // Current statement is a push ...check whether another + // push had been previously done + if self.vec_push.is_none() { + self.vec_push = vec_push_option; + } else { + // There are multiple pushes ... don't lint + self.should_lint = false; + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Given some statement, determine if that statement is a push on a Vec. If it is, return +// the Vec being pushed into and the item being pushed +fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Extract method being called + if let StmtKind::Semi(semi_stmt) = &stmt.kind; + if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind; + // Figure out the parameters for the method call + if let Some(self_expr) = args.get(0); + if let Some(pushed_item) = args.get(1); + // Check that the method being called is push() on a Vec + if match_type(cx, cx.tables().expr_ty(self_expr), &paths::VEC); + if path.ident.name.as_str() == "push"; + then { + return Some((self_expr, pushed_item)) + } + } + None +} + +/// Detects for loop pushing the same item into a Vec +fn detect_same_item_push<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + _: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + _: &'tcx Expr<'_>, +) { + // Determine whether it is safe to lint the body + let mut same_item_push_visitor = SameItemPushVisitor { + should_lint: true, + vec_push: None, + cx, + }; + walk_expr(&mut same_item_push_visitor, body); + if same_item_push_visitor.should_lint { + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { + // Make sure that the push does not involve possibly mutating values + if !has_mutable_variables(cx, pushed_item) { + // Walk through the expression being pushed and make sure that it + // does not contain the for loop pattern + let mut for_pat_visitor = ForPatternVisitor { + found_pattern: false, + for_pattern: pat, + cx, + }; + walk_expr(&mut for_pat_visitor, pushed_item); + + if !for_pat_visitor.found_pattern { + let vec_str = snippet(cx, vec.span, ""); + let item_str = snippet(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } + } +} + /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. #[allow(clippy::too_many_lines)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a08d7da6dcb..1b10226c930 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1935,6 +1935,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copies", }, + Lint { + name: "same_item_push", + group: "style", + desc: "default lint description", + deprecation: None, + module: "same_item_push", + }, Lint { name: "search_is_some", group: "complexity", diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs new file mode 100644 index 00000000000..e3a5a647f76 --- /dev/null +++ b/tests/ui/same_item_push.rs @@ -0,0 +1,77 @@ +#![warn(clippy::same_item_push)] + +fn mutate_increment(x: &mut u8) -> u8 { + *x += 1; + *x +} + +fn increment(x: u8) -> u8 { + x + 1 +} + +fn main() { + // Test for basic case + let mut spaces = Vec::with_capacity(10); + for _ in 0..10 { + spaces.push(vec![b' ']); + } + + let mut vec2: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec2.push(item); + } + + let mut vec3: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec3.push(item); + } + + let mut vec4: Vec = Vec::new(); + for _ in 0..15 { + vec4.push(13); + } + + // Suggestion should not be given as pushed variable can mutate + let mut vec5: Vec = Vec::new(); + let mut item: u8 = 2; + for _ in 0..30 { + vec5.push(mutate_increment(&mut item)); + } + + let mut vec6: Vec = Vec::new(); + let mut item: u8 = 2; + let mut item2 = &mut mutate_increment(&mut item); + for _ in 0..30 { + vec6.push(mutate_increment(item2)); + } + + let mut vec7: Vec = Vec::new(); + for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { + vec7.push(a); + } + + let mut vec8: Vec = Vec::new(); + for i in 0..30 { + vec8.push(increment(i)); + } + + let mut vec9: Vec = Vec::new(); + for i in 0..30 { + vec9.push(i + i * i); + } + + // Suggestion should not be given as there are multiple pushes that are not the same + let mut vec10: Vec = Vec::new(); + let item: u8 = 2; + for _ in 0..30 { + vec10.push(item); + vec10.push(item * 2); + } + + // Suggestion should not be given as Vec is not involved + for _ in 0..5 { + println!("Same Item Push"); + } +} diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr new file mode 100644 index 00000000000..559cc450b9d --- /dev/null +++ b/tests/ui/same_item_push.stderr @@ -0,0 +1,35 @@ +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:16:9 + | +LL | spaces.push(vec![b' ']); + | ^^^^^^ + | + = note: `-D clippy::same-item-push` implied by `-D warnings` + = help: try using vec![<[_]>::into_vec(box [$($x),+]);SIZE] or spaces.resize(NEW_SIZE, <[_]>::into_vec(box [$($x),+])) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:22:9 + | +LL | vec2.push(item); + | ^^^^ + | + = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:28:9 + | +LL | vec3.push(item); + | ^^^^ + | + = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:33:9 + | +LL | vec4.push(13); + | ^^^^ + | + = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 161f47510076d36722546c3541a546f9b724fadd Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 19 Jul 2020 23:43:35 +0900 Subject: 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(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 48891a59c00..1c2f1225497 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1114,6 +1114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { | ExprKind::Cast(expr, _) | ExprKind::Type(expr, _) | ExprKind::AddrOf(_, _, expr) + | ExprKind::Field(expr, _) | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), _ => { // Exploration cannot continue ... calculate the hir_id of the current diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index e3a5a647f76..4bb5e73ff0d 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -74,4 +74,16 @@ fn main() { for _ in 0..5 { println!("Same Item Push"); } + + struct A { + kind: u32, + } + let mut vec_a: Vec = Vec::new(); + for i in 0..30 { + vec_a.push(A{kind: i}); + } + let mut vec12: Vec = Vec::new(); + for a in vec_a { + vec12.push(2u8.pow(a.kind)); + } } -- cgit 1.4.1-3-g733a5 From 2beb9090d1b9adb2b0930da511bf1750e570905b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 20 Jul 2020 22:40:31 +0900 Subject: Rename TypeckTables to TypeckResults --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c2f1225497..766c68df623 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1079,7 +1079,7 @@ fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> &infcx, def_id.expect_local(), cx.param_env, - cx.tables(), + cx.typeck_results(), ).walk_expr(expr); }); @@ -1224,7 +1224,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if match_type(cx, cx.tables().expr_ty(self_expr), &paths::VEC); + if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC); if path.ident.name.as_str() == "push"; then { return Some((self_expr, pushed_item)) -- cgit 1.4.1-3-g733a5 From 1543e117cc7459bef2b57389503f0f526a903f45 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 20 Jul 2020 22:52:30 +0900 Subject: cargo dev update_lints --- clippy_lints/src/lib.rs | 6 +++--- src/lintlist/mod.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2bd5ae0ed98..308868fc90a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -607,10 +607,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_COLLECT, &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, + &loops::SAME_ITEM_PUSH, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, - &loops::SAME_ITEM_PUSH, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, @@ -1293,6 +1293,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_COLLECT), LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1406,7 +1407,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1495,6 +1495,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), @@ -1545,7 +1546,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b10226c930..1f79e44049f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1938,9 +1938,9 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "same_item_push", group: "style", - desc: "default lint description", + desc: "the same item is pushed inside of a for loop", deprecation: None, - module: "same_item_push", + module: "loops", }, Lint { name: "search_is_some", -- cgit 1.4.1-3-g733a5 From 14a4e3bcc8082b0323886ae15365ea2424b512cf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 21 Jul 2020 08:15:13 +0900 Subject: Fix a lint message --- clippy_lints/src/loops.rs | 11 ++++++----- tests/ui/same_item_push.stderr | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 766c68df623..8ca67cae0e9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,9 +3,10 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, span_lint, + get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, + snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; @@ -1262,8 +1263,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut for_pat_visitor, pushed_item); if !for_pat_visitor.found_pattern { - let vec_str = snippet(cx, vec.span, ""); - let item_str = snippet(cx, pushed_item.span, ""); + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); span_lint_and_help( cx, diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 559cc450b9d..ddc5d48cd41 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -5,7 +5,7 @@ LL | spaces.push(vec![b' ']); | ^^^^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![<[_]>::into_vec(box [$($x),+]);SIZE] or spaces.resize(NEW_SIZE, <[_]>::into_vec(box [$($x),+])) + = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:22:9 -- cgit 1.4.1-3-g733a5 From b7ceb4d3d7ed3ea7039caf803073e86ad3643e21 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 21 Jul 2020 08:25:11 +0900 Subject: rustfmt --- clippy_lints/src/loops.rs | 5 +++-- tests/ui/same_item_push.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8ca67cae0e9..3a1fbc4bfed 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1081,7 +1081,8 @@ fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> def_id.expect_local(), cx.param_env, cx.typeck_results(), - ).walk_expr(expr); + ) + .walk_expr(expr); }); delegate.found_mutable @@ -1271,7 +1272,7 @@ fn detect_same_item_push<'tcx>( SAME_ITEM_PUSH, vec.span, "it looks like the same item is being pushed into this Vec", - None, + None, &format!( "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", item_str, vec_str, item_str diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 4bb5e73ff0d..ff1088f86f6 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -80,7 +80,7 @@ fn main() { } let mut vec_a: Vec = Vec::new(); for i in 0..30 { - vec_a.push(A{kind: i}); + vec_a.push(A { kind: i }); } let mut vec12: Vec = Vec::new(); for a in vec_a { -- cgit 1.4.1-3-g733a5 From 228f668282daab05ec20adbbdeb227e923d10864 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 22 Jul 2020 22:11:31 +0900 Subject: Use `mutated_variables` --- clippy_lints/src/loops.rs | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3a1fbc4bfed..86952c10dfc 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1052,42 +1052,6 @@ fn detect_manual_memcpy<'tcx>( } } -// Delegate that traverses expression and detects mutable variables being used -struct UsesMutableDelegate { - found_mutable: bool, -} - -impl<'tcx> Delegate<'tcx> for UsesMutableDelegate { - fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} - - fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { - // Mutable variable is found - if let ty::BorrowKind::MutBorrow = bk { - self.found_mutable = true; - } - } - - fn mutate(&mut self, _: &PlaceWithHirId<'tcx>) {} -} - -// Uses UsesMutableDelegate to find mutable variables in an expression expr -fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - let mut delegate = UsesMutableDelegate { found_mutable: false }; - let def_id = expr.hir_id.owner.to_def_id(); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - def_id.expect_local(), - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(expr); - }); - - delegate.found_mutable -} - // Scans for the usage of the for loop pattern struct ForPatternVisitor<'a, 'tcx> { found_pattern: bool, @@ -1253,7 +1217,7 @@ fn detect_same_item_push<'tcx>( if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values - if !has_mutable_variables(cx, pushed_item) { + if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { // Walk through the expression being pushed and make sure that it // does not contain the for loop pattern let mut for_pat_visitor = ForPatternVisitor { -- cgit 1.4.1-3-g733a5 From e48685edef9889d7c0ae391cf050f878d228ae25 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 22 Jul 2020 23:22:17 +0900 Subject: Just check if it contains `_` in `for pat` --- clippy_lints/src/loops.rs | 87 +---------------------------------------------- 1 file changed, 1 insertion(+), 86 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 86952c10dfc..3104f0c137e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1052,82 +1052,6 @@ fn detect_manual_memcpy<'tcx>( } } -// Scans for the usage of the for loop pattern -struct ForPatternVisitor<'a, 'tcx> { - found_pattern: bool, - // Pattern that we are searching for - for_pattern: &'a Pat<'tcx>, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // Recursively explore an expression until a ExprKind::Path is found - match &expr.kind { - ExprKind::Array(expr_list) | ExprKind::MethodCall(_, _, expr_list, _) | ExprKind::Tup(expr_list) => { - for expr in *expr_list { - self.visit_expr(expr) - } - }, - ExprKind::Binary(_, lhs_expr, rhs_expr) => { - self.visit_expr(lhs_expr); - self.visit_expr(rhs_expr); - }, - ExprKind::Box(expr) - | ExprKind::Unary(_, expr) - | ExprKind::Cast(expr, _) - | ExprKind::Type(expr, _) - | ExprKind::AddrOf(_, _, expr) - | ExprKind::Field(expr, _) - | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), - _ => { - // Exploration cannot continue ... calculate the hir_id of the current - // expr assuming it is a Path - if let Some(hir_id) = var_def_id(self.cx, &expr) { - // Pattern is found - if hir_id == self.for_pattern.hir_id { - self.found_pattern = true; - } - // If the for loop pattern is a tuple, determine whether the current - // expr is inside that tuple pattern - if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { - let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); - if hir_id_list.contains(&hir_id) { - self.found_pattern = true; - } - } - } - }, - } - } - - // This is triggered by walk_expr() for the case of vec.push(pat) - fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, _: HirId, _: Span) { - if_chain! { - if let QPath::Resolved(_, path) = qpath; - if let Res::Local(hir_id) = &path.res; - then { - if *hir_id == self.for_pattern.hir_id{ - self.found_pattern = true; - } - - if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { - let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); - if hir_id_list.contains(&hir_id) { - self.found_pattern = true; - } - } - } - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - // Scans the body of the for loop and determines whether lint should be given struct SameItemPushVisitor<'a, 'tcx> { should_lint: bool, @@ -1218,16 +1142,7 @@ fn detect_same_item_push<'tcx>( if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - // Walk through the expression being pushed and make sure that it - // does not contain the for loop pattern - let mut for_pat_visitor = ForPatternVisitor { - found_pattern: false, - for_pattern: pat, - cx, - }; - walk_expr(&mut for_pat_visitor, pushed_item); - - if !for_pat_visitor.found_pattern { + if let PatKind::Wild = pat.kind { let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); -- cgit 1.4.1-3-g733a5 From 610d4e3c8b1bfa27e059043554f4156fe1254142 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 5 Aug 2020 23:10:30 +0900 Subject: rustfmt --- clippy_lints/src/loops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3104f0c137e..5c918ff648f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,11 +3,11 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, - implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, - last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, - snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, + get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; -- cgit 1.4.1-3-g733a5 From 50a86d492718f2ad5e653575d19324205fa007f1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 6 Aug 2020 00:40:11 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 08d8100a885..80a06758982 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, - span_lint_and_then, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help, + span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -354,7 +354,9 @@ fn check_unsafe_derive_deserialize<'tcx>( if_chain! { if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); if let ty::Adt(def, _) = ty.kind; - if def.did.is_local(); + if let Some(local_def_id) = def.did.as_local(); + let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); if cx.tcx.inherent_impls(def.did) .iter() .map(|imp_did| item_from_def_id(cx, *imp_did)) diff --git a/tests/ui/unsafe_derive_deserialize.rs b/tests/ui/unsafe_derive_deserialize.rs index 7bee9c499e1..690d705573d 100644 --- a/tests/ui/unsafe_derive_deserialize.rs +++ b/tests/ui/unsafe_derive_deserialize.rs @@ -57,4 +57,14 @@ impl E { #[derive(Deserialize)] pub struct F {} +// Check that we honor the `allow` attribute on the ADT +#[allow(clippy::unsafe_derive_deserialize)] +#[derive(Deserialize)] +pub struct G {} +impl G { + pub fn unsafe_block(&self) { + unsafe {} + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 0abc4833e5dc8ec4da48d5b25e1d0df81cceec4d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Thu, 6 Aug 2020 02:42:40 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index dae39aaf5e2..1f798fd1120 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -18,6 +18,10 @@ declare_clippy_lint! { /// ```ignore /// min(0, max(100, x)) /// ``` + /// or + /// ```ignore + /// x.max(100).min(0) + /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value /// between 0 and 100, but has erroneously swapped `min` and `max`. pub MIN_MAX, @@ -60,25 +64,35 @@ enum MinMax { } fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { - if let ExprKind::Call(ref path, ref args) = expr.kind { - if let ExprKind::Path(ref qpath) = path.kind { - cx.typeck_results() - .qpath_res(qpath, path.hir_id) - .opt_def_id() - .and_then(|def_id| { - if match_def_path(cx, def_id, &paths::CMP_MIN) { - fetch_const(cx, args, MinMax::Min) - } else if match_def_path(cx, def_id, &paths::CMP_MAX) { - fetch_const(cx, args, MinMax::Max) - } else { - None - } - }) - } else { - None - } - } else { - None + match expr.kind { + ExprKind::Call(ref path, ref args) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.typeck_results() + .qpath_res(qpath, path.hir_id) + .opt_def_id() + .and_then(|def_id| { + if match_def_path(cx, def_id, &paths::CMP_MIN) { + fetch_const(cx, args, MinMax::Min) + } else if match_def_path(cx, def_id, &paths::CMP_MAX) { + fetch_const(cx, args, MinMax::Max) + } else { + None + } + }) + } else { + None + } + }, + ExprKind::MethodCall(ref path, _, ref args, _) => { + if path.ident.as_str() == sym!(max).as_str() { + fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) + } else { + None + } + }, + _ => None, } } diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 8307d4b3019..90ec5676493 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -30,4 +30,18 @@ fn main() { max(min(s, "Apple"), "Zoo"); max("Apple", min(s, "Zoo")); // ok + + x.min(1).max(3); + x.max(3).min(1); + + x.max(1).min(3); // ok + x.min(3).max(1); // ok + + max(x.min(1), 3); + min(x.max(1), 3); // ok + + s.max("Zoo").min("Apple"); + s.min("Apple").max("Zoo"); + + s.min("Zoo").max("Apple"); // ok } diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index b552c137f7c..653946dc025 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -42,5 +42,35 @@ error: this `min`/`max` combination leads to constant result LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:34:5 + | +LL | x.min(1).max(3); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:35:5 + | +LL | x.max(3).min(1); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:40:5 + | +LL | max(x.min(1), 3); + | ^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:43:5 + | +LL | s.max("Zoo").min("Apple"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:44:5 + | +LL | s.min("Apple").max("Zoo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From e0a4988fcc716e349fd801d98182c0d984a2ee3f Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 4 Aug 2020 20:23:14 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe896d5efa..179ecee03da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1622,6 +1622,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_fn_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_fn_self_type [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 81864340f1a..80c85e70e89 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -254,6 +254,7 @@ mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; +mod needless_fn_self_type; mod needless_pass_by_value; mod needless_update; mod neg_cmp_op_on_partial_ord; @@ -722,6 +723,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, + &needless_fn_self_type::NEEDLESS_FN_SELF_TYPE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, @@ -1027,6 +1029,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box needless_fn_self_type::NeedlessFnSelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1374,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1534,6 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs new file mode 100644 index 00000000000..050a03467fb --- /dev/null +++ b/clippy_lints/src/needless_fn_self_type.rs @@ -0,0 +1,64 @@ +use crate::utils::span_lint_and_help; +use if_chain::if_chain; +use rustc_ast::ast::{Param, TyKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` fn fn parameters that explicitly + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_FN_SELF_TYPE, + style, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessFnSelfType => [NEEDLESS_FN_SELF_TYPE]); + +impl EarlyLintPass for NeedlessFnSelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if_chain! { + if p.is_self(); + if let TyKind::Path(None, path) = &p.ty.kind; + if let Some(segment) = path.segments.first(); + if segment.ident.as_str() == sym!(Self).as_str(); + then { + span_lint_and_help( + cx, + NEEDLESS_FN_SELF_TYPE, + p.ty.span, + "the type of the `self` parameter is already by default `Self`", + None, + "consider removing the type specification", + ); + } + } + } +} diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 06631f89f27..d4acf8df46d 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// fn func(arg: T) {} /// ``` /// or - /// /// + /// /// ```rust /// fn func(arg: T) where T: Clone + Default {} /// ``` diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b64c6e54409..a20e410f79b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1501,6 +1501,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "doc", }, + Lint { + name: "needless_fn_self_type", + group: "style", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_fn_self_type", + }, Lint { name: "needless_lifetimes", group: "complexity", diff --git a/tests/ui/needless_fn_self_type.rs b/tests/ui/needless_fn_self_type.rs new file mode 100644 index 00000000000..12bb84582ff --- /dev/null +++ b/tests/ui/needless_fn_self_type.rs @@ -0,0 +1,26 @@ +#![warn(clippy::style, clippy::needless_fn_self_type)] + +pub enum ValType { + I32, + I64, + F32, + F64, +} + +impl ValType { + pub fn bytes_bad(self: Self) -> usize { + match self { + Self::I32 | Self::F32 => 4, + Self::I64 | Self::F64 => 8, + } + } + + pub fn bytes_good(self) -> usize { + match self { + Self::I32 | Self::F32 => 4, + Self::I64 | Self::F64 => 8, + } + } +} + +fn main() {} diff --git a/tests/ui/needless_fn_self_type.stderr b/tests/ui/needless_fn_self_type.stderr new file mode 100644 index 00000000000..703705c7842 --- /dev/null +++ b/tests/ui/needless_fn_self_type.stderr @@ -0,0 +1,11 @@ +error: the type of the `self` parameter is already by default `Self` + --> $DIR/needless_fn_self_type.rs:11:28 + | +LL | pub fn bytes_bad(self: Self) -> usize { + | ^^^^ + | + = note: `-D clippy::needless-fn-self-type` implied by `-D warnings` + = help: consider removing the type specification + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 737f62cb6eaa5eca23701dbbe8d63465e1c4843b Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 4 Aug 2020 21:07:35 +0200 Subject: fix doc --- clippy_lints/src/needless_fn_self_type.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs index 050a03467fb..b71f2fbbd46 100644 --- a/clippy_lints/src/needless_fn_self_type.rs +++ b/clippy_lints/src/needless_fn_self_type.rs @@ -13,6 +13,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// /// impl ValType { /// pub fn bytes(self: Self) -> usize { /// match self { @@ -26,6 +33,13 @@ declare_clippy_lint! { /// Could be rewritten as /// /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// /// impl ValType { /// pub fn bytes(self) -> usize { /// match self { -- cgit 1.4.1-3-g733a5 From d635b76eaf3435f9bdce1dcbdd315b0e770493f0 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 02:59:30 +0200 Subject: adopt comments from review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +- clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 179ecee03da..3f470f601ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1616,13 +1616,13 @@ Released 2018-09-13 [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main -[`needless_fn_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_fn_self_type [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 80c85e70e89..3c39de1abf1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -250,11 +250,11 @@ mod mut_mut; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; +mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; -mod needless_fn_self_type; mod needless_pass_by_value; mod needless_update; mod neg_cmp_op_on_partial_ord; @@ -718,12 +718,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, &mutex_atomic::MUTEX_ATOMIC, &mutex_atomic::MUTEX_INTEGER, + &needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, &needless_bool::BOOL_COMPARISON, &needless_bool::NEEDLESS_BOOL, &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, - &needless_fn_self_type::NEEDLESS_FN_SELF_TYPE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, @@ -1029,7 +1029,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); - store.register_early_pass(|| box needless_fn_self_type::NeedlessFnSelfType); + store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1374,10 +1374,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mutex_atomic::MUTEX_ATOMIC), + LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1538,7 +1538,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1607,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs new file mode 100644 index 00000000000..1b23ecd9ad2 --- /dev/null +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -0,0 +1,114 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` in fn parameters that + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_ARBITRARY_SELF_TYPE, + complexity, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]); + +enum Mode { + Ref(Option), + Value, +} + +fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { + if_chain! { + if let [segment] = &path.segments[..]; + if segment.ident.name == kw::SelfUpper; + then { + let self_param = match (binding_mode, mutbl) { + (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(None), Mutability::Not) => "&self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Value, Mutability::Mut) => "mut self".to_string(), + (Mode::Value, Mutability::Not) => "self".to_string(), + }; + + span_lint_and_sugg( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter is arbitrary", + "consider to change this parameter to", + self_param, + Applicability::MachineApplicable, + ) + } + } +} + +impl EarlyLintPass for NeedlessArbitrarySelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if !p.is_self() { + return; + } + + match &p.ty.kind { + TyKind::Path(None, path) => { + if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl) + } + }, + TyKind::Rptr(lifetime, mut_ty) => { + if let TyKind::Path(None, path) = &mut_ty.ty.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs deleted file mode 100644 index b71f2fbbd46..00000000000 --- a/clippy_lints/src/needless_fn_self_type.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::utils::span_lint_and_help; -use if_chain::if_chain; -use rustc_ast::ast::{Param, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** The lint checks for `self` fn fn parameters that explicitly - /// specify the `Self`-type explicitly - /// **Why is this bad?** Increases the amount and decreases the readability of code - /// - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// enum ValType { - /// I32, - /// I64, - /// F32, - /// F64, - /// } - /// - /// impl ValType { - /// pub fn bytes(self: Self) -> usize { - /// match self { - /// Self::I32 | Self::F32 => 4, - /// Self::I64 | Self::F64 => 8, - /// } - /// } - /// } - /// ``` - /// - /// Could be rewritten as - /// - /// ```rust - /// enum ValType { - /// I32, - /// I64, - /// F32, - /// F64, - /// } - /// - /// impl ValType { - /// pub fn bytes(self) -> usize { - /// match self { - /// Self::I32 | Self::F32 => 4, - /// Self::I64 | Self::F64 => 8, - /// } - /// } - /// } - /// ``` - pub NEEDLESS_FN_SELF_TYPE, - style, - "type of `self` parameter is already by default `Self`" -} - -declare_lint_pass!(NeedlessFnSelfType => [NEEDLESS_FN_SELF_TYPE]); - -impl EarlyLintPass for NeedlessFnSelfType { - fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if_chain! { - if p.is_self(); - if let TyKind::Path(None, path) = &p.ty.kind; - if let Some(segment) = path.segments.first(); - if segment.ident.as_str() == sym!(Self).as_str(); - then { - span_lint_and_help( - cx, - NEEDLESS_FN_SELF_TYPE, - p.ty.span, - "the type of the `self` parameter is already by default `Self`", - None, - "consider removing the type specification", - ); - } - } - } -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a20e410f79b..91761e8a86d 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1459,6 +1459,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bytecount", }, + Lint { + name: "needless_arbitrary_self_type", + group: "complexity", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_arbitrary_self_type", + }, Lint { name: "needless_bool", group: "complexity", @@ -1501,13 +1508,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "doc", }, - Lint { - name: "needless_fn_self_type", - group: "style", - desc: "type of `self` parameter is already by default `Self`", - deprecation: None, - module: "needless_fn_self_type", - }, Lint { name: "needless_lifetimes", group: "complexity", diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs new file mode 100644 index 00000000000..da4bbcf4a7d --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -0,0 +1,58 @@ +#![warn(clippy::needless_arbitrary_self_type)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self: Self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self: Self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(self: &Self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn mut_ref_bad(self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_bad(mut self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr new file mode 100644 index 00000000000..ee803b24071 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -0,0 +1,46 @@ +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:9:16 + | +LL | pub fn bad(self: Self) { + | ^^^^^^^^^^ help: consider to change this parameter to: `self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:17:20 + | +LL | pub fn mut_bad(mut self: Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:25:20 + | +LL | pub fn ref_bad(self: &Self) { + | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:29:38 + | +LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:37:24 + | +LL | pub fn mut_ref_bad(self: &mut Self) { + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:41:42 + | +LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:49:28 + | +LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { + | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_fn_self_type.rs b/tests/ui/needless_fn_self_type.rs deleted file mode 100644 index 12bb84582ff..00000000000 --- a/tests/ui/needless_fn_self_type.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![warn(clippy::style, clippy::needless_fn_self_type)] - -pub enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes_bad(self: Self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } - - pub fn bytes_good(self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} - -fn main() {} diff --git a/tests/ui/needless_fn_self_type.stderr b/tests/ui/needless_fn_self_type.stderr deleted file mode 100644 index 703705c7842..00000000000 --- a/tests/ui/needless_fn_self_type.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: the type of the `self` parameter is already by default `Self` - --> $DIR/needless_fn_self_type.rs:11:28 - | -LL | pub fn bytes_bad(self: Self) -> usize { - | ^^^^ - | - = note: `-D clippy::needless-fn-self-type` implied by `-D warnings` - = help: consider removing the type specification - -error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From e03f73e627721c35459886781af281632cac299d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 23:38:55 +0200 Subject: fix nits --- clippy_lints/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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 1b23ecd9ad2..4590128bedc 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -82,7 +82,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod cx, NEEDLESS_ARBITRARY_SELF_TYPE, span, - "the type of the `self` parameter is arbitrary", + "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, Applicability::MachineApplicable, diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index 642e48fd131..bc770d8bf68 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -29,11 +29,15 @@ impl ValType { unimplemented!(); } + pub fn ref_good(&self) { + unimplemented!(); + } + pub fn ref_bad_with_lifetime<'a>(&'a self) { unimplemented!(); } - pub fn ref_good(&self) { + pub fn ref_good_with_lifetime<'a>(&'a self) { unimplemented!(); } @@ -41,11 +45,15 @@ impl ValType { unimplemented!(); } + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { unimplemented!(); } - pub fn mut_ref_good(&mut self) { + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 178abc341a8..9074920b204 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -29,11 +29,15 @@ impl ValType { unimplemented!(); } + pub fn ref_good(&self) { + unimplemented!(); + } + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { unimplemented!(); } - pub fn ref_good(&self) { + pub fn ref_good_with_lifetime<'a>(&'a self) { unimplemented!(); } @@ -41,11 +45,15 @@ impl ValType { unimplemented!(); } + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { unimplemented!(); } - pub fn mut_ref_good(&mut self) { + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index fc21d3d0afd..227c6d73b62 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -1,4 +1,4 @@ -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:12:16 | LL | pub fn bad(self: Self) { @@ -6,38 +6,38 @@ LL | pub fn bad(self: Self) { | = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:20:20 | LL | pub fn mut_bad(mut self: Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:28:20 | LL | pub fn ref_bad(self: &Self) { | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:32:38 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:36:38 | LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:40:24 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:44:24 | LL | pub fn mut_ref_bad(self: &mut Self) { | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:44:42 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:52:42 | LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:52:28 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:60:28 | LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` -- cgit 1.4.1-3-g733a5 From bfe610cc8d7d380cfaf83f03629a23747fc54fad Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 7 Aug 2020 18:03:12 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 4590128bedc..38bdd0f7ed2 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -104,8 +104,12 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { } }, TyKind::Rptr(lifetime, mut_ty) => { - if let TyKind::Path(None, path) = &mut_ty.ty.kind { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + if_chain! { + if let TyKind::Path(None, path) = &mut_ty.ty.kind; + if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind; + then { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } } }, _ => {}, diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index bc770d8bf68..9da21eb6b29 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -57,7 +57,7 @@ impl ValType { unimplemented!(); } - pub fn mut_ref_mut_bad(&mut self) { + pub fn mut_ref_mut_good(mut self: &mut Self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 9074920b204..17aeaaf97ac 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -57,7 +57,7 @@ impl ValType { unimplemented!(); } - pub fn mut_ref_mut_bad(mut self: &mut Self) { + pub fn mut_ref_mut_good(mut self: &mut Self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index 227c6d73b62..f4c645d35c8 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -36,11 +36,5 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: the type of the `self` parameter does not need to be arbitrary - --> $DIR/needless_arbitrary_self_type.rs:60:28 - | -LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { - | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 87e740921abd4132152f090545fa4c9ed9fa0d6d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 7 Aug 2020 17:55:25 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 1f798fd1120..004dd50a31b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -84,12 +85,20 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(ref path, _, ref args, _) => { - if path.ident.as_str() == sym!(max).as_str() { - fetch_const(cx, args, MinMax::Max) - } else if path.ident.as_str() == sym!(min).as_str() { - fetch_const(cx, args, MinMax::Min) - } else { - None + if_chain! { + if let [obj, _] = args; + if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); + then { + if path.ident.as_str() == sym!(max).as_str() { + fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) + } else { + None + } + } else { + None + } } }, _ => None, diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 90ec5676493..f7ed72a11cf 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -6,6 +6,18 @@ use std::cmp::{max, min}; const LARGE: usize = 3; +struct NotOrd(u64); + +impl NotOrd { + fn min(self, x: u64) -> NotOrd { + NotOrd(x) + } + + fn max(self, x: u64) -> NotOrd { + NotOrd(x) + } +} + fn main() { let x; x = 2usize; @@ -31,11 +43,14 @@ fn main() { max("Apple", min(s, "Zoo")); // ok + let f = 3f32; x.min(1).max(3); x.max(3).min(1); + f.max(3f32).min(1f32); x.max(1).min(3); // ok x.min(3).max(1); // ok + f.min(3f32).max(1f32); // ok max(x.min(1), 3); min(x.max(1), 3); // ok @@ -44,4 +59,7 @@ fn main() { s.min("Apple").max("Zoo"); s.min("Zoo").max("Apple"); // ok + + let not_ord = NotOrd(1); + not_ord.min(1).max(3); // ok } diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index 653946dc025..9f8e26fa406 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:12:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,70 +7,76 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:13:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:14:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:15:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:17:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:29:5 + --> $DIR/min_max.rs:41:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:30:5 + --> $DIR/min_max.rs:42:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:34:5 + --> $DIR/min_max.rs:47:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:35:5 + --> $DIR/min_max.rs:48:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:40:5 + --> $DIR/min_max.rs:49:5 + | +LL | f.max(3f32).min(1f32); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:55:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:43:5 + --> $DIR/min_max.rs:58:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:44:5 + --> $DIR/min_max.rs:59:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 888657e09a2133fc105382136f61915086144e3f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 8 Aug 2020 18:13:43 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6359c20040c..1729fea7bc8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2374,7 +2374,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, @@ -2386,20 +2386,20 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont ); } if method.ident.name == sym!(is_empty) { - let span = shorten_span(expr, sym!(iter)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - "get(0).is_none()".to_string(), + "next().is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2579,13 +2579,13 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) } } -fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { - let mut current_expr = expr; - while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind { - if path.ident.name == target_fn_name { +fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { + if_chain! { + if let ExprKind::MethodCall(.., args, _) = &expr.kind; + if let ExprKind::MethodCall(_, span, ..) = &args[0].kind; + then { return expr.span.with_lo(span.lo()); } - current_expr = &args[0]; } - unreachable!() + unreachable!(); } diff --git a/tests/ui/crashes/ice-5872.rs b/tests/ui/crashes/ice-5872.rs new file mode 100644 index 00000000000..68afa8f8c3a --- /dev/null +++ b/tests/ui/crashes/ice-5872.rs @@ -0,0 +1,5 @@ +#![warn(clippy::needless_collect)] + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); +} diff --git a/tests/ui/crashes/ice-5872.stderr b/tests/ui/crashes/ice-5872.stderr new file mode 100644 index 00000000000..a60ca345cf7 --- /dev/null +++ b/tests/ui/crashes/ice-5872.stderr @@ -0,0 +1,10 @@ +error: avoid using `collect()` when not needed + --> $DIR/ice-5872.rs:4:39 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index be37dc16b9a..7f2fcf02f6b 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -5,11 +5,11 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.get(0).is_none() { + if sample.iter().next().is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 7ee603afeb0..788a9eb3264 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -5,7 +5,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().collect::>().len(); diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 9113aad90dd..2a9539d5975 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -7,10 +7,10 @@ LL | let len = sample.iter().collect::>().len(); = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:15 + --> $DIR/needless_collect.rs:12:22 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:15:28 -- cgit 1.4.1-3-g733a5 From fd87cdb357b801b1fab465f0be595386a3f84134 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 8 Aug 2020 19:20:34 +0200 Subject: Run fmt --- clippy_lints/src/functions.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 6a141f1fc78..28b276967bc 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -294,7 +294,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) { + if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) + { check_must_use_candidate( cx, &sig.decl, -- cgit 1.4.1-3-g733a5 From a77e881ec9f324cdc544150f897d8b34281f92e4 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 17 Jun 2020 01:16:34 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 570ae66d595..3009aa3a64e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1497,11 +1497,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if cx.access_levels.is_exported(impl_item.hir_id) { // check missing trait implementations for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { + let no_lifetime_params = || { + impl_item.generics.params.iter().filter(|p| match p.kind { + hir::GenericParamKind::Lifetime { .. } => true, + _ => false, + }).count() == 0 + }; if name == method_name && sig.decl.inputs.len() == n_args && out_type.matches(cx, &sig.decl.output) && self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) { + fn_header_equals(*fn_header, sig.header) && + // ignore methods with lifetime params, risk of false positive + no_lifetime_params() + { span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( "defining a method called `{}` on this type; consider implementing \ the `{}` trait or choosing a less ambiguous name", name, trait_name)); diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 7880cf36415..3b267b0dab2 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -10,6 +10,7 @@ clippy::non_ascii_literal, clippy::new_without_default, clippy::needless_pass_by_value, + clippy::needless_lifetimes, clippy::print_stdout, clippy::must_use_candidate, clippy::use_self, @@ -82,6 +83,10 @@ impl T { fn new(self) -> Self { unimplemented!(); } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T> { + unimplemented!(); + } } pub struct T1; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 01cf487ac14..9b8ecaed692 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:39:5 + --> $DIR/methods.rs:40:5 | LL | / pub fn add(self, other: T) -> T { LL | | self @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::should-implement-trait` implied by `-D warnings` error: methods called `new` usually return `Self` - --> $DIR/methods.rs:169:5 + --> $DIR/methods.rs:174:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +19,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:188:13 + --> $DIR/methods.rs:193:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:191:13 + --> $DIR/methods.rs:196:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +38,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:208:22 + --> $DIR/methods.rs:213:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +46,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:209:20 + --> $DIR/methods.rs:214:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:210:20 + --> $DIR/methods.rs:215:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:211:22 + --> $DIR/methods.rs:216:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:13 + --> $DIR/methods.rs:219:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +74,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:220:22 + --> $DIR/methods.rs:225:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:223:13 + --> $DIR/methods.rs:228:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +90,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:229:22 + --> $DIR/methods.rs:234:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:232:13 + --> $DIR/methods.rs:237:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ -- cgit 1.4.1-3-g733a5 From 2bc0ecd44b4d09476eade641e02451d949a1c8e2 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 19 Jun 2020 22:12:51 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3009aa3a64e..c225a3bd359 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3424,6 +3424,7 @@ const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30 ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), + // FIXME: default doesn't work ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 3b267b0dab2..adf81607440 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -37,9 +37,136 @@ use option_helpers::IteratorFalsePositives; pub struct T; impl T { + // ******************************************* + // complete trait method list, should lint all + // ******************************************* pub fn add(self, other: T) -> T { - self + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() } + // ***************** + // complete list end + // ***************** +} + +pub struct T1; +impl T1 { + // corner cases: should not lint // no error, not public interface pub(crate) fn drop(&mut self) {} @@ -50,22 +177,22 @@ impl T { } // no error, private function - fn eq(&self, other: T) -> bool { + fn eq(&self, other: Self) -> bool { true } // No error; self is a ref. - fn sub(&self, other: T) -> &T { + fn sub(&self, other: Self) -> &Self { self } // No error; different number of arguments. - fn div(self) -> T { + fn div(self) -> Self { self } // No error; wrong return type. - fn rem(self, other: T) {} + fn rem(self, other: Self) {} // Fine fn into_u32(self) -> u32 { @@ -89,16 +216,15 @@ impl T { } } -pub struct T1; - -impl T1 { +pub struct T2; +impl T2 { // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: T1) -> T1 { + pub unsafe fn add(self, rhs: Self) -> Self { self } // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { + pub async fn next(&mut self) -> Option { None } } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 9b8ecaed692..5105fff8f5b 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,15 +1,249 @@ error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:40:5 + --> $DIR/methods.rs:43:5 | LL | / pub fn add(self, other: T) -> T { -LL | | self +LL | | unimplemented!() LL | | } | |_____^ | = note: `-D clippy::should-implement-trait` implied by `-D warnings` +error: defining a method called `as_mut` on this type; consider implementing the `std::convert::AsMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:47:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `as_ref` on this type; consider implementing the `std::convert::AsRef` trait or choosing a less ambiguous name + --> $DIR/methods.rs:51:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitand` on this type; consider implementing the `std::ops::BitAnd` trait or choosing a less ambiguous name + --> $DIR/methods.rs:55:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitor` on this type; consider implementing the `std::ops::BitOr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:59:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitxor` on this type; consider implementing the `std::ops::BitXor` trait or choosing a less ambiguous name + --> $DIR/methods.rs:63:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `borrow` on this type; consider implementing the `std::borrow::Borrow` trait or choosing a less ambiguous name + --> $DIR/methods.rs:67:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `borrow_mut` on this type; consider implementing the `std::borrow::BorrowMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:71:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `clone` on this type; consider implementing the `std::clone::Clone` trait or choosing a less ambiguous name + --> $DIR/methods.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `cmp` on this type; consider implementing the `std::cmp::Ord` trait or choosing a less ambiguous name + --> $DIR/methods.rs:79:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `deref` on this type; consider implementing the `std::ops::Deref` trait or choosing a less ambiguous name + --> $DIR/methods.rs:87:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `deref_mut` on this type; consider implementing the `std::ops::DerefMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:91:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `div` on this type; consider implementing the `std::ops::Div` trait or choosing a less ambiguous name + --> $DIR/methods.rs:95:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `drop` on this type; consider implementing the `std::ops::Drop` trait or choosing a less ambiguous name + --> $DIR/methods.rs:99:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `eq` on this type; consider implementing the `std::cmp::PartialEq` trait or choosing a less ambiguous name + --> $DIR/methods.rs:103:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `from_iter` on this type; consider implementing the `std::iter::FromIterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:107:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `from_str` on this type; consider implementing the `std::str::FromStr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:111:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/methods.rs:111:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::missing-errors-doc` implied by `-D warnings` + +error: defining a method called `hash` on this type; consider implementing the `std::hash::Hash` trait or choosing a less ambiguous name + --> $DIR/methods.rs:115:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `index` on this type; consider implementing the `std::ops::Index` trait or choosing a less ambiguous name + --> $DIR/methods.rs:119:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `index_mut` on this type; consider implementing the `std::ops::IndexMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:123:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:127:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `mul` on this type; consider implementing the `std::ops::Mul` trait or choosing a less ambiguous name + --> $DIR/methods.rs:131:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `neg` on this type; consider implementing the `std::ops::Neg` trait or choosing a less ambiguous name + --> $DIR/methods.rs:135:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `next` on this type; consider implementing the `std::iter::Iterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:139:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `not` on this type; consider implementing the `std::ops::Not` trait or choosing a less ambiguous name + --> $DIR/methods.rs:143:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `rem` on this type; consider implementing the `std::ops::Rem` trait or choosing a less ambiguous name + --> $DIR/methods.rs:147:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `shl` on this type; consider implementing the `std::ops::Shl` trait or choosing a less ambiguous name + --> $DIR/methods.rs:151:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `shr` on this type; consider implementing the `std::ops::Shr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:155:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `sub` on this type; consider implementing the `std::ops::Sub` trait or choosing a less ambiguous name + --> $DIR/methods.rs:159:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + error: methods called `new` usually return `Self` - --> $DIR/methods.rs:174:5 + --> $DIR/methods.rs:300:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +253,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:193:13 + --> $DIR/methods.rs:319:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +262,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:196:13 + --> $DIR/methods.rs:322:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +272,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:213:22 + --> $DIR/methods.rs:339:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +280,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:20 + --> $DIR/methods.rs:340:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:215:20 + --> $DIR/methods.rs:341:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:216:22 + --> $DIR/methods.rs:342:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:219:13 + --> $DIR/methods.rs:345:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +308,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:225:22 + --> $DIR/methods.rs:351:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:228:13 + --> $DIR/methods.rs:354:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +324,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:234:22 + --> $DIR/methods.rs:360:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:237:13 + --> $DIR/methods.rs:363:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -105,5 +339,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 13 previous errors +error: aborting due to 42 previous errors -- cgit 1.4.1-3-g733a5 From e6b2254f9e55743dbace44cc73c6447b6bda58e5 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 21 Jun 2020 00:12:09 +0200 Subject: should_implement_trait - pr remarks --- clippy_lints/src/methods/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c225a3bd359..a75989e3f13 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1498,10 +1498,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { // check missing trait implementations for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { let no_lifetime_params = || { - impl_item.generics.params.iter().filter(|p| match p.kind { - hir::GenericParamKind::Lifetime { .. } => true, - _ => false, - }).count() == 0 + !impl_item.generics.params.iter() + .any(|p| matches!( + p.kind, + hir::GenericParamKind::Lifetime { .. })) }; if name == method_name && sig.decl.inputs.len() == n_args && @@ -1510,7 +1510,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn_header_equals(*fn_header, sig.header) && // ignore methods with lifetime params, risk of false positive no_lifetime_params() - { + { span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( "defining a method called `{}` on this type; consider implementing \ the `{}` trait or choosing a less ambiguous name", name, trait_name)); -- cgit 1.4.1-3-g733a5 From 7cc1a2ed879e45605a53b802cfa5291c9a51284c Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 23 Jun 2020 21:27:48 +0200 Subject: should_implement_trait - filter on explicit lifetime param only --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a75989e3f13..bad1bb7224e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1501,7 +1501,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { !impl_item.generics.params.iter() .any(|p| matches!( p.kind, - hir::GenericParamKind::Lifetime { .. })) + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) }; if name == method_name && sig.decl.inputs.len() == n_args && -- cgit 1.4.1-3-g733a5 From 166c520e9a8b1a45819255e75dee737136aa6ec8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 30 Jul 2020 01:41:12 +0200 Subject: 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 ++++++++ tests/ui/should_impl_trait/method_list_1.stderr | 143 +++++++++++++ tests/ui/should_impl_trait/method_list_2.rs | 88 ++++++++ tests/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bad1bb7224e..a549c3b78ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1495,25 +1495,31 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { if cx.access_levels.is_exported(impl_item.hir_id) { - // check missing trait implementations - for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { - let no_lifetime_params = || { - !impl_item.generics.params.iter() - .any(|p| matches!( - p.kind, - hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) - }; - if name == method_name && - sig.decl.inputs.len() == n_args && - out_type.matches(cx, &sig.decl.output) && - self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) && - // ignore methods with lifetime params, risk of false positive - no_lifetime_params() + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name && + sig.decl.inputs.len() == method_config.param_count && + method_config.output_type.matches(cx, &sig.decl.output) && + method_config.self_kind.matches(cx, self_ty, first_arg_ty) && + fn_header_equals(*method_config.fn_header, sig.header) && + method_config.lifetime_param_cond(&impl_item) { - span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( - "defining a method called `{}` on this type; consider implementing \ - the `{}` trait or choosing a less ambiguous name", name, trait_name)); + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, + method_config.trait_name, + method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ) + ); } } } @@ -3412,39 +3418,85 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { abi: rustc_target::spec::abi::Abi::Rust, }; +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: &'static hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: &'static hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + #[rustfmt::skip] -const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [ - ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"), - ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), - ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), - ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), - ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"), - ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"), - ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), - ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), - ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), - ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", "add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, true), // FIXME: default doesn't work - ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), - ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), - ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), - ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"), - ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), - ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), - ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"), - ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"), - ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), - ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"), - ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), - ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), - ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"), - ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"), - ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), - ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"), - ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"), - ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"), - ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"), - ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"), + ShouldImplTraitCase::new("std::default::Default", "default", 0, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index adf81607440..80dd2f744b3 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -34,201 +34,6 @@ use std::sync::{self, Arc}; use option_helpers::IteratorFalsePositives; -pub struct T; - -impl T { - // ******************************************* - // complete trait method list, should lint all - // ******************************************* - pub fn add(self, other: T) -> T { - unimplemented!() - } - - pub fn as_mut(&mut self) -> &mut T { - unimplemented!() - } - - pub fn as_ref(&self) -> &T { - unimplemented!() - } - - pub fn bitand(self, rhs: T) -> T { - unimplemented!() - } - - pub fn bitor(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn bitxor(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn borrow(&self) -> &str { - unimplemented!() - } - - pub fn borrow_mut(&mut self) -> &mut str { - unimplemented!() - } - - pub fn clone(&self) -> Self { - unimplemented!() - } - - pub fn cmp(&self, other: &Self) -> Self { - unimplemented!() - } - - pub fn default() -> Self { - unimplemented!() - } - - pub fn deref(&self) -> &Self { - unimplemented!() - } - - pub fn deref_mut(&mut self) -> &mut Self { - unimplemented!() - } - - pub fn div(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn drop(&mut self) { - unimplemented!() - } - - pub fn eq(&self, other: &Self) -> bool { - unimplemented!() - } - - pub fn from_iter(iter: T) -> Self { - unimplemented!() - } - - pub fn from_str(s: &str) -> Result { - unimplemented!() - } - - pub fn hash(&self, state: &mut T) { - unimplemented!() - } - - pub fn index(&self, index: usize) -> &Self { - unimplemented!() - } - - pub fn index_mut(&mut self, index: usize) -> &mut Self { - unimplemented!() - } - - pub fn into_iter(self) -> Self { - unimplemented!() - } - - pub fn mul(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn neg(self) -> Self { - unimplemented!() - } - - pub fn next(&mut self) -> Option { - unimplemented!() - } - - pub fn not(self) -> Self { - unimplemented!() - } - - pub fn rem(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn shl(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn shr(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn sub(self, rhs: Self) -> Self { - unimplemented!() - } - // ***************** - // complete list end - // ***************** -} - -pub struct T1; -impl T1 { - // corner cases: should not lint - - // no error, not public interface - pub(crate) fn drop(&mut self) {} - - // no error, private function - fn neg(self) -> Self { - self - } - - // no error, private function - fn eq(&self, other: Self) -> bool { - true - } - - // No error; self is a ref. - fn sub(&self, other: Self) -> &Self { - self - } - - // No error; different number of arguments. - fn div(self) -> Self { - self - } - - // No error; wrong return type. - fn rem(self, other: Self) {} - - // Fine - fn into_u32(self) -> u32 { - 0 - } - - fn into_u16(&self) -> u16 { - 0 - } - - fn to_something(self) -> u32 { - 0 - } - - fn new(self) -> Self { - unimplemented!(); - } - - pub fn next<'b>(&'b mut self) -> Option<&'b mut T> { - unimplemented!(); - } -} - -pub struct T2; -impl T2 { - // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: Self) -> Self { - self - } - - // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { - None - } -} - struct Lt<'a> { foo: &'a u32, } @@ -302,6 +107,8 @@ impl BadNew { } } +struct T; + impl Mul for T { type Output = T; // No error, obviously. diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 5105fff8f5b..2a0a43e83a6 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,249 +1,5 @@ -error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:43:5 - | -LL | / pub fn add(self, other: T) -> T { -LL | | unimplemented!() -LL | | } - | |_____^ - | - = note: `-D clippy::should-implement-trait` implied by `-D warnings` - -error: defining a method called `as_mut` on this type; consider implementing the `std::convert::AsMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:47:5 - | -LL | / pub fn as_mut(&mut self) -> &mut T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `as_ref` on this type; consider implementing the `std::convert::AsRef` trait or choosing a less ambiguous name - --> $DIR/methods.rs:51:5 - | -LL | / pub fn as_ref(&self) -> &T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitand` on this type; consider implementing the `std::ops::BitAnd` trait or choosing a less ambiguous name - --> $DIR/methods.rs:55:5 - | -LL | / pub fn bitand(self, rhs: T) -> T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitor` on this type; consider implementing the `std::ops::BitOr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:59:5 - | -LL | / pub fn bitor(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitxor` on this type; consider implementing the `std::ops::BitXor` trait or choosing a less ambiguous name - --> $DIR/methods.rs:63:5 - | -LL | / pub fn bitxor(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `borrow` on this type; consider implementing the `std::borrow::Borrow` trait or choosing a less ambiguous name - --> $DIR/methods.rs:67:5 - | -LL | / pub fn borrow(&self) -> &str { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `borrow_mut` on this type; consider implementing the `std::borrow::BorrowMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:71:5 - | -LL | / pub fn borrow_mut(&mut self) -> &mut str { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `clone` on this type; consider implementing the `std::clone::Clone` trait or choosing a less ambiguous name - --> $DIR/methods.rs:75:5 - | -LL | / pub fn clone(&self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `cmp` on this type; consider implementing the `std::cmp::Ord` trait or choosing a less ambiguous name - --> $DIR/methods.rs:79:5 - | -LL | / pub fn cmp(&self, other: &Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `deref` on this type; consider implementing the `std::ops::Deref` trait or choosing a less ambiguous name - --> $DIR/methods.rs:87:5 - | -LL | / pub fn deref(&self) -> &Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `deref_mut` on this type; consider implementing the `std::ops::DerefMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:91:5 - | -LL | / pub fn deref_mut(&mut self) -> &mut Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `div` on this type; consider implementing the `std::ops::Div` trait or choosing a less ambiguous name - --> $DIR/methods.rs:95:5 - | -LL | / pub fn div(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `drop` on this type; consider implementing the `std::ops::Drop` trait or choosing a less ambiguous name - --> $DIR/methods.rs:99:5 - | -LL | / pub fn drop(&mut self) { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `eq` on this type; consider implementing the `std::cmp::PartialEq` trait or choosing a less ambiguous name - --> $DIR/methods.rs:103:5 - | -LL | / pub fn eq(&self, other: &Self) -> bool { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `from_iter` on this type; consider implementing the `std::iter::FromIterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:107:5 - | -LL | / pub fn from_iter(iter: T) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `from_str` on this type; consider implementing the `std::str::FromStr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:111:5 - | -LL | / pub fn from_str(s: &str) -> Result { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: docs for function returning `Result` missing `# Errors` section - --> $DIR/methods.rs:111:5 - | -LL | / pub fn from_str(s: &str) -> Result { -LL | | unimplemented!() -LL | | } - | |_____^ - | - = note: `-D clippy::missing-errors-doc` implied by `-D warnings` - -error: defining a method called `hash` on this type; consider implementing the `std::hash::Hash` trait or choosing a less ambiguous name - --> $DIR/methods.rs:115:5 - | -LL | / pub fn hash(&self, state: &mut T) { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `index` on this type; consider implementing the `std::ops::Index` trait or choosing a less ambiguous name - --> $DIR/methods.rs:119:5 - | -LL | / pub fn index(&self, index: usize) -> &Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `index_mut` on this type; consider implementing the `std::ops::IndexMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:123:5 - | -LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:127:5 - | -LL | / pub fn into_iter(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `mul` on this type; consider implementing the `std::ops::Mul` trait or choosing a less ambiguous name - --> $DIR/methods.rs:131:5 - | -LL | / pub fn mul(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `neg` on this type; consider implementing the `std::ops::Neg` trait or choosing a less ambiguous name - --> $DIR/methods.rs:135:5 - | -LL | / pub fn neg(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `next` on this type; consider implementing the `std::iter::Iterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:139:5 - | -LL | / pub fn next(&mut self) -> Option { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `not` on this type; consider implementing the `std::ops::Not` trait or choosing a less ambiguous name - --> $DIR/methods.rs:143:5 - | -LL | / pub fn not(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `rem` on this type; consider implementing the `std::ops::Rem` trait or choosing a less ambiguous name - --> $DIR/methods.rs:147:5 - | -LL | / pub fn rem(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `shl` on this type; consider implementing the `std::ops::Shl` trait or choosing a less ambiguous name - --> $DIR/methods.rs:151:5 - | -LL | / pub fn shl(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `shr` on this type; consider implementing the `std::ops::Shr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:155:5 - | -LL | / pub fn shr(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `sub` on this type; consider implementing the `std::ops::Sub` trait or choosing a less ambiguous name - --> $DIR/methods.rs:159:5 - | -LL | / pub fn sub(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - error: methods called `new` usually return `Self` - --> $DIR/methods.rs:300:5 + --> $DIR/methods.rs:105:5 | LL | / fn new() -> i32 { LL | | 0 @@ -253,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:319:13 + --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -262,7 +18,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:322:13 + --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -272,7 +28,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:339:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -280,25 +36,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:340:20 + --> $DIR/methods.rs:147:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:341:20 + --> $DIR/methods.rs:148:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:342:22 + --> $DIR/methods.rs:149:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:345:13 + --> $DIR/methods.rs:152:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -308,13 +64,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:351:22 + --> $DIR/methods.rs:158:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:354:13 + --> $DIR/methods.rs:161:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -324,13 +80,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:360:22 + --> $DIR/methods.rs:167:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:363:13 + --> $DIR/methods.rs:170:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -339,5 +95,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 42 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/should_impl_trait/corner_cases.rs b/tests/ui/should_impl_trait/corner_cases.rs new file mode 100644 index 00000000000..6c5ffe6aba8 --- /dev/null +++ b/tests/ui/should_impl_trait/corner_cases.rs @@ -0,0 +1,83 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +pub struct T1; +impl T1 { + // corner cases: should not lint + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: Self) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: Self) -> &Self { + self + } + + // No error; different number of arguments. + fn div(self) -> Self { + self + } + + // No error; wrong return type. + fn rem(self, other: Self) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> { + unimplemented!(); + } +} + +pub struct T2; +impl T2 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: Self) -> Self { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option { + None + } +} diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs new file mode 100644 index 00000000000..f8d248fc98d --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -0,0 +1,87 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 1, should lint all + // ***************************************** + pub fn add(self, other: T) -> T { + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + // ********** + // part 1 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr new file mode 100644 index 00000000000..2b7d4628c3f --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -0,0 +1,143 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> $DIR/method_list_1.rs:25:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> $DIR/method_list_1.rs:29:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> $DIR/method_list_1.rs:33:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> $DIR/method_list_1.rs:37:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> $DIR/method_list_1.rs:41:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> $DIR/method_list_1.rs:45:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> $DIR/method_list_1.rs:49:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> $DIR/method_list_1.rs:53:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> $DIR/method_list_1.rs:57:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> $DIR/method_list_1.rs:61:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> $DIR/method_list_1.rs:69:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> $DIR/method_list_1.rs:73:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> $DIR/method_list_1.rs:77:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> $DIR/method_list_1.rs:81:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs new file mode 100644 index 00000000000..ed5e0d384bf --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -0,0 +1,88 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 2, should lint all + // ***************************************** + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() + } + // ********** + // part 2 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr new file mode 100644 index 00000000000..b6fd4356956 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -0,0 +1,153 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> $DIR/method_list_2.rs:26:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> $DIR/method_list_2.rs:30:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> $DIR/method_list_2.rs:34:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> $DIR/method_list_2.rs:38:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> $DIR/method_list_2.rs:42:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> $DIR/method_list_2.rs:46:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> $DIR/method_list_2.rs:50:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> $DIR/method_list_2.rs:54:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> $DIR/method_list_2.rs:58:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> $DIR/method_list_2.rs:62:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> $DIR/method_list_2.rs:66:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> $DIR/method_list_2.rs:70:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> $DIR/method_list_2.rs:74:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> $DIR/method_list_2.rs:78:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> $DIR/method_list_2.rs:82:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + -- cgit 1.4.1-3-g733a5 From f9ba829f6701ae03a5c226044dbbde13ce87e123 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 9 Aug 2020 15:35:41 +0200 Subject: should_impl_trait - self linting --- clippy_lints/src/methods/mod.rs | 67 +++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 33 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a549c3b78ef..07e55ab0762 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1470,6 +1470,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if in_external_macro(cx.sess(), impl_item.span) { return; @@ -1501,7 +1502,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { sig.decl.inputs.len() == method_config.param_count && method_config.output_type.matches(cx, &sig.decl.output) && method_config.self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*method_config.fn_header, sig.header) && + fn_header_equals(method_config.fn_header, sig.header) && method_config.lifetime_param_cond(&impl_item) { span_lint_and_help( @@ -3422,7 +3423,7 @@ struct ShouldImplTraitCase { trait_name: &'static str, method_name: &'static str, param_count: usize, - fn_header: &'static hir::FnHeader, + fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) self_kind: SelfKind, // checks against the output type @@ -3435,7 +3436,7 @@ impl ShouldImplTraitCase { trait_name: &'static str, method_name: &'static str, param_count: usize, - fn_header: &'static hir::FnHeader, + fn_header: hir::FnHeader, self_kind: SelfKind, output_type: OutType, lint_explicit_lifetime: bool, @@ -3466,37 +3467,37 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", "add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), // FIXME: default doesn't work - ShouldImplTraitCase::new("std::default::Default", "default", 0, &FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", "div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", "index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", "not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] -- cgit 1.4.1-3-g733a5 From 3e3e50bf0fa6282c7265e34589170033c2301edd Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 08:50:20 -0600 Subject: Add example of false positive to PTR_ARG docs. Fixes #214 --- clippy_lints/src/ptr.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7b6bd69ffca..33c1bbd488a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -36,14 +36,27 @@ declare_clippy_lint! { /// argument may also fail to compile if you change the argument. Applying /// this lint on them will fix the problem, but they may be in other crates. /// + /// One notable example of a function that may cause issues, and which cannot + /// easily be changed due to beinng in the standard library is `Vec::contains`. + /// when called on a `Vec>`. If a `&Vec` is passed to that method then + /// it will compile, but if a `&[T]` is passed then it will not compile. + /// + /// ```ignore + /// fn cannot_take_a_slice(v: &Vec) -> bool { + /// let vec_of_vecs: Vec> = some_other_fn(); + /// + /// vec_of_vecs.contains(v) + /// } + /// ``` + /// /// Also there may be `fn(&Vec)`-typed references pointing to your function. /// If you have them, you will get a compiler error after applying this lint's /// suggestions. You then have the choice to undo your changes or change the /// type of the reference. /// /// Note that if the function is part of your public interface, there may be - /// other crates referencing it you may not be aware. Carefully deprecate the - /// function before applying the lint suggestions in this case. + /// other crates referencing it, of which you may not be aware. Carefully + /// deprecate the function before applying the lint suggestions in this case. /// /// **Example:** /// ```ignore -- cgit 1.4.1-3-g733a5 From fbf637d12c95528846bfa65ce67bd652f2affb43 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 09:19:40 -0600 Subject: formatting --- clippy_lints/src/ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 33c1bbd488a..7f58a381adc 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -55,7 +55,7 @@ declare_clippy_lint! { /// type of the reference. /// /// Note that if the function is part of your public interface, there may be - /// other crates referencing it, of which you may not be aware. Carefully + /// other crates referencing it, of which you may not be aware. Carefully /// deprecate the function before applying the lint suggestions in this case. /// /// **Example:** -- cgit 1.4.1-3-g733a5 From e57aafe33f338208e612ecb816a948d28f2c3741 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:06:33 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 28b276967bc..ac1c7aa9bbb 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -374,7 +374,7 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.") + span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines") } } diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index 4b77ac551e7..fb12257021a 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { diff --git a/tests/ui/functions_maxlines.stderr b/tests/ui/functions_maxlines.stderr index 9b0e7550cc3..c640c82d6d7 100644 --- a/tests/ui/functions_maxlines.stderr +++ b/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { -- cgit 1.4.1-3-g733a5 From 0876f17d77e8747f4cba889bd29fb64a0dc1a63f Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:14:12 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 8e44f2ec240..dc5aa669139 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>( cx, BOOL_COMPARISON, e.span, - "This comparison might be written more concisely", + "this comparison might be written more concisely", "try simplifying it as shown", format!( "{} != {}", diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index eeb1f20ee89..55d94b8257d 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:120:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:121:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:125:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:126:8 | LL | if !b == a {}; -- cgit 1.4.1-3-g733a5 From fd379a889e25c94c0568fe6fc08d0783bcbc3dd7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:16:10 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 29aba7c1218..38b11c5e733 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -271,7 +271,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, BUILTIN_TYPE_SHADOW, param.ident.span, - &format!("This generic shadows the built-in type `{}`", name), + &format!("this generic shadows the built-in type `{}`", name), ); } } diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr index bc785b075e0..f42b246afd2 100644 --- a/tests/ui/builtin-type-shadow.stderr +++ b/tests/ui/builtin-type-shadow.stderr @@ -1,4 +1,4 @@ -error: This generic shadows the built-in type `u32` +error: this generic shadows the built-in type `u32` --> $DIR/builtin-type-shadow.rs:4:8 | LL | fn foo(a: u32) -> u32 { -- cgit 1.4.1-3-g733a5 From 40416c0fa8409da63fb27f065e82cabb51ec17d3 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:18:13 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index dde799fcae4..cdb49d777d8 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { cx, NAIVE_BYTECOUNT, expr.span, - "You appear to be counting bytes the naive way", - "Consider using the bytecount crate", + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", format!("bytecount::count({}, {})", snippet_with_applicability(cx, haystack.span, "..", &mut applicability), snippet_with_applicability(cx, needle.span, "..", &mut applicability)), diff --git a/tests/ui/bytecount.stderr b/tests/ui/bytecount.stderr index 436f5d86a06..1dc37fc8b25 100644 --- a/tests/ui/bytecount.stderr +++ b/tests/ui/bytecount.stderr @@ -1,8 +1,8 @@ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:5:13 | LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)` | note: the lint level is defined here --> $DIR/bytecount.rs:1:8 @@ -10,17 +10,17 @@ note: the lint level is defined here LL | #[deny(clippy::naive_bytecount)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:7:13 | LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)` -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:19:13 | LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 5d66bd7bb3fd701d70ec11217e3f89fabe5cb0a7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 10 Aug 2020 23:38:58 +0200 Subject: 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(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 9f967d59c8b..223628cc610 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -43,6 +43,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -868,11 +869,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { + cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() + } + if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 + def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + const_eval::is_const_fn(cx.tcx, def_id) + }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2045ffdb5f0..67faa8bd4a0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -116,4 +116,12 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 522f31b72d0..9867e2eedcf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -116,4 +116,12 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 9b7ab1d38b13ad8af555793cdf7ce08c12c22595 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 16:58:20 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 841902943f0..28c1a54d2c5 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { cx, CHECKED_CONVERSIONS, item.span, - "Checked cast can be simplified.", + "checked cast can be simplified", "try", format!("{}::try_from({}).is_ok()", to_type, snippet), applicability, diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 648ba3ccd01..18518def0ac 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,4 +1,4 @@ -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; @@ -6,91 +6,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | = note: `-D clippy::checked-conversions` implied by `-D warnings` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; -- cgit 1.4.1-3-g733a5 From 8679dd375b928a3a5d8420401ed80057eea6f198 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 17:06:29 +0200 Subject: unnecessary_unwrap, panicking_unwrap: make lints adhere to lint message convention --- clippy_lints/src/unwrap.rs | 6 ++-- .../ui/checked_unwrap/complex_conditionals.stderr | 40 +++++++++++----------- .../complex_conditionals_nested.stderr | 4 +-- tests/ui/checked_unwrap/simple_conditionals.stderr | 26 +++++++------- 4 files changed, 38 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index f2bbde28c2a..fd755dcc790 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("You checked before that `{}()` cannot fail. \ - Instead of checking and unwrapping, it's better to use `if let` or `match`.", + &format!("you checked before that `{}()` cannot fail. \ + Instead of checking and unwrapping, it's better to use `if let` or `match`", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, ); @@ -191,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, PANICKING_UNWRAP, expr.span, - &format!("This call to `{}()` will always panic.", + &format!("this call to `{}()` will always panic", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, ); diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index dc666bab460..5b62dca629f 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:9:9 | LL | if x.is_ok() && y.is_err() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:10:9 | LL | if x.is_ok() && y.is_err() { @@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -45,7 +45,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:25:9 | LL | if x.is_ok() || y.is_ok() { @@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -63,7 +63,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:27:9 | LL | if x.is_ok() || y.is_ok() { @@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -89,7 +89,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:33:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -98,7 +98,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:34:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -125,7 +125,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:37:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -134,7 +134,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:45:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -161,7 +161,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:48:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -170,7 +170,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:49:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index e4d085470c3..46ffc16c23e 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals_nested.rs:10:13 | LL | if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 4013d1ed667..bf4b6c93098 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:41:9 | LL | if x.is_some() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:44:9 | LL | if x.is_none() { @@ -35,7 +35,7 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { @@ -44,7 +44,7 @@ LL | if x.is_none() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { @@ -57,7 +57,7 @@ LL | m!(x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { @@ -65,7 +65,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:55:9 | LL | if x.is_ok() { @@ -74,7 +74,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { @@ -83,7 +83,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { @@ -92,7 +92,7 @@ LL | if x.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:61:9 | LL | if x.is_err() { @@ -100,7 +100,7 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { @@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { @@ -118,7 +118,7 @@ LL | if x.is_err() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { -- cgit 1.4.1-3-g733a5 From 3d592b515492c8d054583078163c1986c44e222a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:21:31 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7b6bd69ffca..460d631fab0 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { cx, CMP_NULL, expr.span, - "Comparing with null is better expressed by the `.is_null()` method", + "comparing with null is better expressed by the `.is_null()` method", ); } } diff --git a/tests/ui/cmp_null.stderr b/tests/ui/cmp_null.stderr index b563a2ebec2..a1f4c70fb27 100644 --- a/tests/ui/cmp_null.stderr +++ b/tests/ui/cmp_null.stderr @@ -1,4 +1,4 @@ -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:9:8 | LL | if p == ptr::null() { @@ -6,7 +6,7 @@ LL | if p == ptr::null() { | = note: `-D clippy::cmp-null` implied by `-D warnings` -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:14:8 | LL | if m == ptr::null_mut() { -- cgit 1.4.1-3-g733a5 From 6b0a6a70f8dfa743707c440fdf908b9aceb1b8ae Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:39:35 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index ea244768129..874e19d9e9f 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("Calling `{}` is more clear than this expression", replacement), + &format!("calling `{}` is more clear than this expression", replacement), "try", replacement, Applicability::Unspecified, // First resolve the TODO above diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 453760c6b92..26b2057548b 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,4 +1,4 @@ -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:8:22 | LL | let s1: String = Default::default(); @@ -6,43 +6,43 @@ LL | let s1: String = Default::default(); | = note: `-D clippy::default-trait-access` implied by `-D warnings` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:12:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:14:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:18:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `GenericDerivedDefault::default()` is more clear than this expression +error: calling `GenericDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:28:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` -error: Calling `TupleDerivedDefault::default()` is more clear than this expression +error: calling `TupleDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:34:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` -error: Calling `ArrayDerivedDefault::default()` is more clear than this expression +error: calling `ArrayDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:36:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` -error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression +error: calling `TupleStructDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:40:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); -- cgit 1.4.1-3-g733a5 From ba7a01a6a8709eaff32f88d47382c940b24a3c9e Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:50:28 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 5d16192b754..bae7c4647d4 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -60,7 +60,7 @@ impl<'tcx> DoubleComparisons { cx, DOUBLE_COMPARISONS, span, - "This binary expression can be simplified", + "this binary expression can be simplified", "try", sugg, applicability, diff --git a/tests/ui/double_comparison.stderr b/tests/ui/double_comparison.stderr index 5dcda7b3af4..05ef4e25f7f 100644 --- a/tests/ui/double_comparison.stderr +++ b/tests/ui/double_comparison.stderr @@ -1,4 +1,4 @@ -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:6:8 | LL | if x == y || x < y { @@ -6,43 +6,43 @@ LL | if x == y || x < y { | = note: `-D clippy::double-comparisons` implied by `-D warnings` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:9:8 | LL | if x < y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x <= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:12:8 | LL | if x == y || x > y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:15:8 | LL | if x > y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:18:8 | LL | if x < y || x > y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:21:8 | LL | if x > y || x < y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:24:8 | LL | if x <= y && x >= y { | ^^^^^^^^^^^^^^^^ help: try: `x == y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:27:8 | LL | if x >= y && x <= y { -- cgit 1.4.1-3-g733a5 From 590b91d8d4250bd6e30e2396a36c54cdbae3780f Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:56:01 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 1eb380a22cc..abbcaf43f41 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -45,15 +45,12 @@ impl EarlyLintPass for DoubleParens { return; } + let msg: &str = "consider removing unnecessary double parentheses"; + match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint( - cx, - DOUBLE_PARENS, - expr.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, expr.span, &msg); }, _ => {}, }, @@ -61,12 +58,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, @@ -74,12 +66,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index 0e4c9b5682d..40fcad2ab1d 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,4 +1,4 @@ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:15:5 | LL | ((0)) @@ -6,31 +6,31 @@ LL | ((0)) | = note: `-D clippy::double-parens` implied by `-D warnings` -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:19:14 | LL | dummy_fn((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:23:20 | LL | x.dummy_method((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:27:5 | LL | ((1, 2)) | ^^^^^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:31:5 | LL | (()) | ^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:53:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); -- cgit 1.4.1-3-g733a5 From 0db5cb13934bff7a561dde2e13c3455120dc1bd4 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 20:05:42 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index 4afbd1ed0e5..ec3b6afa630 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -33,11 +33,11 @@ declare_clippy_lint! { /// ``` pub DROP_BOUNDS, correctness, - "Bounds of the form `T: Drop` are useless" + "bounds of the form `T: Drop` are useless" } -const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \ - Use `std::mem::needs_drop` to detect if a type has drop glue."; +const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \ + use `std::mem::needs_drop` to detect if a type has drop glue"; declare_lint_pass!(DropBounds => [DROP_BOUNDS]); diff --git a/tests/ui/drop_bounds.stderr b/tests/ui/drop_bounds.stderr index 5d360ef30a1..8208c0ed7e3 100644 --- a/tests/ui/drop_bounds.stderr +++ b/tests/ui/drop_bounds.stderr @@ -1,4 +1,4 @@ -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:2:11 | LL | fn foo() {} @@ -6,7 +6,7 @@ LL | fn foo() {} | = note: `#[deny(clippy::drop_bounds)]` on by default -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:5:8 | LL | T: Drop, -- cgit 1.4.1-3-g733a5 From 2792260636f720a44921c5f6571535e887aa6047 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 22:40:50 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3ce110e8e0f..376ac55f9c9 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -605,7 +605,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as cx, EMPTY_LINE_AFTER_OUTER_ATTR, begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ + "found an empty line after an outer attribute. \ Perhaps you forgot to add a `!` to make it an inner attribute?", ); } diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index bf753a732f0..594fca44a32 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,4 +1,4 @@ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] @@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } | = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] @@ -17,7 +17,7 @@ LL | | LL | | fn with_one_newline() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] @@ -26,7 +26,7 @@ LL | | LL | | fn with_two_newlines() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] @@ -34,7 +34,7 @@ LL | | LL | | enum Baz { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] @@ -42,7 +42,7 @@ LL | | LL | | struct Foo { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] -- cgit 1.4.1-3-g733a5 From 4418ff122fdc65d642dd4adb709634c4f879171e Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:31:33 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 38b11c5e733..02789735c17 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -298,9 +298,9 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, pat.span, - "All the struct fields are matched to a wildcard pattern, consider using `..`.", + "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("Try with `{} {{ .. }}` instead", type_name), + &format!("try with `{} {{ .. }}` instead", type_name), ); return; } @@ -313,7 +313,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` instead", + "you matched a field with a wildcard pattern, consider using `..` instead", ); } else { let mut normal = vec![]; @@ -333,10 +333,10 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` \ + "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), ); } } diff --git a/tests/ui/unneeded_field_pattern.stderr b/tests/ui/unneeded_field_pattern.stderr index e7b92ce1e19..b8d3c294532 100644 --- a/tests/ui/unneeded_field_pattern.stderr +++ b/tests/ui/unneeded_field_pattern.stderr @@ -1,19 +1,19 @@ -error: You matched a field with a wildcard pattern. Consider using `..` instead +error: you matched a field with a wildcard pattern, consider using `..` instead --> $DIR/unneeded_field_pattern.rs:14:15 | LL | Foo { a: _, b: 0, .. } => {}, | ^^^^ | = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings` - = help: Try with `Foo { b: 0, .. }` + = help: try with `Foo { b: 0, .. }` -error: All the struct fields are matched to a wildcard pattern, consider using `..`. +error: all the struct fields are matched to a wildcard pattern, consider using `..` --> $DIR/unneeded_field_pattern.rs:16:9 | LL | Foo { a: _, b: _, c: _ } => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Try with `Foo { .. }` instead + = help: try with `Foo { .. }` instead error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From b36a6c9594ffb7e225a3d8872d8de2889ea0bac1 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:36:20 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index fe457aad50e..3fda00403c6 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for RefInDeref { cx, REF_IN_DEREF, object.span, - "Creating a reference that is immediately dereferenced.", + "creating a reference that is immediately dereferenced", "try this", snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), applicability, diff --git a/tests/ui/unnecessary_ref.stderr b/tests/ui/unnecessary_ref.stderr index 34ba167a947..d0a0f219097 100644 --- a/tests/ui/unnecessary_ref.stderr +++ b/tests/ui/unnecessary_ref.stderr @@ -1,4 +1,4 @@ -error: Creating a reference that is immediately dereferenced. +error: creating a reference that is immediately dereferenced --> $DIR/unnecessary_ref.rs:13:17 | LL | let inner = (&outer).inner; -- cgit 1.4.1-3-g733a5 From 7954c22a99275a8b7be79c14d2bb882750de53ea Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:37:16 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 407527251da..234ae37612b 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -75,7 +75,7 @@ pub fn get_attr<'a>( }) .map_or_else( || { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute"); false }, |deprecation_status| { diff --git a/tests/ui/unknown_attribute.stderr b/tests/ui/unknown_attribute.stderr index 47e37aed246..618c5980d64 100644 --- a/tests/ui/unknown_attribute.stderr +++ b/tests/ui/unknown_attribute.stderr @@ -1,4 +1,4 @@ -error: Usage of unknown attribute +error: usage of unknown attribute --> $DIR/unknown_attribute.rs:1:11 | LL | #[clippy::unknown] -- cgit 1.4.1-3-g733a5 From fe37ddbd11dd3f2cb2e529846492b312e44ed1b9 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:45:24 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 502fffc5e6c..4e335a0222f 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_ARITHMETIC_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_OP_ASSIGN_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } } diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 7e42d72c30b..23d47e3f1ff 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,4 +1,4 @@ -error: Suspicious use of binary operator in `Add` impl +error: suspicious use of binary operator in `Add` impl --> $DIR/suspicious_arithmetic_impl.rs:11:20 | LL | Foo(self.0 - other.0) @@ -6,7 +6,7 @@ LL | Foo(self.0 - other.0) | = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` -error: Suspicious use of binary operator in `AddAssign` impl +error: suspicious use of binary operator in `AddAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:17:23 | LL | *self = *self - other; @@ -14,7 +14,7 @@ LL | *self = *self - other; | = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default -error: Suspicious use of binary operator in `MulAssign` impl +error: suspicious use of binary operator in `MulAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:30:16 | LL | self.0 /= other.0; -- cgit 1.4.1-3-g733a5 From 5d69ca5e114a1e44be08c835394b82ffc9cc7f14 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:46:52 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 234ae37612b..a3975683cb3 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -80,7 +80,7 @@ pub fn get_attr<'a>( }, |deprecation_status| { let mut diag = - sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { diag.emit(); diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index a399ff52fb8..88046762483 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,4 +1,4 @@ -error: Usage of deprecated attribute +error: usage of deprecated attribute --> $DIR/renamed_builtin_attr.rs:3:11 | LL | #[clippy::cyclomatic_complexity = "1"] -- cgit 1.4.1-3-g733a5 From 3e1e0c9bdb582b15c7804e354085b17f8b6c62d5 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:54:34 +0200 Subject: redundant-static-lifetimes: make lint adhere to lint message convention --- clippy_lints/src/redundant_static_lifetimes.rs | 4 +-- tests/ui/redundant_static_lifetimes.stderr | 32 +++++++++++----------- .../ui/redundant_static_lifetimes_multiple.stderr | 20 +++++++------- 3 files changed, 28 insertions(+), 28 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index c6f57298c26..7bbcc67aa2d 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { - self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); // Don't check associated consts because `'static` cannot be elided on those (issue // #2438) } if let ItemKind::Static(ref var_type, _, _) = item.kind { - self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "statics have by default a `'static` lifetime"); } } } diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 3c3d2eacd8d..649831f9c06 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. @@ -6,91 +6,91 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. diff --git a/tests/ui/redundant_static_lifetimes_multiple.stderr b/tests/ui/redundant_static_lifetimes_multiple.stderr index afc853dcfce..cc7e55a757a 100644 --- a/tests/ui/redundant_static_lifetimes_multiple.stderr +++ b/tests/ui/redundant_static_lifetimes_multiple.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:18 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static @@ -6,55 +6,55 @@ LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:30 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:29 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:39 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:40 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:55 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:26 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:38 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:37 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:47 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; -- cgit 1.4.1-3-g733a5 From 81f77a411e844ca553ba93adcc8be617d372ac30 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:12:21 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4c1f2e8e01a..f88075798ca 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { span_lint(cx, RANGE_ZIP_WITH_LEN, expr.span, - &format!("It is more idiomatic to use `{}.iter().enumerate()`", + &format!("it is more idiomatic to use `{}.iter().enumerate()`", snippet(cx, iter_args[0].span, "_"))); } } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index d53c1edecac..dcb5061371f 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -1,4 +1,4 @@ -error: It is more idiomatic to use `v1.iter().enumerate()` +error: it is more idiomatic to use `v1.iter().enumerate()` --> $DIR/range.rs:5:14 | LL | let _x = v1.iter().zip(0..v1.len()); -- cgit 1.4.1-3-g733a5 From 9178363574625a6185ea779c4a231b8f18205261 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:16:28 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 66a145a7f14..b8583402928 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { cx, PATH_BUF_PUSH_OVERWRITE, lit.span, - "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", "try", format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), Applicability::MachineApplicable, diff --git a/tests/ui/path_buf_push_overwrite.stderr b/tests/ui/path_buf_push_overwrite.stderr index 09b18d71baf..bb8dce2bbba 100644 --- a/tests/ui/path_buf_push_overwrite.stderr +++ b/tests/ui/path_buf_push_overwrite.stderr @@ -1,4 +1,4 @@ -error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition +error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition --> $DIR/path_buf_push_overwrite.rs:7:12 | LL | x.push("/bar"); -- cgit 1.4.1-3-g733a5 From e519bb3c850199d03eed7f2bd29637b3d5479551 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:18:34 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 4d4a9676654..3c041bac234 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -42,13 +42,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if let BinOpKind::Lt = op.node { if let BinOpKind::Add = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C overflow conditions that will fail in Rust."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } if let BinOpKind::Gt = op.node { if let BinOpKind::Sub = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C underflow conditions that will fail in Rust."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } @@ -67,13 +67,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if let BinOpKind::Gt = op.node { if let BinOpKind::Add = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C overflow conditions that will fail in Rust."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } if let BinOpKind::Lt = op.node { if let BinOpKind::Sub = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C underflow conditions that will fail in Rust."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } diff --git a/tests/ui/overflow_check_conditional.stderr b/tests/ui/overflow_check_conditional.stderr index ad66135d326..19e843c2c0a 100644 --- a/tests/ui/overflow_check_conditional.stderr +++ b/tests/ui/overflow_check_conditional.stderr @@ -1,4 +1,4 @@ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:8:8 | LL | if a + b < a {} @@ -6,43 +6,43 @@ LL | if a + b < a {} | = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:9:8 | LL | if a > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:10:8 | LL | if a + b < b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:11:8 | LL | if b > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:12:8 | LL | if a - b > b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:13:8 | LL | if b < a - b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:14:8 | LL | if a - b > a {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:15:8 | LL | if a < a - b {} -- cgit 1.4.1-3-g733a5 From 178da9b2ef9e8c94ab0fbe7812e11445664f67b0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:24:11 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 6b6c950e0ab..aa550510867 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)); if cx.typeck_results().expr_ty(exp).is_integral(); then { - span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`"); + span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`"); } } } diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index f08bbd6a12c..ad677f6d6fb 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr @@ -1,4 +1,4 @@ -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:27:5 | LL | x * -1; @@ -6,7 +6,7 @@ LL | x * -1; | = note: `-D clippy::neg-multiply` implied by `-D warnings` -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:29:5 | LL | -1 * x; -- cgit 1.4.1-3-g733a5 From dabf9891954aa0d0c59b230ab0e7afcfd4142be0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 28 Jul 2020 12:13:22 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 95613a1b82e..0f5d5ce3495 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { cx, NEG_CMP_OP_ON_PARTIAL_ORD, expr.span, - "The use of negated comparison operators on partially ordered \ + "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor. Please \ consider using the `partial_cmp` method instead, to make it \ clear that the two values could be incomparable." diff --git a/tests/ui/neg_cmp_op_on_partial_ord.stderr b/tests/ui/neg_cmp_op_on_partial_ord.stderr index 8c5d548222e..193d9f9bcea 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.stderr +++ b/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -1,4 +1,4 @@ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21 | LL | let _not_less = !(a_value < another_value); @@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value); | = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30 | LL | let _not_less_or_equal = !(a_value <= another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 | LL | let _not_greater = !(a_value > another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33 | LL | let _not_greater_or_equal = !(a_value >= another_value); -- cgit 1.4.1-3-g733a5 From c514ff0c93264e3cebd5eda9caf0b99fc8fd0daa Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 10 Aug 2020 23:50:40 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 0f5d5ce3495..4fb899125e8 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -80,9 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { NEG_CMP_OP_ON_PARTIAL_ORD, expr.span, "the use of negated comparison operators on partially ordered \ - types produces code that is hard to read and refactor. Please \ + types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable." + clear that the two values could be incomparable" ) } } -- cgit 1.4.1-3-g733a5 From 1b46e485b28613426f7bebccc009effad50fcaf7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 10 Aug 2020 23:50:52 +0200 Subject: Update clippy_lints/src/unwrap.rs Co-authored-by: Jane Lusby --- clippy_lints/src/unwrap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index fd755dcc790..ea4b8172c9c 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("you checked before that `{}()` cannot fail. \ - Instead of checking and unwrapping, it's better to use `if let` or `match`", + &format!("you checked before that `{}()` cannot fail, \ + instead of checking and unwrapping, it's better to use `if let` or `match`", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, ); -- cgit 1.4.1-3-g733a5 From b8713e3854cb90b974eceaa1d50484831591619c Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 12:35:55 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index b8dc5081632..be3ae7ab380 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -39,6 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { arguments, cx.typeck_results().expr_ty(fn_expr), &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)), + "function", ); } }, @@ -46,14 +47,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); - check_arguments(cx, arguments, method_type, &path.ident.as_str()) + check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method") }, _ => (), } } } -fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) { +fn check_arguments<'tcx>( + cx: &LateContext<'tcx>, + arguments: &[Expr<'_>], + type_definition: Ty<'tcx>, + name: &str, + fn_kind: &str, +) { match type_definition.kind { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); @@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("The function/method `{}` doesn't need a mutable reference", name), + &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), ); } }, diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index fa8c82ae0f3..062d30b262c 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,4 +1,4 @@ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | LL | takes_an_immutable_reference(&mut 42); @@ -6,13 +6,13 @@ LL | takes_an_immutable_reference(&mut 42); | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` -error: The function/method `as_ptr` doesn't need a mutable reference +error: the function `as_ptr` doesn't need a mutable reference --> $DIR/mut_reference.rs:19:12 | LL | as_ptr(&mut 42); | ^^^^^^^ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the method `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:23:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); -- cgit 1.4.1-3-g733a5 From 9311c11d7c01d64d22dc7914e9dff4c5167adb49 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 11 Aug 2020 13:57:32 +0200 Subject: Fix sync fallout --- clippy_lints/src/transmute.rs | 12 +++--- tests/compile-test.rs | 7 ++-- tests/ui/transmutes_expressible_as_ptr_casts.fixed | 38 +++++++------------ tests/ui/transmutes_expressible_as_ptr_casts.rs | 38 +++++++------------ .../ui/transmutes_expressible_as_ptr_casts.stderr | 44 +++++++++++----------- 5 files changed, 59 insertions(+), 80 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index f077c146183..7b5e92eb5ee 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -61,12 +61,14 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust,ignore - /// core::intrinsics::transmute::<*const [i32], *const [u16]>(p) + /// ```rust + /// # let p: *const [i32] = &[]; + /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; /// ``` /// Use instead: /// ```rust - /// p as *const [u16] + /// # let p: *const [i32] = &[]; + /// p as *const [u16]; /// ``` pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, complexity, @@ -704,14 +706,14 @@ fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::*; + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) ) } -/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 26a47d23706..e662d608edf 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -147,9 +147,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -217,6 +214,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 98288dde6d8..b6f1e83181c 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -9,60 +9,48 @@ use std::mem::{size_of, transmute}; -// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is // valid, which we quote from below. fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { - usize::MAX as *const i32 - }; + let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 }; let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { - ptr_i32 as *const i8 - }; + let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 }; let _ptr_i8 = ptr_i32 as *const i8; - let slice_ptr = &[0,1,2,3] as *const [i32]; + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast - let _ptr_to_unsized_transmute = unsafe { - slice_ptr as *const [u16] - }; + let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] }; let _ptr_to_unsized = slice_ptr as *const [u16]; // TODO: We could try testing vtable casts here too, but maybe // we should wait until std::raw::TraitObject is stabilized? // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast - let _usize_from_int_ptr_transmute = unsafe { - ptr_i32 as usize - }; + let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize }; let _usize_from_int_ptr = ptr_i32 as usize; - let array_ref: &[i32; 4] = &[1,2,3,4]; + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; // e has type &[T; n] and U is *const T; array-ptr-cast - let _array_ptr_transmute = unsafe { - array_ref as *const [i32; 4] - }; + let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] }; let _array_ptr = array_ref as *const [i32; 4]; - fn foo(_: usize) -> u8 { 42 } + fn foo(_: usize) -> u8 { + 42 + } // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast - let _usize_ptr_transmute = unsafe { - foo as *const usize - }; + let _usize_ptr_transmute = unsafe { foo as *const usize }; let _usize_ptr_transmute = foo as *const usize; // e is a function pointer type and U is an integer; fptr-addr-cast - let _usize_from_fn_ptr_transmute = unsafe { - foo as usize - }; + let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; let _usize_from_fn_ptr = foo as *const usize; } diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index fd5055c08f6..0205d1ece60 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -9,60 +9,48 @@ use std::mem::{size_of, transmute}; -// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is // valid, which we quote from below. fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { - transmute::(usize::MAX) - }; + let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { - transmute::<*const i32, *const i8>(ptr_i32) - }; + let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; let _ptr_i8 = ptr_i32 as *const i8; - let slice_ptr = &[0,1,2,3] as *const [i32]; + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast - let _ptr_to_unsized_transmute = unsafe { - transmute::<*const [i32], *const [u16]>(slice_ptr) - }; + let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; let _ptr_to_unsized = slice_ptr as *const [u16]; // TODO: We could try testing vtable casts here too, but maybe // we should wait until std::raw::TraitObject is stabilized? // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast - let _usize_from_int_ptr_transmute = unsafe { - transmute::<*const i32, usize>(ptr_i32) - }; + let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; let _usize_from_int_ptr = ptr_i32 as usize; - let array_ref: &[i32; 4] = &[1,2,3,4]; + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; // e has type &[T; n] and U is *const T; array-ptr-cast - let _array_ptr_transmute = unsafe { - transmute::<&[i32; 4], *const [i32; 4]>(array_ref) - }; + let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; let _array_ptr = array_ref as *const [i32; 4]; - fn foo(_: usize) -> u8 { 42 } + fn foo(_: usize) -> u8 { + 42 + } // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast - let _usize_ptr_transmute = unsafe { - transmute:: u8, *const usize>(foo) - }; + let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; let _usize_ptr_transmute = foo as *const usize; // e is a function pointer type and U is an integer; fptr-addr-cast - let _usize_from_fn_ptr_transmute = unsafe { - transmute:: u8, usize>(foo) - }; + let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; let _usize_from_fn_ptr = foo as *const usize; } diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 46597acc6c0..1157b179317 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,53 +1,53 @@ error: transmute from an integer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:20:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 | -LL | transmute::(usize::MAX) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` +LL | let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` | = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:26:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 | -LL | transmute::<*const i32, *const i8>(ptr_i32) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` +LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` | = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 | -LL | transmute::<*const [i32], *const [u16]>(slice_ptr) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` +LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:42:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 | -LL | transmute::<*const i32, usize>(ptr_i32) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` +LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` | = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:50:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 | -LL | transmute::<&[i32; 4], *const [i32; 4]>(array_ref) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` +LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:58:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 | -LL | transmute:: u8, *const usize>(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` +LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 | -LL | transmute:: u8, usize>(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` +LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:77:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` -- cgit 1.4.1-3-g733a5 From c0a9d64818d7076b72fd6c3a9e6172eca659034b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 11:50:26 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index cd7056620a2..22c49a20451 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -111,9 +111,9 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "Use {} instead of {}", - detection.method.unstable_name(), - detection.method.stable_name() + "used {} instead of {}", + detection.method.stable_name(), + detection.method.unstable_name() ) .as_str(), "try", diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b0b729ede48..b73012a4691 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); -- cgit 1.4.1-3-g733a5 From ac194cafc124276d4614bf023ca7ea6e9be9c6ed Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 11:53:21 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 641e6a17043..1cd5b201292 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { cx, MAP_CLONE, root.trim_start(receiver).unwrap(), - "You are needlessly cloning iterator elements", - "Remove the `map` call", + "you are needlessly cloning iterator elements", + "remove the `map` call", String::new(), Applicability::MachineApplicable, ) @@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for copying elements", - "Consider calling the dedicated `copied` method", + "you are using an explicit closure for copying elements", + "consider calling the dedicated `copied` method", format!( "{}.copied()", snippet_with_applicability(cx, root, "..", &mut applicability) @@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for cloning elements", - "Consider calling the dedicated `cloned` method", + "you are using an explicit closure for cloning elements", + "consider calling the dedicated `cloned` method", format!( "{}.cloned()", snippet_with_applicability(cx, root, "..", &mut applicability) diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 9eec6928e8c..4f43cff5024 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,40 +1,40 @@ -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: You are using an explicit closure for cloning elements +error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:14:26 | LL | let _: Option = Some(&16).map(|b| *b); - | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:15:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` -error: You are needlessly cloning iterator elements +error: you are needlessly cloning iterator elements --> $DIR/map_clone.rs:26:29 | LL | let _ = std::env::args().map(|v| v.clone()); - | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 04867e004ebc0f2edf66d0a457e785848451f13a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 12:10:42 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 568898aa5c9..21efee71269 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`.", + "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`", atomic_name ); match mutex_param.kind { diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 7dac0865855..a3511ba708a 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,4 +1,4 @@ -error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:6:5 | LL | Mutex::new(true); @@ -6,31 +6,31 @@ LL | Mutex::new(true); | = note: `-D clippy::mutex-atomic` implied by `-D warnings` -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:7:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:11:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(0u32); @@ -38,7 +38,7 @@ LL | Mutex::new(0u32); | = note: `-D clippy::mutex-integer` implied by `-D warnings` -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(0i32); -- cgit 1.4.1-3-g733a5 From 6af297f80e59050c87078f1ba6f05c97d6f90fd7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 12:42:50 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 570ae66d595..f4eb9c4516f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2280,7 +2280,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on a Slice without end index.", + "using `.iter().next()` on a Slice without end index", "try calling", format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), applicability, @@ -2299,7 +2299,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on an array", + "using `.iter().next()` on an array", "try calling", format!( "{}.get(0)", diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index bbf61df0cda..8c10a252ee0 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -1,4 +1,4 @@ -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:5 | LL | s.iter().next(); @@ -6,19 +6,19 @@ LL | s.iter().next(); | = note: `-D clippy::iter-next-slice` implied by `-D warnings` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:12:5 | LL | s[2..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:15:5 | LL | v[5..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:5 | LL | v.iter().next(); -- cgit 1.4.1-3-g733a5 From f171f89aed11043e459c3baab305e7f859debb94 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 15:14:32 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index e91fb0c2f27..c629ee05ab9 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -152,7 +152,7 @@ impl IntPlusOne { cx, INT_PLUS_ONE, block.span, - "Unnecessary `>= y + 1` or `x - 1 >=`", + "unnecessary `>= y + 1` or `x - 1 >=`", "change it to", recommendation, Applicability::MachineApplicable, // snippet @@ -163,8 +163,8 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec.clone()); + if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { + Self::emit_warning(cx, item, rec); } } } diff --git a/tests/ui/int_plus_one.stderr b/tests/ui/int_plus_one.stderr index 29a6914761c..c5b020ba8ce 100644 --- a/tests/ui/int_plus_one.stderr +++ b/tests/ui/int_plus_one.stderr @@ -1,4 +1,4 @@ -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:9:13 | LL | let _ = x >= y + 1; @@ -6,19 +6,19 @@ LL | let _ = x >= y + 1; | = note: `-D clippy::int-plus-one` implied by `-D warnings` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:10:13 | LL | let _ = y + 1 <= x; | ^^^^^^^^^^ help: change it to: `y < x` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:12:13 | LL | let _ = x - 1 >= y; | ^^^^^^^^^^ help: change it to: `x > y` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:13:13 | LL | let _ = y <= x - 1; -- cgit 1.4.1-3-g733a5 From bdf4dc3abd9a49f699d9de209a1f4d55ce770191 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 15:22:59 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5f931a0adde..b57fe8dc426 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { cx, IMPLICIT_SATURATING_SUB, expr.span, - "Implicitly performing saturating subtraction", + "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()), + format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), Applicability::MachineApplicable, ); } diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 2eb2023b3b9..5bb9a606422 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,4 +1,4 @@ -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:13:5 | LL | / if u_8 > 0 { @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:20:13 | LL | / if u_8 > 0 { @@ -16,7 +16,7 @@ LL | | u_8 -= 1; LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:34:5 | LL | / if u_16 > 0 { @@ -24,7 +24,7 @@ LL | | u_16 -= 1; LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:44:5 | LL | / if u_32 != 0 { @@ -32,7 +32,7 @@ LL | | u_32 -= 1; LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:65:5 | LL | / if u_64 > 0 { @@ -40,7 +40,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:70:5 | LL | / if 0 < u_64 { @@ -48,7 +48,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:75:5 | LL | / if 0 != u_64 { @@ -56,7 +56,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:96:5 | LL | / if u_usize > 0 { @@ -64,7 +64,7 @@ LL | | u_usize -= 1; LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:108:5 | LL | / if i_8 > i8::MIN { @@ -72,7 +72,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:113:5 | LL | / if i_8 > i8::MIN { @@ -80,7 +80,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:118:5 | LL | / if i_8 != i8::MIN { @@ -88,7 +88,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 != i8::MIN { @@ -96,7 +96,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_16 > i16::MIN { @@ -104,7 +104,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_16 > i16::MIN { @@ -112,7 +112,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:143:5 | LL | / if i_16 != i16::MIN { @@ -120,7 +120,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 != i16::MIN { @@ -128,7 +128,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_32 > i32::MIN { @@ -136,7 +136,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_32 > i32::MIN { @@ -144,7 +144,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:168:5 | LL | / if i_32 != i32::MIN { @@ -152,7 +152,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 != i32::MIN { @@ -160,7 +160,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i64::MIN < i_64 { @@ -168,7 +168,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i64::MIN != i_64 { @@ -176,7 +176,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:193:5 | LL | / if i64::MIN < i_64 { -- cgit 1.4.1-3-g733a5 From 1f17c3b02bce95c7c95a320e9e6e8e88b216d235 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 15:28:51 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 9fb10c7f627..4e6bb604785 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { cx, MULTIPLE_INHERENT_IMPL, *additional_span, - "Multiple implementations of this structure", + "multiple implementations of this structure", |diag| { - diag.span_note(*initial_span, "First implementation here"); + diag.span_note(*initial_span, "first implementation here"); }, ) }) diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index 585d32845d2..aab688cc2d8 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -1,4 +1,4 @@ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:10:1 | LL | / impl MyStruct { @@ -7,7 +7,7 @@ LL | | } | |_^ | = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { @@ -15,7 +15,7 @@ LL | | fn first() {} LL | | } | |_^ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:24:5 | LL | / impl super::MyStruct { @@ -23,7 +23,7 @@ LL | | fn third() {} LL | | } | |_____^ | -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { -- cgit 1.4.1-3-g733a5 From 423615693ba27f77f2e01a82948bbe592f48f6d0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:28:05 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index cb0fd59a2d4..d73d0f1752e 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -227,7 +227,7 @@ fn check_variant( cx, lint, span, - &format!("All variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {}fix: `{}`", what, value), None, &format!( "remove the {}fixes and use full paths to \ diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 2835391de7f..3aa0e4ddcfe 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -24,7 +24,7 @@ error: Variant name starts with the enum's name LL | FoodBad, | ^^^^^^^ -error: All variants have the same prefix: `Food` +error: all variants have the same prefix: `Food` --> $DIR/enum_variants.rs:26:1 | LL | / enum Food { @@ -36,7 +36,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `CallType` +error: all variants have the same prefix: `CallType` --> $DIR/enum_variants.rs:36:1 | LL | / enum BadCallType { @@ -48,7 +48,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Constant` +error: all variants have the same prefix: `Constant` --> $DIR/enum_variants.rs:48:1 | LL | / enum Consts { @@ -60,7 +60,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:82:1 | LL | / enum Seallll { @@ -72,7 +72,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Prefix` +error: all variants have the same prefix: `Prefix` --> $DIR/enum_variants.rs:88:1 | LL | / enum NonCaps { @@ -84,7 +84,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:94:1 | LL | / pub enum PubSeall { -- cgit 1.4.1-3-g733a5 From 2de290d5c5e1d2f0c0f112a51f0cba2e0cb91636 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:31:02 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 1dfb2eaa579..8ece44878fe 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { cx, DURATION_SUBSEC, expr.span, - &format!("Calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{}()` is more concise than this calculation", suggested_fn), "try", format!( "{}.{}()", diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index bd8adc2c570..cdbeff6a037 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -1,4 +1,4 @@ -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:10:24 | LL | let bad_millis_1 = dur.subsec_micros() / 1_000; @@ -6,25 +6,25 @@ LL | let bad_millis_1 = dur.subsec_micros() / 1_000; | = note: `-D clippy::duration-subsec` implied by `-D warnings` -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:11:24 | LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:16:22 | LL | let bad_micros = dur.subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:21:13 | LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:25:13 | LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; -- cgit 1.4.1-3-g733a5 From db390f5e6a2e68a0f9e0d235f2b734e907cafef9 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:35:09 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 91214f277be..48caf48dbdb 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { cx, ENUM_CLIKE_UNPORTABLE_VARIANT, var.span, - "Clike enum variant discriminant is not portable to 32-bit targets", + "C-like enum variant discriminant is not portable to 32-bit targets", ); }; } diff --git a/tests/ui/enum_clike_unportable_variant.stderr b/tests/ui/enum_clike_unportable_variant.stderr index 71f3f5e083e..5935eea5e03 100644 --- a/tests/ui/enum_clike_unportable_variant.stderr +++ b/tests/ui/enum_clike_unportable_variant.stderr @@ -1,4 +1,4 @@ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:8:5 | LL | X = 0x1_0000_0000, @@ -6,49 +6,49 @@ LL | X = 0x1_0000_0000, | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:15:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:18:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:25:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:26:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:28:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:35:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:40:5 | LL | X = ::Number, -- cgit 1.4.1-3-g733a5 From 89591a78b83df30830bcc8f6fe57f6fe1fbf918e Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:38:20 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d73d0f1752e..a9294a87f15 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -183,10 +183,10 @@ fn check_variant( && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) { - span_lint(cx, lint, var.span, "Variant name starts with the enum's name"); + span_lint(cx, lint, var.span, "variant name starts with the enum's name"); } if partial_rmatch(item_name, &name) == item_name_chars { - span_lint(cx, lint, var.span, "Variant name ends with the enum's name"); + span_lint(cx, lint, var.span, "variant name ends with the enum's name"); } } let first = &def.variants[0].ident.name.as_str(); diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 3aa0e4ddcfe..b1d481190ff 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -1,4 +1,4 @@ -error: Variant name ends with the enum's name +error: variant name ends with the enum's name --> $DIR/enum_variants.rs:16:5 | LL | cFoo, @@ -6,19 +6,19 @@ LL | cFoo, | = note: `-D clippy::enum-variant-names` implied by `-D warnings` -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:27:5 | LL | FoodGood, | ^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:28:5 | LL | FoodMiddle, | ^^^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:29:5 | LL | FoodBad, -- cgit 1.4.1-3-g733a5 From 605e027fda4420669784864940abcbabef5b0efe Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:40:45 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 5b22df5fe49..28b20cdeac3 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { cx, IF_LET_SOME_RESULT, expr.span.with_hi(op.span.hi()), - "Matching on `Some` with `ok()` is redundant", - &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), sugg, applicability, ); diff --git a/tests/ui/if_let_some_result.stderr b/tests/ui/if_let_some_result.stderr index 334ccb01016..6afee0f36b9 100644 --- a/tests/ui/if_let_some_result.stderr +++ b/tests/ui/if_let_some_result.stderr @@ -1,22 +1,22 @@ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | LL | if let Some(y) = x.parse().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x.parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:24:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x . parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From be3e695b60b07e911a88f6cb660c5617836c5365 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:43:53 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index c11e291f98e..b86d2e76656 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,7 +60,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary boolean `not` operation", + "unnecessary boolean `not` operation", None, "remove the `!` and swap the blocks of the `if`/`else`", ); @@ -70,7 +70,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary `!=` operation", + "unnecessary `!=` operation", None, "change to `==` and swap the blocks of the `if`/`else`", ); diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 776c6bc57ca..427a1b65773 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ``` pub USE_SELF, nursery, - "Unnecessary structure name repetition whereas `Self` is applicable" + "unnecessary structure name repetition whereas `Self` is applicable" } declare_lint_pass!(UseSelf => [USE_SELF]); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bbb300296be..ccc9e250952 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2498,7 +2498,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "use_self", group: "nursery", - desc: "Unnecessary structure name repetition whereas `Self` is applicable", + desc: "unnecessary structure name repetition whereas `Self` is applicable", deprecation: None, module: "use_self", }, diff --git a/tests/ui/if_not_else.stderr b/tests/ui/if_not_else.stderr index 78bc4d4bd20..53d1b86d02a 100644 --- a/tests/ui/if_not_else.stderr +++ b/tests/ui/if_not_else.stderr @@ -1,4 +1,4 @@ -error: Unnecessary boolean `not` operation +error: unnecessary boolean `not` operation --> $DIR/if_not_else.rs:9:5 | LL | / if !bla() { @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::if-not-else` implied by `-D warnings` = help: remove the `!` and swap the blocks of the `if`/`else` -error: Unnecessary `!=` operation +error: unnecessary `!=` operation --> $DIR/if_not_else.rs:14:5 | LL | / if 4 != 5 { -- cgit 1.4.1-3-g733a5 From 8a96b9cdfe408106fff94745fee1223b2e3ddb26 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 12 Aug 2020 14:27:06 +0200 Subject: write.rs: don't clone TokenStream --- clippy_lints/src/write.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 063f94582b9..5f88dcb188a 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -237,7 +237,7 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( cx, @@ -252,7 +252,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(print) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -273,7 +273,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -294,7 +294,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(writeln) { - if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; let suggestion = expr.map_or_else( @@ -364,17 +364,11 @@ impl Write { /// (Some("string to write: {}"), Some(buf)) /// ``` #[allow(clippy::too_many_lines)] - fn check_tts<'a>( - &self, - cx: &EarlyContext<'a>, - tts: &TokenStream, - is_write: bool, - ) -> (Option, Option) { + fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { use rustc_parse_format::{ AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser, Piece, }; - let tts = tts.clone(); let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); let mut expr: Option = None; -- cgit 1.4.1-3-g733a5 From 7d2e42daec1a56ad8f70a2b146bd842e98e0430d Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Wed, 12 Aug 2020 08:54:32 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7f58a381adc..31da45f6b8a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// this lint on them will fix the problem, but they may be in other crates. /// /// One notable example of a function that may cause issues, and which cannot - /// easily be changed due to beinng in the standard library is `Vec::contains`. + /// easily be changed due to being in the standard library is `Vec::contains`. /// when called on a `Vec>`. If a `&Vec` is passed to that method then /// it will compile, but if a `&[T]` is passed then it will not compile. /// -- cgit 1.4.1-3-g733a5 From 0fc61becb5f1b9a183311c8b053d996b8695a071 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 07:30:55 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 4e335a0222f..e026f27d9a7 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -86,12 +86,18 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, expr, binop.node, - &["Add", "Sub", "Mul", "Div"], + &["Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr"], &[ hir::BinOpKind::Add, hir::BinOpKind::Sub, hir::BinOpKind::Mul, hir::BinOpKind::Div, + hir::BinOpKind::Rem, + hir::BinOpKind::BitAnd, + hir::BinOpKind::BitOr, + hir::BinOpKind::BitXor, + hir::BinOpKind::Shl, + hir::BinOpKind::Shr, ], ) { span_lint( -- cgit 1.4.1-3-g733a5 From 616682deb72aa3760b1af3c701090e894f227ac7 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 09:18:16 -0600 Subject: formatting --- clippy_lints/src/suspicious_trait_impl.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index e026f27d9a7..596d4eb6141 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -86,7 +86,9 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, expr, binop.node, - &["Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr"], + &[ + "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr" + ], &[ hir::BinOpKind::Add, hir::BinOpKind::Sub, -- cgit 1.4.1-3-g733a5 From c70581732de89a1b4f064818edeca9a18913ded2 Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Mon, 10 Aug 2020 09:21:20 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 596d4eb6141..3a688a7bbef 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { expr, binop.node, &[ - "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr" + "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr", ], &[ hir::BinOpKind::Add, -- cgit 1.4.1-3-g733a5 From 480ccc3dbec4440bea0aa1f47d2ad21ebcdd578e Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 11 Aug 2020 18:01:10 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c3dea447521..78cebc30472 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -353,14 +353,25 @@ impl Types { ); return; // don't recurse into the type } - if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) { + if match_type_parameter(cx, qpath, &paths::BOX).is_some() { + let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => panic!("Box that isn't a type"), + }, + _ => panic!("Rc without type argument"), + }; + let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => panic!("Box without type argument"), + }; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), + format!("Rc<{}>", snippet(cx, inner_span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/tests/ui/redundant_allocation.fixed b/tests/ui/redundant_allocation.fixed index 26635833458..6514fd6d1ac 100644 --- a/tests/ui/redundant_allocation.fixed +++ b/tests/ui/redundant_allocation.fixed @@ -33,7 +33,7 @@ pub fn test5(a: Rc) {} // Rc> -pub fn test6(a: Box) {} +pub fn test6(a: Rc) {} // Box<&T> diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index eaa57ce3024..92e4f67f5db 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -28,7 +28,7 @@ error: usage of `Rc>` --> $DIR/redundant_allocation.rs:36:17 | LL | pub fn test6(a: Rc>) {} - | ^^^^^^^^^^^^^ help: try: `Box` + | ^^^^^^^^^^^^^ help: try: `Rc` error: usage of `Box<&T>` --> $DIR/redundant_allocation.rs:40:22 -- cgit 1.4.1-3-g733a5 From 5634c8da02862653be557c6ab1242a6c9ce86ce8 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 12 Aug 2020 21:37:27 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1bf37632e32..4ab2b5e796d 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,3 +1,4 @@ +use crate::utils::sugg::Sugg; use crate::utils::{ get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, @@ -158,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if TyS::same_type(a, b); then { - let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( @@ -167,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { e.span, "useless conversion to the same type", &sugg_msg, - sugg, + sugg.to_string(), Applicability::MachineApplicable, // snippet ); } diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 813cdaecaa9..8a9b0cd3cf0 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -64,4 +64,9 @@ fn main() { let _ = "".lines(); let _ = vec![1, 2, 3].into_iter(); let _: String = format!("Hello {}", "world"); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = (a + b) * 3; } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 540fea23b36..4faa1572973 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -64,4 +64,9 @@ fn main() { let _ = "".lines().into_iter(); let _ = vec![1, 2, 3].into_iter().into_iter(); let _: String = format!("Hello {}", "world").into(); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = i32::from(a + b) * 3; } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index b958b035452..f1e880d2696 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -64,5 +64,11 @@ error: useless conversion to the same type LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: aborting due to 10 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:71:13 + | +LL | let _ = i32::from(a + b) * 3; + | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 4e28d99f413572087a74e1a70b17f051a08d3821 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 12 Aug 2020 13:24:55 -0700 Subject: Replace panics with early returns --- clippy_lints/src/types.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 78cebc30472..e0204273197 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -357,13 +357,13 @@ impl Types { let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { GenericArg::Type(ty) => match &ty.kind { TyKind::Path(qpath) => qpath, - _ => panic!("Box that isn't a type"), + _ => return, }, - _ => panic!("Rc without type argument"), + _ => return, }; let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { GenericArg::Type(ty) => ty.span, - _ => panic!("Box without type argument"), + _ => return, }; span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From 48a142559de48ff38326e8276ffdfce9ef3b5c95 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Thu, 13 Aug 2020 13:52:21 -0600 Subject: docs: typo in `temporary_cstring_as_ptr`: s/point/&s --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f4eb9c4516f..b6266ef2ba1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -799,7 +799,7 @@ declare_clippy_lint! { /// call_some_ffi_func(c_str); /// } /// ``` - /// Here `c_str` point to a freed address. The correct use would be: + /// Here `c_str` points to a freed address. The correct use would be: /// ```rust /// # use std::ffi::CString; /// # fn call_some_ffi_func(_: *const i8) {} -- cgit 1.4.1-3-g733a5 From 8514b8407ac83dc02532c82c9188c49967d9a5d6 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 14 Aug 2020 14:13:35 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..4a4445200a1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -930,11 +930,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); let too_large_for_stack = conf.too_large_for_stack; store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); + store.register_late_pass(move || box vec::UselessVec{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); - store.register_late_pass(|| box vec::UselessVec); store.register_late_pass(|| box drop_bounds::DropBounds); store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ba3492a6fff..292dbd7ad6b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -138,7 +138,7 @@ define_Conf! { (type_complexity_threshold, "type_complexity_threshold": u64, 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), - /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (too_large_for_stack, "too_large_for_stack": u64, 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index f2e76442a19..84e907d7125 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,13 +1,20 @@ -use crate::consts::constant; +use crate::consts::{constant, Constant}; +use crate::rustc_target::abi::LayoutOf; use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct UselessVec { + pub too_large_for_stack: u64, +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would /// be possible. @@ -31,7 +38,7 @@ declare_clippy_lint! { "useless `vec!`" } -declare_lint_pass!(UselessVec => [USELESS_VEC]); +impl_lint_pass!(UselessVec => [USELESS_VEC]); impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -42,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, expr.span); } } @@ -60,46 +67,62 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, span); } } } } -fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { - let mut applicability = Applicability::MachineApplicable; - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if constant(cx, cx.typeck_results(), len).is_some() { - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - if let Some(last) = args.iter().last() { - let span = args[0].span.to(last.span); +impl UselessVec { + fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + let mut applicability = Applicability::MachineApplicable; + let snippet = match *vec_args { + higher::VecArgs::Repeat(elem, len) => { + if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + #[allow(clippy::cast_possible_truncation)] + if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { + return; + } - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) - } else { - "&[]".into() - } - }, - }; + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Some(last) = args.iter().last() { + #[allow(clippy::cast_possible_truncation)] + if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { + return; + } + let span = args[0].span.to(last.span); + + 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, + ); + } +} - span_lint_and_sugg( - cx, - USELESS_VEC, - span, - "useless use of `vec!`", - "you can use a slice directly", - snippet, - applicability, - ); +fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { + let ty = cx.typeck_results().expr_ty_adjusted(expr); + cx.layout_of(ty).map_or(0, |l| l.size.bytes()) } /// Returns the item type of the vector (i.e., the `T` in `Vec`). diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index e73a791891f..85677159620 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 3eb960f53d7..03b8ee81665 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } -- cgit 1.4.1-3-g733a5 From 8e549978e58fe724c4637ab71808ff8785743c61 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 22 Jul 2020 21:44:53 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..71433773254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1723,6 +1723,7 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..7ae185103a3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -296,6 +296,7 @@ mod swap; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; +mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; @@ -788,6 +789,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &to_string_in_display::TO_STRING_IN_DISPLAY, &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, @@ -1017,6 +1019,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box reference::DerefAddrOf); store.register_early_pass(|| box reference::RefInDeref); store.register_early_pass(|| box double_parens::DoubleParens); + store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new()); store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); store.register_early_pass(|| box if_not_else::IfNotElse); store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); @@ -1427,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), @@ -1708,6 +1712,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs new file mode 100644 index 00000000000..11bdd27d9b1 --- /dev/null +++ b/clippy_lints/src/to_string_in_display.rs @@ -0,0 +1,100 @@ +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for uses of `to_string()` in `Display` traits. + /// + /// **Why is this bad?** Usually `to_string` is implemented indirectly + /// via `Display`. Hence using it while implementing `Display` would + /// lead to infinite recursion. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.to_string()) + /// } + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// ``` + pub TO_STRING_IN_DISPLAY, + correctness, + "to_string method used while implementing Display trait" +} + +#[derive(Default)] +pub struct ToStringInDisplay { + in_display_impl: bool, +} + +impl ToStringInDisplay { + pub fn new() -> Self { + Self { in_display_impl: false } + } +} + +impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]); + +impl LateLintPass<'_> for ToStringInDisplay { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = true; + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = false; + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind; + if path.ident.name == sym!(to_string); + if match_trait_method(cx, expr, &paths::TO_STRING); + if self.in_display_impl; + + then { + span_lint( + cx, + TO_STRING_IN_DISPLAY, + expr.span, + "Using to_string in fmt::Display implementation might lead to infinite recursion", + ); + } + } + } +} + +fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if_chain! { + if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind; + if let Some(did) = trait_ref.trait_def_id(); + then { + match_def_path(cx, did, &paths::DISPLAY_TRAIT) + } else { + false + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..46827084a60 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "to_digit_is_some", }, + Lint { + name: "to_string_in_display", + group: "correctness", + desc: "to_string method used while implementing Display trait", + deprecation: None, + module: "to_string_in_display", + }, Lint { name: "todo", group: "restriction", diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs new file mode 100644 index 00000000000..3b46324704e --- /dev/null +++ b/tests/ui/to_string_in_display.rs @@ -0,0 +1,55 @@ +#![warn(clippy::to_string_in_display)] +#![allow(clippy::inherent_to_string_shadow_display)] + +use std::fmt; + +struct A; +impl A { + fn fmt(&self) { + self.to_string(); + } +} + +trait B { + fn fmt(&self) {} +} + +impl B for A { + fn fmt(&self) { + self.to_string(); + } +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn fmt(a: A) { + a.to_string(); +} + +struct C; + +impl C { + fn to_string(&self) -> String { + String::from("I am C") + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn main() { + let a = A; + a.to_string(); + a.fmt(); + fmt(a); + + let c = C; + c.to_string(); +} diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr new file mode 100644 index 00000000000..cbc0a41036b --- /dev/null +++ b/tests/ui/to_string_in_display.stderr @@ -0,0 +1,10 @@ +error: Using to_string in fmt::Display implementation might lead to infinite recursion + --> $DIR/to_string_in_display.rs:25:25 + | +LL | write!(f, "{}", self.to_string()) + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::to-string-in-display` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f98ffa271d0112d04b482e1d61228d99bf006ccf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 14 Aug 2020 22:54:12 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8352a8a3d2c..f7db2563d2b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1141,11 +1141,36 @@ fn detect_same_item_push<'tcx>( if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values - if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id) { + let node = cx.tcx.hir().get(hir_id); + if_chain! { + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + then { + match bind_ann { + BindingAnnotation::RefMut | BindingAnnotation::Mutable => {}, + _ => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } + } + } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index ff1088f86f6..bfe27e02044 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -86,4 +86,12 @@ fn main() { for a in vec_a { vec12.push(2u8.pow(a.kind)); } + + // Fix #5902 + let mut vec13: Vec = Vec::new(); + let mut item = 0; + for _ in 0..10 { + vec13.push(item); + item += 10; + } } -- cgit 1.4.1-3-g733a5 From 72d2c2eab42fe8c7247e4a45b01a6e7411898443 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Sun, 9 Aug 2020 17:47:11 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..3b9cbbf0dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1699,6 +1699,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..4f261ba932e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,6 +287,7 @@ mod repeat_once; mod returns; mod serde_api; mod shadow; +mod single_char_push_str; mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; @@ -775,6 +776,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, + &single_char_push_str::SINGLE_CHAR_PUSH_STR, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, @@ -932,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); + store.register_late_pass(|| box single_char_push_str::SingleCharPushStrPass); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box vec::UselessVec); @@ -1416,6 +1419,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), @@ -1556,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/single_char_push_str.rs b/clippy_lints/src/single_char_push_str.rs new file mode 100644 index 00000000000..68bbef7261a --- /dev/null +++ b/clippy_lints/src/single_char_push_str.rs @@ -0,0 +1,62 @@ +use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** This is in all probability not the intended outcome. At + /// the least it hurts readability of the code. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + +declare_lint_pass!(SingleCharPushStrPass => [SINGLE_CHAR_PUSH_STR]); + +impl<'tcx> LateLintPass<'tcx> for SingleCharPushStrPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; + if let [base_string, extension_string] = args; + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, fn_def_id, &paths::PUSH_STR); + if let ExprKind::Lit(ref lit) = extension_string.kind; + if let LitKind::Str(symbol,_) = lit.node; + let extension_string_val = symbol.as_str().to_string(); + if extension_string_val.len() == 1; + then { + let mut applicability = Applicability::MachineApplicable; + let base_string_snippet = snippet_with_applicability(cx, base_string.span, "_", &mut applicability); + let sugg = format!("{}.push({:?})", base_string_snippet, extension_string_val.chars().next().unwrap()); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 923b319d777..ffab0395120 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -84,6 +84,7 @@ pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE: [&str; 3] = ["core", "ops", "Range"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..000ab8b8f36 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2012,6 +2012,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "single_char_push_str", + group: "style", + desc: "`push_str()` used with a single-character string literal as parameter", + deprecation: None, + module: "single_char_push_str", + }, Lint { name: "single_component_path_imports", group: "style", diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed new file mode 100644 index 00000000000..49607c49218 --- /dev/null +++ b/tests/ui/single_char_push_str.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); +} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs new file mode 100644 index 00000000000..bbeebd027b1 --- /dev/null +++ b/tests/ui/single_char_push_str.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); +} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr new file mode 100644 index 00000000000..453ec2d42f1 --- /dev/null +++ b/tests/ui/single_char_push_str.stderr @@ -0,0 +1,16 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:6:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:7:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From ae56e988a2ae7b59c684cbbc5c326cb8097b3688 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 14 Aug 2020 12:52:19 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4f261ba932e..5e4a4a4f49c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,7 +287,6 @@ mod repeat_once; mod returns; mod serde_api; mod shadow; -mod single_char_push_str; mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; @@ -678,6 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, + &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -776,7 +776,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, - &single_char_push_str::SINGLE_CHAR_PUSH_STR, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, @@ -934,7 +933,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); - store.register_late_pass(|| box single_char_push_str::SingleCharPushStrPass); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box vec::UselessVec); @@ -1352,6 +1350,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1419,7 +1418,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), - LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), @@ -1536,6 +1534,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::WRONG_SELF_CONVENTION), @@ -1560,7 +1559,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6266ef2ba1..2986a5a5944 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1306,6 +1306,29 @@ declare_clippy_lint! { "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" } +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** it's less clear that we are pushing a single character + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1327,6 +1350,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, + SINGLE_CHAR_PUSH_STR, SEARCH_IS_SOME, TEMPORARY_CSTRING_AS_PTR, FILTER_NEXT, @@ -1441,6 +1465,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::lint(cx, expr, &args[0], self_ty); } + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + lint_single_char_push_string(cx, expr, args); + } + } + match self_ty.kind { ty::Ref(_, ty, _) if ty.kind == ty::Str => { for &(method, pos) in &PATTERN_METHODS { @@ -3124,15 +3154,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn get_hint_if_single_char_arg<'tcx>( + cx: &LateContext<'tcx>, + arg: &'tcx hir::Expr<'_>, + applicability: &mut Applicability, +) -> Option { if_chain! { if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; - if r.as_str().len() == 1; + let string = r.as_str(); + if string.len() == 1; then { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## @@ -3142,19 +3175,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr &snip[1..(snip.len() - 1)] }; let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); + Some(hint) + } else { + None } } } +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } +} + +/// lint for length-1 `str`s as argument for `push_str` +fn lint_single_char_push_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/single_char_push_str.rs b/clippy_lints/src/single_char_push_str.rs deleted file mode 100644 index 68bbef7261a..00000000000 --- a/clippy_lints/src/single_char_push_str.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Warns when using push_str with a single-character string literal, - /// and push with a char would work fine. - /// - /// **Why is this bad?** This is in all probability not the intended outcome. At - /// the least it hurts readability of the code. - /// - /// **Known problems:** None - /// - /// **Example:** - /// ``` - /// let mut string = String::new(); - /// string.push_str("R"); - /// ``` - /// Could be written as - /// ``` - /// let mut string = String::new(); - /// string.push('R'); - /// ``` - pub SINGLE_CHAR_PUSH_STR, - style, - "`push_str()` used with a single-character string literal as parameter" -} - -declare_lint_pass!(SingleCharPushStrPass => [SINGLE_CHAR_PUSH_STR]); - -impl<'tcx> LateLintPass<'tcx> for SingleCharPushStrPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; - if let [base_string, extension_string] = args; - if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, fn_def_id, &paths::PUSH_STR); - if let ExprKind::Lit(ref lit) = extension_string.kind; - if let LitKind::Str(symbol,_) = lit.node; - let extension_string_val = symbol.as_str().to_string(); - if extension_string_val.len() == 1; - then { - let mut applicability = Applicability::MachineApplicable; - let base_string_snippet = snippet_with_applicability(cx, base_string.span, "_", &mut applicability); - let sugg = format!("{}.push({:?})", base_string_snippet, extension_string_val.chars().next().unwrap()); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PUSH_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability - ); - } - } - } -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 000ab8b8f36..4fd32776874 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2017,7 +2017,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "`push_str()` used with a single-character string literal as parameter", deprecation: None, - module: "single_char_push_str", + module: "methods", }, Lint { name: "single_component_path_imports", diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index 49607c49218..0812c026a64 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -7,4 +7,9 @@ fn main() { string.push('\''); string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); } diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs index bbeebd027b1..ab293bbe4ee 100644 --- a/tests/ui/single_char_push_str.rs +++ b/tests/ui/single_char_push_str.rs @@ -7,4 +7,9 @@ fn main() { string.push_str("'"); string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 453ec2d42f1..0e9bdaa23e7 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -12,5 +12,23 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str("'"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` -error: aborting due to 2 previous errors +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:12:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:13:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:14:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From b381ade1795f36149e36a646cdc83ee2fff032bf Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Sat, 15 Aug 2020 01:40:47 +0200 Subject: elide lifetimes --- clippy_lints/src/methods/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2986a5a5944..614773a7e26 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1310,7 +1310,7 @@ declare_clippy_lint! { /// **What it does:** Warns when using push_str with a single-character string literal, /// and push with a char would work fine. /// - /// **Why is this bad?** it's less clear that we are pushing a single character + /// **Why is this bad?** It's less clear that we are pushing a single character /// /// **Known problems:** None /// @@ -3154,9 +3154,9 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -fn get_hint_if_single_char_arg<'tcx>( - cx: &LateContext<'tcx>, - arg: &'tcx hir::Expr<'_>, +fn get_hint_if_single_char_arg( + cx: &LateContext<'_>, + arg: &hir::Expr<'_>, applicability: &mut Applicability, ) -> Option { if_chain! { @@ -3183,7 +3183,7 @@ fn get_hint_if_single_char_arg<'tcx>( } /// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let mut applicability = Applicability::MachineApplicable; if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { span_lint_and_sugg( @@ -3199,7 +3199,7 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr } /// lint for length-1 `str`s as argument for `push_str` -fn lint_single_char_push_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { +fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); -- cgit 1.4.1-3-g733a5 From 6d18fe730e88903fa881f7b753369623108a5d55 Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 12 Aug 2020 15:43:44 +0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..d2f66cf9bd0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -256,6 +256,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; +mod needless_return; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -726,6 +727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + &needless_return::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -769,7 +771,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, @@ -1027,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_early_pass(|| box returns::Return); store.register_late_pass(|| box let_and_return::LetReturn); + store.register_late_pass(|| box needless_return::NeedlessReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1381,6 +1383,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_return::NEEDLESS_RETURN), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1413,7 +1416,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), @@ -1543,6 +1545,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&needless_return::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1554,7 +1557,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs new file mode 100644 index 00000000000..a8876619ac1 --- /dev/null +++ b/clippy_lints/src/needless_return.rs @@ -0,0 +1,166 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_ast::ast::Attribute; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_span::source_map::Span; +use rustc_middle::lint::in_external_macro; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; + +use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for return statements at the end of a block. + /// + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// **Known problems:** If the computation returning the value borrows a local + /// variable, removing the `return` may run afoul of the borrow checker. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +#[derive(PartialEq, Eq, Copy, Clone)] +enum RetReplacement { + Empty, + Block, +} + +declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); + +impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { + fn check_fn(&mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, _: Span, _: HirId) { + match kind { + FnKind::Closure(_) => { + check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) + } + FnKind::ItemFn(..) | FnKind::Method(..) => { + if let ExprKind::Block(ref block, _) = body.value.kind { + check_block_return(cx, block) + } + } + } + } +} + +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} + +fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { + if let Some(expr) = block.expr { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + } + _ => (), + } + } +} + + +fn check_final_expr( + cx: &LateContext<'_>, + expr: &Expr<'_>, + span: Option, + replacement: RetReplacement, +) { + match expr.kind { + // simple return is always "bad" + ExprKind::Ret(ref inner) => { + + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + emit_return_lint( + cx, + span.expect("`else return` is not possible"), + inner.as_ref().map(|i| i.span), + replacement, + ); + } + } + // a whole block? check it! + ExprKind::Block(ref block, _) => { + check_block_return(cx, block); + } + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + + ExprKind::Match(_, ref arms, source) => { + match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } + } + MatchSource::IfDesugar { contains_else_clause: true } | MatchSource::IfLetDesugar { contains_else_clause: true } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + } + _ => () + } + } + _ => (), + } +} + +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { + return; + } + + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } + }) + } + None => match replacement { + RetReplacement::Empty => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", + String::new(), + Applicability::MachineApplicable, + ); + } + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + } + }, + } +} diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8ed20995a70..2bd0cccd39d 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -3,38 +3,11 @@ use rustc_ast::ast; use rustc_ast::visit::FnKind; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for return statements at the end of a block. - /// - /// **Why is this bad?** Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// **Known problems:** If the computation returning the value borrows a local - /// variable, removing the `return` may run afoul of the borrow checker. - /// - /// **Example:** - /// ```rust - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```rust - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - pub NEEDLESS_RETURN, - style, - "using a return statement like `return expr;` where an expression would suffice" -} +use crate::utils::{span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -57,117 +30,12 @@ declare_clippy_lint! { "needless unit expression" } -#[derive(PartialEq, Eq, Copy, Clone)] -enum RetReplacement { - Empty, - Block, -} -declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]); - -impl Return { - // Check the final stmt or expr in a block for unnecessary return. - fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => { - self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), - } - } - } - - // Check the final expression in a block if it's a return. - fn check_final_expr( - &mut self, - cx: &EarlyContext<'_>, - expr: &ast::Expr, - span: Option, - replacement: RetReplacement, - ) { - match expr.kind { - // simple return is always "bad" - ast::ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - Self::emit_return_lint( - cx, - span.expect("`else return` is not possible"), - inner.as_ref().map(|i| i.span), - replacement, - ); - } - }, - // a whole block? check it! - ast::ExprKind::Block(ref block, _) => { - self.check_block_return(cx, block); - }, - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => { - self.check_block_return(cx, ifblock); - self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty); - }, - // a match expr, check all arms - ast::ExprKind::Match(_, ref arms) => { - for arm in arms { - self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - _ => (), - } - } - - fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() { - return; - } - - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - }, - } - } -} +declare_lint_pass!(Return => [UNUSED_UNIT]); impl EarlyLintPass for Return { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - match kind { - FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block), - FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty), - FnKind::Fn(.., None) => {}, - } + if_chain! { if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; if let ast::TyKind::Tup(ref vals) = ty.kind; @@ -234,9 +102,6 @@ impl EarlyLintPass for Return { } } -fn attr_is_cfg(attr: &ast::Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) -} // get the def site #[must_use] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..7464f1cc6de 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1534,7 +1534,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "using a return statement like `return expr;` where an expression would suffice", deprecation: None, - module: "returns", + module: "needless_return", }, Lint { name: "needless_update", -- cgit 1.4.1-3-g733a5 From 85f4ef0fbd92453cf480af4e3f9eed877071ea2e Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 12 Aug 2020 17:14:12 +0300 Subject: Visitor added --- clippy_lints/src/needless_return.rs | 45 +++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index a8876619ac1..861a7ec558c 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -2,12 +2,14 @@ use rustc_lint::{LateLintPass, LateContext}; use rustc_ast::ast::Attribute; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; +use rustc_hir::intravisit::{FnKind, walk_expr, NestedVisitorMap, Visitor}; use rustc_span::source_map::Span; use rustc_middle::lint::in_external_macro; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::subst::GenericArgKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for return statements at the end of a block. @@ -164,3 +166,42 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + -- cgit 1.4.1-3-g733a5 From 65d10c7abf4752923f040264c79433da8fc234ea Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 15:14:08 +0300 Subject: Borrow checker added --- clippy_lints/src/needless_return.rs | 99 ++++++++++++++++++++++--------------- clippy_lints/src/returns.rs | 5 +- tests/ui/needless_return.fixed | 10 ++++ tests/ui/needless_return.rs | 10 ++++ 4 files changed, 79 insertions(+), 45 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index 861a7ec558c..ba280cd5a61 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -1,13 +1,13 @@ -use rustc_lint::{LateLintPass, LateContext}; use rustc_ast::ast::Attribute; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, walk_expr, NestedVisitorMap, Visitor}; -use rustc_span::source_map::Span; -use rustc_middle::lint::in_external_macro; +use rustc_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_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; +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); - } - } - 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); + 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); + }, + _ => (), + }, _ => (), } } @@ -139,7 +157,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option match replacement { RetReplacement::Empty => { span_lint_and_sugg( @@ -151,7 +169,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { span_lint_and_sugg( cx, @@ -162,7 +180,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { NestedVisitorMap::None } } - diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2bd0cccd39d..593e2f6c74b 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -30,12 +30,10 @@ declare_clippy_lint! { "needless unit expression" } - declare_lint_pass!(Return => [UNUSED_UNIT]); impl EarlyLintPass for Return { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; if let ast::TyKind::Tup(ref vals) = ty.kind; @@ -102,7 +100,6 @@ impl EarlyLintPass for Return { } } - // get the def site #[must_use] fn get_def(span: Span) -> Option { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ad20e238107..6b5cf2626df 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,6 +69,16 @@ fn test_void_match(x: u32) { } } +mod no_lint_if_stmt_borrows { + mod issue_5858 { + fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); + } + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index af0cdfb207f..1a693c9aa53 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,6 +69,16 @@ fn test_void_match(x: u32) { } } +mod no_lint_if_stmt_borrows { + mod issue_5858 { + fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); + } + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); -- cgit 1.4.1-3-g733a5 From cd6ca72e0e4040a5c3a6623c2e8533db91db6042 Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 15:21:09 +0300 Subject: Known problems changed --- clippy_lints/src/needless_return.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index ba280cd5a61..eb0bf12c0ab 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -17,8 +17,7 @@ declare_clippy_lint! { /// **Why is this bad?** Removing the `return` and semicolon will make the code /// more rusty. /// - /// **Known problems:** If the computation returning the value borrows a local - /// variable, removing the `return` may run afoul of the borrow checker. + /// **Known problems:** None. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From a7d5c2f967dd1f075ba5f8c4ca05c4b2ca2d22b4 Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 19:24:34 +0300 Subject: 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 | 347 +++++++++++++++++++++++++----------- clippy_lints/src/unused_unit.rs | 145 +++++++++++++++ tests/ui/needless_return.fixed | 11 ++ tests/ui/needless_return.rs | 11 ++ 7 files changed, 425 insertions(+), 462 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs deleted file mode 100644 index fa560ffb980..00000000000 --- a/clippy_lints/src/let_and_return.rs +++ /dev/null @@ -1,124 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_lint_pass!(LetReturn => [LET_AND_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for LetReturn { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d2f66cf9bd0..63de0f8a0c2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -218,7 +218,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -256,7 +255,6 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; -mod needless_return; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -311,6 +309,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; +mod unused_unit; mod unwrap; mod use_self; mod useless_conversion; @@ -587,7 +586,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &let_and_return::LET_AND_RETURN, + &returns::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -727,7 +726,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &needless_return::NEEDLESS_RETURN, + &returns::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -771,7 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &returns::UNUSED_UNIT, + &unused_unit::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1026,9 +1025,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box misc_early::MiscEarlyLints); store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); - store.register_early_pass(|| box returns::Return); - store.register_late_pass(|| box let_and_return::LetReturn); - store.register_late_pass(|| box needless_return::NeedlessReturn); + store.register_early_pass(|| box unused_unit::UnusedUnit); + store.register_late_pass(|| box returns::Return); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1286,7 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1383,7 +1381,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_return::NEEDLESS_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1416,7 +1414,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1502,7 +1500,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1545,7 +1543,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&needless_return::NEEDLESS_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1557,7 +1555,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs deleted file mode 100644 index eb0bf12c0ab..00000000000 --- a/clippy_lints/src/needless_return.rs +++ /dev/null @@ -1,223 +0,0 @@ -use rustc_ast::ast::Attribute; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for return statements at the end of a block. - /// - /// **Why is this bad?** Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```rust - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - pub NEEDLESS_RETURN, - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -#[derive(PartialEq, Eq, Copy, Clone)] -enum RetReplacement { - Empty, - Block, -} - -declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - _: Span, - _: HirId, - ) { - match kind { - FnKind::Closure(_) => { - if !last_statement_borrows(cx, &body.value) { - check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) - } - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(ref block, _) = body.value.kind { - if let Some(expr) = block.expr { - if !last_statement_borrows(cx, expr) { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { - if !last_statement_borrows(cx, expr) { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - } - }, - _ => (), - } - } - } - }, - } - } -} - -fn attr_is_cfg(attr: &Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) -} - -fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { - if let Some(expr) = block.expr { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), - } - } -} - -fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option, replacement: RetReplacement) { - match expr.kind { - // simple return is always "bad" - ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - emit_return_lint( - cx, - span.expect("`else return` is not possible"), - inner.as_ref().map(|i| i.span), - replacement, - ); - } - }, - // a whole block? check it! - ExprKind::Block(ref block, _) => { - check_block_return(cx, block); - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, ref arms, source) => match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - MatchSource::IfDesugar { - contains_else_clause: true, - } - | MatchSource::IfLetDesugar { - contains_else_clause: true, - } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { - check_block_return(cx, ifblock); - } - check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); - }, - _ => (), - }, - _ => (), - } -} - -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { - return; - } - - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - }, - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 593e2f6c74b..4d91f9be999 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,145 +1,290 @@ use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; -use crate::utils::span_lint_and_sugg; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { - /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. /// - /// **Why is this bad?** Such expressions add no value, but can make the code - /// less readable. Depending on formatting they can make a `break` or `return` - /// statement look like a function call. + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust - /// fn return_unit() -> () { - /// () + /// fn foo() -> String { + /// let x = String::new(); + /// x /// } /// ``` - pub UNUSED_UNIT, + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, style, - "needless unit expression" + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" } -declare_lint_pass!(Return => [UNUSED_UNIT]); +declare_clippy_lint! { + /// **What it does:** Checks for return statements at the end of a block. + /// + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, + style, + "using a return statement like `return expr;` where an expression would suffice" +} -impl EarlyLintPass for Return { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - lint_unneeded_unit_return(cx, ty, span); - } - } - } +#[derive(PartialEq, Eq, Copy, Clone)] +enum RetReplacement { + Empty, + Block, +} + +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { +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 check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { - let segments = &poly.trait_ref.path.segments; +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} - 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 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); + }, + _ => (), } } } -// 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 check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + span: Option, + replacement: RetReplacement, +) { + if last_statement_borrows(cx, expr) { + 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 + 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); + }, + _ => (), + }, + _ => (), } } -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, - ) +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); + } }) - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - ret_span, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + }, + None => match replacement { + RetReplacement::Empty => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", + String::new(), + Applicability::MachineApplicable, + ); + }, + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + }, + }, + } +} + +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs new file mode 100644 index 00000000000..e322e402535 --- /dev/null +++ b/clippy_lints/src/unused_unit.rs @@ -0,0 +1,145 @@ +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::span_lint_and_sugg; + +declare_clippy_lint! { + /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// + /// **Why is this bad?** Such expressions add no value, but can make the code + /// less readable. Depending on formatting they can make a `break` or `return` + /// statement look like a function call. + /// + /// **Known problems:** The lint currently misses unit return types in types, + /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// + /// **Example:** + /// ```rust + /// fn return_unit() -> () { + /// () + /// } + /// ``` + pub UNUSED_UNIT, + style, + "needless unit expression" +} + +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); + +impl EarlyLintPass for UnusedUnit { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { + if_chain! { + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; + if let ast::TyKind::Tup(ref vals) = ty.kind; + if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); + then { + lint_unneeded_unit_return(cx, ty, span); + } + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + if_chain! { + if let Some(ref stmt) = block.stmts.last(); + if let ast::StmtKind::Expr(ref expr) = stmt.kind; + if is_unit_expr(expr) && !stmt.span.from_expansion(); + then { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + match e.kind { + ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { + if is_unit_expr(expr) && !expr.span.from_expansion() { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } +} + +// get the def site +#[must_use] +fn get_def(span: Span) -> Option { + if span.from_expansion() { + Some(span.ctxt().outer_expn_data().def_site) + } else { + None + } +} + +// is this expr a `()` unit? +fn is_unit_expr(expr: &ast::Expr) -> bool { + if let ast::ExprKind::Tup(ref vals) = expr.kind { + vals.is_empty() + } else { + false + } +} + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 6b5cf2626df..b795516f999 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -76,6 +76,17 @@ mod no_lint_if_stmt_borrows { let stdin = ::std::io::stdin(); return stdin.lock().lines().next().unwrap().unwrap(); } + + fn read_line2(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } + } } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 1a693c9aa53..3547991935d 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -76,6 +76,17 @@ mod no_lint_if_stmt_borrows { let stdin = ::std::io::stdin(); return stdin.lock().lines().next().unwrap().unwrap(); } + + fn read_line2(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } + } } } -- cgit 1.4.1-3-g733a5 From 96efaee55240a3d3428ea5fe30c884a3227dad6e Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 19:30:49 +0300 Subject: cargo dev update_lints --- clippy_lints/src/lib.rs | 18 +++++++++--------- src/lintlist/mod.rs | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 63de0f8a0c2..c0b645e9311 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -586,7 +586,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &returns::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -726,7 +725,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &returns::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -770,7 +768,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &unused_unit::UNUSED_UNIT, + &returns::LET_AND_RETURN, + &returns::NEEDLESS_RETURN, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -840,6 +839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, + &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, &use_self::USE_SELF, @@ -1284,7 +1284,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1381,7 +1380,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1414,7 +1412,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&unused_unit::UNUSED_UNIT), + LintId::of(&returns::LET_AND_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1460,6 +1459,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), @@ -1500,7 +1500,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&returns::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1543,7 +1542,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1555,7 +1553,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&unused_unit::UNUSED_UNIT), + LintId::of(&returns::LET_AND_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), @@ -1564,6 +1563,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 7464f1cc6de..dff6198d00d 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1037,7 +1037,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", deprecation: None, - module: "let_and_return", + module: "returns", }, Lint { name: "let_underscore_lock", @@ -1534,7 +1534,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "using a return statement like `return expr;` where an expression would suffice", deprecation: None, - module: "needless_return", + module: "returns", }, Lint { name: "needless_update", @@ -2479,7 +2479,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "needless unit expression", deprecation: None, - module: "returns", + module: "unused_unit", }, Lint { name: "unwrap_used", -- cgit 1.4.1-3-g733a5 From baa4cb1cddc3a8ce1f47c4006e236edf082ee858 Mon Sep 17 00:00:00 2001 From: jrqc Date: Fri, 14 Aug 2020 09:25:26 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4d91f9be999..3c5541e64b4 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -160,10 +160,6 @@ fn check_final_expr<'tcx>( span: Option, replacement: RetReplacement, ) { - if last_statement_borrows(cx, expr) { - return; - } - match expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index e322e402535..7548c6afa97 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -16,8 +16,7 @@ declare_clippy_lint! { /// less readable. Depending on formatting they can make a `break` or `return` /// statement look like a function call. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index b795516f999..d849e093da7 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,24 +69,20 @@ fn test_void_match(x: u32) { } } -mod no_lint_if_stmt_borrows { - mod issue_5858 { - fn read_line() -> String { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - return stdin.lock().lines().next().unwrap().unwrap(); - } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} - fn read_line2(value: bool) -> String { - if value { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - let _a = stdin.lock().lines().next().unwrap().unwrap(); - return String::from("test"); - } else { - return String::new(); - } - } +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + String::from("test") + } else { + String::new() } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 3547991935d..29f2bd1852a 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,24 +69,20 @@ fn test_void_match(x: u32) { } } -mod no_lint_if_stmt_borrows { - mod issue_5858 { - fn read_line() -> String { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - return stdin.lock().lines().next().unwrap().unwrap(); - } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} - fn read_line2(value: bool) -> String { - if value { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - let _a = stdin.lock().lines().next().unwrap().unwrap(); - return String::from("test"); - } else { - return String::new(); - } - } +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); } } diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index c34eecbcbb6..f73c833a801 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -72,5 +72,17 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` -error: aborting due to 12 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:83:9 + | +LL | return String::from("test"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:85:9 + | +LL | return String::new(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 1a140dcc1c933ae84365bd1153372a7430f6f647 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 16 Aug 2020 00:25:54 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 6ce36fd2360..9555459e240 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,16 +1,22 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{AttrKind, Attribute}; +use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::EmitterWriter; +use rustc_errors::Handler; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; +use rustc_parse::maybe_new_parser_from_source_str; +use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{BytePos, MultiSpan, Span}; -use rustc_span::Pos; +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::{FileName, Pos}; +use std::io; use std::ops::Range; use url::Url; @@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { + fn has_needless_main(code: &str) -> bool { + let filename = FileName::anon_source_code(code); + + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); + } + return false; + }, + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + Ok(Some(item)) => match &item.kind { + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) => return false, + // We found a main function ... + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + } + + if has_needless_main(text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 682d7b3c4ce..883683e08a2 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -9,8 +9,14 @@ /// } /// ``` /// -/// This should, too. +/// With an explicit return type it should lint too +/// ``` +/// fn main() -> () { +/// unimplemented!(); +/// } +/// ``` /// +/// This should, too. /// ```rust /// fn main() { /// unimplemented!(); @@ -18,7 +24,6 @@ /// ``` /// /// This one too. -/// /// ```no_run /// fn main() { /// unimplemented!(); @@ -33,6 +38,20 @@ fn bad_doctests() {} /// fn main(){} /// ``` /// +/// This shouldn't lint either, because main is async: +/// ``` +/// async fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Same here, because the return type is not the unit type: +/// ``` +/// fn main() -> Result<()> { +/// Ok(()) +/// } +/// ``` +/// /// This shouldn't lint either, because there's a `static`: /// ``` /// static ANSWER: i32 = 42; @@ -42,6 +61,15 @@ fn bad_doctests() {} /// } /// ``` /// +/// This shouldn't lint either, because there's a `const`: +/// ``` +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// +/// const ANSWER: i32 = 42; +/// ``` +/// /// Neither should this lint because of `extern crate`: /// ``` /// #![feature(test)] @@ -51,8 +79,41 @@ fn bad_doctests() {} /// } /// ``` /// -/// We should not lint ignored examples: +/// Neither should this lint because it has an extern block: +/// ``` +/// extern {} +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This should not lint because there is another function defined: +/// ``` +/// fn fun() {} +/// +/// fn main() { +/// unimplemented!(); +/// } +/// ``` /// +/// We should not lint inside raw strings ... +/// ``` +/// let string = r#" +/// fn main() { +/// unimplemented!(); +/// } +/// "#; +/// ``` +/// +/// ... or comments +/// ``` +/// // fn main() { +/// // let _inception = 42; +/// // } +/// let _inception = 42; +/// ``` +/// +/// We should not lint ignored examples: /// ```rust,ignore /// fn main() { /// unimplemented!(); @@ -60,7 +121,6 @@ fn bad_doctests() {} /// ``` /// /// Or even non-rust examples: -/// /// ```text /// fn main() { /// is what starts the program diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 65d40ee6832..05c7f9d33a7 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -7,16 +7,22 @@ LL | /// fn main() { = note: `-D clippy::needless-doctest-main` implied by `-D warnings` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:15:4 + --> $DIR/needless_doc_main.rs:14:4 + | +LL | /// fn main() -> () { + | ^^^^^^^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:21:4 | LL | /// fn main() { | ^^^^^^^^^^^^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:23:4 + --> $DIR/needless_doc_main.rs:28:4 | LL | /// fn main() { | ^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From a3ea65c2d9bc52f308745ae488435388fce753a2 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 11:21:56 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 50fe7612909..c21190e9b9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1646,6 +1646,7 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option +[`option_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_or_else [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 986e9d9bee4..cb29f71387b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -672,6 +672,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, + &methods::UNNECESSARY_LAZY_EVALUATION, &methods::OR_FUN_CALL, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, @@ -1360,6 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1610,6 +1612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2330978e67f..61b7f2647ee 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1329,6 +1329,32 @@ declare_clippy_lint! { "`push_str()` used with a single-character string literal as parameter" } +declare_clippy_lint! { + /// **What it does:** Looks for unnecessary lazily evaluated closures on `Option` and `Result`. + /// + /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let opt: Option = None; + /// + /// opt.unwrap_or_else(|| 42); + /// ``` + /// Use instead: + /// ```rust + /// let opt: Option = None; + /// + /// opt.unwrap_or(42); + /// ``` + pub UNNECESSARY_LAZY_EVALUATION, + style, + "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1378,6 +1404,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, + UNNECESSARY_LAZY_EVALUATION, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1398,13 +1425,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), + ["unwrap_or_else", "map"] => { + lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"); + lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]); + }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { + lint_lazy_eval(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { + lint_lazy_eval(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1448,6 +1480,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["unwrap_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -2663,6 +2698,99 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +fn lint_lazy_eval<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(result_type)); + + if !is_option && !is_result { + return; + } + + // Return true if the expression is an accessor of any of the arguments + fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) + } + + fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + let simplify = match ex.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(ex, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if allow_variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + !args.iter().any(|arg| expr_uses_argument(arg, params)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + }; + + if simplify { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATION, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } +} + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 233f95deedd..1e76163f946 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2383,6 +2383,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "unnecessary_lazy_eval", + group: "style", + desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", + deprecation: None, + module: "methods", + }, Lint { name: "unnecessary_mut_passed", group: "style", diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed new file mode 100644 index 00000000000..fcfa6dfe12d --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -0,0 +1,105 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluation)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: u32, +} + +impl SomeStruct { + fn return_some_field(&self) -> u32 { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_str = SomeStruct { some_field: 10 }; + + // Should lint - Option + let mut opt = Some(42); + let ext_opt = Some(42); + let _ = opt.unwrap_or(2); + let _ = opt.unwrap_or(astronomers_pi); + let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.and(ext_opt); + let _ = opt.or(ext_opt); + let _ = opt.or(None); + let _ = opt.get_or_insert(2); + let _ = opt.ok_or(2); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or(2); + let _ = Some(10).and(ext_opt); + let _: Option = None.or(ext_opt); + let _ = None.get_or_insert(2); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or(2); + let _ = deep.0.and(ext_opt); + let _ = deep.0.or(None); + let _ = deep.0.get_or_insert(2); + let _ = deep.0.ok_or(2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or(2); + let _ = res2.unwrap_or(astronomers_pi); + let _ = res2.unwrap_or(ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..04b3c8ae1e2 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -0,0 +1,105 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_eval)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: u32, +} + +impl SomeStruct { + fn return_some_field(&self) -> u32 { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_str = SomeStruct { some_field: 10 }; + + // Should lint - Option + let mut opt = Some(42); + let ext_opt = Some(42); + let _ = opt.unwrap_or_else(|| 2); + let _ = opt.unwrap_or_else(|| astronomers_pi); + let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.and_then(|_| ext_opt); + let _ = opt.or_else(|| ext_opt); + let _ = opt.or_else(|| None); + let _ = opt.get_or_insert_with(|| 2); + let _ = opt.ok_or_else(|| 2); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or_else(|| 2); + let _ = Some(10).and_then(|_| ext_opt); + let _: Option = None.or_else(|| ext_opt); + let _ = None.get_or_insert_with(|| 2); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or_else(|| 2); + let _ = deep.0.and_then(|_| ext_opt); + let _ = deep.0.or_else(|| None); + let _ = deep.0.get_or_insert_with(|| 2); + let _ = deep.0.ok_or_else(|| 2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or_else(|_| 2); + let _ = res2.unwrap_or_else(|_| astronomers_pi); + let _ = res2.unwrap_or_else(|_| ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr new file mode 100644 index 00000000000..b941bf84246 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -0,0 +1,144 @@ +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:30:13 + | +LL | let _ = opt.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:31:13 + | +LL | let _ = opt.unwrap_or_else(|| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:32:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:33:13 + | +LL | let _ = opt.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 + | +LL | let _ = opt.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:36:13 + | +LL | let _ = opt.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:37:13 + | +LL | let _ = opt.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:40:13 + | +LL | let _ = Some(10).unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:41:13 + | +LL | let _ = Some(10).and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:42:26 + | +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | +LL | let _ = None.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:44:31 + | +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:45:26 + | +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:48:13 + | +LL | let _ = deep.0.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:49:13 + | +LL | let _ = deep.0.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:50:13 + | +LL | let _ = deep.0.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:51:13 + | +LL | let _ = deep.0.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:52:13 + | +LL | let _ = deep.0.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:73:13 + | +LL | let _ = res2.unwrap_or_else(|_| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:74:13 + | +LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:75:13 + | +LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + +error: unknown clippy lint: clippy::unnecessary_lazy_eval + --> $DIR/unnecessary_lazy_eval.rs:2:9 + | +LL | #![warn(clippy::unnecessary_lazy_eval)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_lazy_evaluation` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 23 previous errors + -- cgit 1.4.1-3-g733a5 From 848af393103d769458a206547ea4506fd0229304 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 14:55:23 +0200 Subject: Add note to `or_fun_call`, list checked methods --- clippy_lints/src/methods/mod.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 61b7f2647ee..c672ca41dec 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1330,11 +1330,21 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Looks for unnecessary lazily evaluated closures on `Option` and `Result`. + /// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary + /// lazily evaluated closures on `Option` and `Result`. + /// + /// This lint suggests changing the following functions, when eager evaluation results in + /// simpler code: + /// - `unwrap_or_else` to `unwrap_or` + /// - `and_then` to `and` + /// - `or_else` to `or` + /// - `get_or_insert_with` to `get_or_insert` + /// - `ok_or_else` to `ok_or` /// /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. /// - /// **Known problems:** None. + /// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have + /// side effects. Eagerly evaluating them can change the semantics of the program. /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From a7cc5d40683b9351c35a627b05886f43fdec684f Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 15:26:02 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c672ca41dec..ed8eaba75d1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2753,6 +2753,16 @@ fn lint_lazy_eval<'a, 'tcx>( // Closures returning literals can be unconditionally simplified hir::ExprKind::Lit(_) => true, + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if !expr_uses_argument(object, params) { + // arguments are not used as index + !expr_uses_argument(index, params) + } else { + false + } + }, + // Reading fields can be simplified if the object is not an argument of the closure hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index fcfa6dfe12d..c806cf8dce4 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -3,15 +3,15 @@ #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] -struct Deep(Option); +struct Deep(Option); #[derive(Copy, Clone)] struct SomeStruct { - some_field: u32, + some_field: usize, } impl SomeStruct { - fn return_some_field(&self) -> u32 { + fn return_some_field(&self) -> usize { self.some_field } } @@ -22,6 +22,7 @@ fn some_call() -> T { fn main() { let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; // Should lint - Option @@ -30,19 +31,21 @@ fn main() { let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.unwrap_or(ext_arr[0]); let _ = opt.and(ext_opt); let _ = opt.or(ext_opt); let _ = opt.or(None); let _ = opt.get_or_insert(2); let _ = opt.ok_or(2); + let _ = opt.ok_or(ext_arr[0]); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or(2); let _ = Some(10).and(ext_opt); - let _: Option = None.or(ext_opt); + let _: Option = None.or(ext_opt); let _ = None.get_or_insert(2); - let _: Result = None.ok_or(2); - let _: Option = None.or(None); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); let mut deep = Deep(Some(42)); let _ = deep.0.unwrap_or(2); @@ -55,20 +58,22 @@ fn main() { let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); - let _: Result = opt.ok_or_else(|| some_call()); - let _: Result = opt.ok_or_else(some_call); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); // These are handled by bind_instead_of_map - let _: Option = None.or_else(|| Some(3)); + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); // Should lint - Result - let res: Result = Err(5); - let res2: Result = Err(SomeStruct { some_field: 5 }); + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); let _ = res2.unwrap_or(2); let _ = res2.unwrap_or(astronomers_pi); @@ -76,30 +81,31 @@ fn main() { // Should not lint - Result let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); let _ = res2.unwrap_or_else(|err| err.some_field); let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); - let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.or_else(|err| Err(err)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); // These are handled by bind_instead_of_map - let _: Result = res.and_then(|_| Ok(2)); - let _: Result = res.and_then(|_| Ok(astronomers_pi)); - let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 04b3c8ae1e2..dfc6d3ba573 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -3,15 +3,15 @@ #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] -struct Deep(Option); +struct Deep(Option); #[derive(Copy, Clone)] struct SomeStruct { - some_field: u32, + some_field: usize, } impl SomeStruct { - fn return_some_field(&self) -> u32 { + fn return_some_field(&self) -> usize { self.some_field } } @@ -22,6 +22,7 @@ fn some_call() -> T { fn main() { let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; // Should lint - Option @@ -30,19 +31,21 @@ fn main() { let _ = opt.unwrap_or_else(|| 2); let _ = opt.unwrap_or_else(|| astronomers_pi); let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.unwrap_or_else(|| ext_arr[0]); let _ = opt.and_then(|_| ext_opt); let _ = opt.or_else(|| ext_opt); let _ = opt.or_else(|| None); let _ = opt.get_or_insert_with(|| 2); let _ = opt.ok_or_else(|| 2); + let _ = opt.ok_or_else(|| ext_arr[0]); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or_else(|| 2); let _ = Some(10).and_then(|_| ext_opt); - let _: Option = None.or_else(|| ext_opt); + let _: Option = None.or_else(|| ext_opt); let _ = None.get_or_insert_with(|| 2); - let _: Result = None.ok_or_else(|| 2); - let _: Option = None.or_else(|| None); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); let mut deep = Deep(Some(42)); let _ = deep.0.unwrap_or_else(|| 2); @@ -55,20 +58,22 @@ fn main() { let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); - let _: Result = opt.ok_or_else(|| some_call()); - let _: Result = opt.ok_or_else(some_call); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); // These are handled by bind_instead_of_map - let _: Option = None.or_else(|| Some(3)); + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); // Should lint - Result - let res: Result = Err(5); - let res2: Result = Err(SomeStruct { some_field: 5 }); + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); let _ = res2.unwrap_or_else(|_| 2); let _ = res2.unwrap_or_else(|_| astronomers_pi); @@ -76,30 +81,31 @@ fn main() { // Should not lint - Result let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); let _ = res2.unwrap_or_else(|err| err.some_field); let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); - let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.or_else(|err| Err(err)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); // These are handled by bind_instead_of_map - let _: Result = res.and_then(|_| Ok(2)); - let _: Result = res.and_then(|_| Ok(astronomers_pi)); - let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index b941bf84246..15591817540 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:30:13 + --> $DIR/unnecessary_lazy_eval.rs:31:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,43 +7,49 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:31:13 + --> $DIR/unnecessary_lazy_eval.rs:32:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:32:13 + --> $DIR/unnecessary_lazy_eval.rs:33:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:33:13 + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:38:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` @@ -51,83 +57,89 @@ LL | let _ = opt.ok_or_else(|| 2); error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | +LL | let _ = opt.ok_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:26 + --> $DIR/unnecessary_lazy_eval.rs:45:28 | -LL | let _: Option = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:46:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:31 + --> $DIR/unnecessary_lazy_eval.rs:47:35 | -LL | let _: Result = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:26 + --> $DIR/unnecessary_lazy_eval.rs:48:28 | -LL | let _: Option = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:13 + --> $DIR/unnecessary_lazy_eval.rs:51:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:52:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:53:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:74:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:75:13 + --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` @@ -140,5 +152,5 @@ LL | #![warn(clippy::unnecessary_lazy_eval)] | = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From d7220dbd9143422a5b3a1ebf4dd46b708f83f1d3 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 15:22:22 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 54489751014..675dfd420a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1754,7 +1754,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold -[`unnecessary_lazy_eval`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_eval +[`unnecessary_lazy_evaluation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluation [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cb29f71387b..ae7045884f7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -672,7 +672,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, - &methods::UNNECESSARY_LAZY_EVALUATION, &methods::OR_FUN_CALL, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, @@ -686,6 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNNECESSARY_LAZY_EVALUATION, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -1542,6 +1542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), @@ -1612,7 +1613,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::OPTION_AS_REF_DEREF), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SUSPICIOUS_MAP), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1e76163f946..2a66d3495ed 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2384,7 +2384,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "unnecessary_lazy_eval", + name: "unnecessary_lazy_evaluation", group: "style", desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", deprecation: None, -- cgit 1.4.1-3-g733a5 From 75637c1edac6db37b3c8aa17ef6b5a91db699a00 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 20:01:18 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ed8eaba75d1..463ef48f62c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2744,12 +2744,8 @@ fn lint_lazy_eval<'a, 'tcx>( paths.iter().any(|candidate| match_qpath(path, candidate)) } - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; - - let simplify = match ex.kind { + fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { // Closures returning literals can be unconditionally simplified hir::ExprKind::Lit(_) => true, @@ -2767,15 +2763,16 @@ fn lint_lazy_eval<'a, 'tcx>( hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(ex, params), + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), // Calls to Some, Ok, Err can be considered literals if they don't derive an argument hir::ExprKind::Call(ref func, ref args) => if_chain! { - if allow_variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map if let hir::ExprKind::Path(ref path) = func.kind; if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); then { - !args.iter().any(|arg| expr_uses_argument(arg, params)) + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) } else { false } @@ -2784,9 +2781,15 @@ fn lint_lazy_eval<'a, 'tcx>( // For anything more complex than the above, a closure is probably the right solution, // or the case is handled by an other lint _ => false, - }; + } + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; - if simplify { + if can_simplify(ex, params, allow_variant_calls) { let msg = if is_option { "unnecessary closure used to substitute value for `Option::None`" } else { diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index c806cf8dce4..7f9d90a8569 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -25,9 +25,12 @@ fn main() { let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; - // Should lint - Option let mut opt = Some(42); let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); @@ -56,6 +59,9 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); let _: Result = opt.ok_or_else(|| some_call()); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index dfc6d3ba573..ca8238d6dcf 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -25,9 +25,12 @@ fn main() { let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; - // Should lint - Option let mut opt = Some(42); let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option let _ = opt.unwrap_or_else(|| 2); let _ = opt.unwrap_or_else(|| astronomers_pi); let _ = opt.unwrap_or_else(|| ext_str.some_field); @@ -56,6 +59,9 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); let _: Result = opt.ok_or_else(|| some_call()); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 15591817540..b8ec654e5c7 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:31:13 + --> $DIR/unnecessary_lazy_eval.rs:34:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,139 +7,139 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:32:13 + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:33:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:38:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:39:13 + --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| ext_arr[0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:46:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:13 + --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:28 + --> $DIR/unnecessary_lazy_eval.rs:48:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:49:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:47:35 + --> $DIR/unnecessary_lazy_eval.rs:50:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:28 + --> $DIR/unnecessary_lazy_eval.rs:51:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:78:13 + --> $DIR/unnecessary_lazy_eval.rs:84:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:79:13 + --> $DIR/unnecessary_lazy_eval.rs:85:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:80:13 + --> $DIR/unnecessary_lazy_eval.rs:86:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` -- cgit 1.4.1-3-g733a5 From a7083eea1c8821ff187218f55a1ac17b0f2c0fcb Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 26 Jul 2020 20:18:12 +0200 Subject: Removed the extra lifetime parameter --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 463ef48f62c..6693c358a02 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2710,8 +2710,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` -fn lint_lazy_eval<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, +fn lint_lazy_eval<'tcx>( + cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], allow_variant_calls: bool, -- cgit 1.4.1-3-g733a5 From 94cf90e5a56bbd9adbf6d6181d046ede9b96a7d8 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 26 Jul 2020 20:52:00 +0200 Subject: Apply suggested change Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6693c358a02..4578c228107 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2717,8 +2717,8 @@ fn lint_lazy_eval<'tcx>( allow_variant_calls: bool, simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(result_type)); if !is_option && !is_result { return; -- cgit 1.4.1-3-g733a5 From 9c41822d34c69dbfded4d9d8b4b8962564be80f1 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 26 Jul 2020 21:08:07 +0200 Subject: Apply suggested change Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4578c228107..70a6b1f5021 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2717,8 +2717,8 @@ fn lint_lazy_eval<'tcx>( allow_variant_calls: bool, simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.tables().expr_ty(&args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); if !is_option && !is_result { return; -- cgit 1.4.1-3-g733a5 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: Fix typo in description of unnecessary_mut_passed --- clippy_lints/src/mut_reference.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index be3ae7ab380..c506440ed79 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -9,8 +9,8 @@ declare_clippy_lint! { /// **What it does:** Detects passing a mutable reference to a function that only /// requires an immutable reference. /// - /// **Why is this bad?** The immutable reference rules out all other references - /// to the value. Also the code misleads about the intent of the call site. + /// **Why is this bad?** The mutable reference rules out all other references to + /// the value. Also the code misleads about the intent of the call site. /// /// **Known problems:** None. /// -- cgit 1.4.1-3-g733a5 From d71b418ac511ee604af9320e401a7ff3fd115482 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 20:47:50 +0200 Subject: Moved to submodule, don't trigger if map_unwrap_or does --- clippy_lints/src/methods/mod.rs | 136 +++------------------- clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70a6b1f5021..bfc89a74742 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; mod unnecessary_filter_map; +mod unnecessary_lazy_eval; use std::borrow::Cow; use std::fmt; @@ -1436,17 +1437,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { - lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"); - lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]); + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + } }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { - lint_lazy_eval(cx, expr, arg_lists[0], false, "and"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { - lint_lazy_eval(cx, expr, arg_lists[0], false, "or"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1490,9 +1492,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), - ["unwrap_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"), - ["get_or_insert_with", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "get_or_insert"), - ["ok_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "ok_or"), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -2708,119 +2710,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } -/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be -/// replaced with `(return value of simple closure)` -fn lint_lazy_eval<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - args: &'tcx [hir::Expr<'_>], - allow_variant_calls: bool, - simplify_using: &str, -) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); - - if !is_option && !is_result { - return; - } - - // Return true if the expression is an accessor of any of the arguments - fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { - params.iter().any(|arg| { - if_chain! { - if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; - if let [p, ..] = path.segments; - then { - ident.name == p.ident.name - } else { - false - } - } - }) - } - - fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { - paths.iter().any(|candidate| match_qpath(path, candidate)) - } - - fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { - match expr.kind { - // Closures returning literals can be unconditionally simplified - hir::ExprKind::Lit(_) => true, - - hir::ExprKind::Index(ref object, ref index) => { - // arguments are not being indexed into - if !expr_uses_argument(object, params) { - // arguments are not used as index - !expr_uses_argument(index, params) - } else { - false - } - }, - - // Reading fields can be simplified if the object is not an argument of the closure - hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), - - // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), - - // Calls to Some, Ok, Err can be considered literals if they don't derive an argument - hir::ExprKind::Call(ref func, ref args) => if_chain! { - if variant_calls; // Disable lint when rules conflict with bind_instead_of_map - if let hir::ExprKind::Path(ref path) = func.kind; - if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); - then { - // Recursively check all arguments - args.iter().all(|arg| can_simplify(arg, params, variant_calls)) - } else { - false - } - }, - - // For anything more complex than the above, a closure is probably the right solution, - // or the case is handled by an other lint - _ => false, - } - } - - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; - - if can_simplify(ex, params, allow_variant_calls) { - let msg = if is_option { - "unnecessary closure used to substitute value for `Option::None`" - } else { - "unnecessary closure used to substitute value for `Result::Err`" - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_LAZY_EVALUATION, - expr.span, - msg, - &format!("Use `{}` instead", simplify_using), - format!( - "{0}.{1}({2})", - snippet(cx, args[0].span, ".."), - simplify_using, - snippet(cx, ex.span, ".."), - ), - Applicability::MachineApplicable, - ); - } - } -} - /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], -) { +) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type)); @@ -2832,10 +2728,10 @@ fn lint_map_unwrap_or_else<'tcx>( let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { - return; + return false; } } else { - return; + return false; } // lint message @@ -2865,9 +2761,15 @@ fn lint_map_unwrap_or_else<'tcx>( map_snippet, unwrap_snippet, ), ); + true } else if same_span && multiline { span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - }; + true + } else { + false + } + } else { + false } } diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..0dbedc4919c --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -0,0 +1,113 @@ +use crate::utils::{match_qpath, span_lint_and_sugg, snippet, is_type_diagnostic_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::UNNECESSARY_LAZY_EVALUATION; + +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +pub(super) fn lint<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); + + if !is_option && !is_result { + return; + } + + // Return true if the expression is an accessor of any of the arguments + fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) + } + + fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) + } + + fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if !expr_uses_argument(object, params) { + // arguments are not used as index + !expr_uses_argument(index, params) + } else { + false + } + }, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + } + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + if can_simplify(ex, params, allow_variant_calls) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATION, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } +} -- cgit 1.4.1-3-g733a5 From 8a14c115369f163e7f96544df48b26376a06a3fb Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 20:50:30 +0200 Subject: Cleanup, explain return value --- clippy_lints/src/methods/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bfc89a74742..c2b2cb98012 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2711,6 +2711,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -2761,16 +2762,14 @@ fn lint_map_unwrap_or_else<'tcx>( map_snippet, unwrap_snippet, ), ); - true + return true; } else if same_span && multiline { span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - true - } else { - false + return true; } - } else { - false } + + false } /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s -- cgit 1.4.1-3-g733a5 From 3b52d7f780f2023d6596fe73c0a71f663b26bc33 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 20:51:16 +0200 Subject: run cargo dev fmt --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 0dbedc4919c..b44089f7bfc 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, span_lint_and_sugg, snippet, is_type_diagnostic_item}; +use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -- cgit 1.4.1-3-g733a5 From b7ee8685ac83e0f3f32ac5ab5d597d5451d07057 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 21:04:02 +0200 Subject: Fix dogfooding test errors --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 172 +++++++++++----------- 1 file changed, 85 insertions(+), 87 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index b44089f7bfc..a3e7e9971f8 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -6,6 +6,66 @@ use rustc_lint::LateContext; use super::UNNECESSARY_LAZY_EVALUATION; +// Return true if the expression is an accessor of any of the arguments +fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) +} + +fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) +} + +fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if expr_uses_argument(object, params) { + false + } else { + // arguments are not used as index + !expr_uses_argument(index, params) + } + }, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + } +} + /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` pub(super) fn lint<'tcx>( @@ -18,96 +78,34 @@ pub(super) fn lint<'tcx>( let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); - if !is_option && !is_result { - return; - } - - // 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 - } - } - }) - } + 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; - 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) + if can_simplify(ex, params, allow_variant_calls) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" } 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), + "unnecessary closure used to substitute value for `Result::Err`" + }; - // 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, - ); + 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, + ); + } } } } -- cgit 1.4.1-3-g733a5 From fc1e07e0c1803edb3ade2db2f46034cf227642c9 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 22:16:39 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 675dfd420a7..f662de122f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1754,7 +1754,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold -[`unnecessary_lazy_evaluation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluation +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ae7045884f7..17501e8e6da 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,7 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, - &methods::UNNECESSARY_LAZY_EVALUATION, + &methods::UNNECESSARY_LAZY_EVALUATIONS, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -1361,7 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1542,7 +1542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c2b2cb98012..0f50a4c813a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1361,7 +1361,7 @@ declare_clippy_lint! { /// /// opt.unwrap_or(42); /// ``` - pub UNNECESSARY_LAZY_EVALUATION, + pub UNNECESSARY_LAZY_EVALUATIONS, style, "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } @@ -1415,7 +1415,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, - UNNECESSARY_LAZY_EVALUATION, + UNNECESSARY_LAZY_EVALUATIONS, ]); impl<'tcx> LateLintPass<'tcx> for Methods { diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a3e7e9971f8..31517659c34 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use super::UNNECESSARY_LAZY_EVALUATION; +use super::UNNECESSARY_LAZY_EVALUATIONS; // Return true if the expression is an accessor of any of the arguments fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { @@ -93,7 +93,7 @@ pub(super) fn lint<'tcx>( span_lint_and_sugg( cx, - UNNECESSARY_LAZY_EVALUATION, + UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, &format!("Use `{}` instead", simplify_using), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 2a66d3495ed..3229c8da507 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2384,7 +2384,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "unnecessary_lazy_evaluation", + name: "unnecessary_lazy_evaluations", group: "style", desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", deprecation: None, diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 7f9d90a8569..fa66e68794e 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_evaluation)] +#![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index fd8f8ed0329..04f47d1aa29 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_evaluation)] +#![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index e86b7ed6253..5c1b2eb1f14 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -4,7 +4,7 @@ error: unnecessary closure used to substitute value for `Option::None` LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` | - = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 -- cgit 1.4.1-3-g733a5 From 6afa4ef60f973218c901d0f802d586fe6c43017d Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index dab1e96e282..b3185b88840 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,5 +1,5 @@ use crate::utils::{ - get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, }; use crate::utils::{higher, sugg}; use if_chain::if_chain; @@ -70,11 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { return; } // lhs op= l op r - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) { + if eq_expr_value(cx, lhs, l) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r); } // lhs op= l commutative_op r - if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) { + if is_commutative(op.node) && eq_expr_value(cx, lhs, r) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l); } } @@ -161,14 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { if visitor.counter == 1 { // a = a op b - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) { + if eq_expr_value(cx, assignee, l) { lint(assignee, r); } // a = b commutative_op a // Limited to primitive type as these ops are know to be commutative - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) - && cx.typeck_results().expr_ty(assignee).is_primitive_ty() - { + if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() { match op.node { hir::BinOpKind::Add | hir::BinOpKind::Mul @@ -253,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) { + if eq_expr_value(self.cx, self.assignee, expr) { self.counter += 1; } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 18529f2113e..280a2c7fe67 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,6 +1,6 @@ use crate::utils::{ - get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg, - span_lint_and_then, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, + span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -128,7 +128,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } } for (n, expr) in self.terminals.iter().enumerate() { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) { + if eq_expr_value(self.cx, e, expr) { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -138,8 +138,8 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if implements_ord(self.cx, e_lhs); if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; if negate(e_binop.node) == Some(expr_binop.node); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs); + if eq_expr_value(self.cx, e_lhs, expr_lhs); + if eq_expr_value(self.cx, e_rhs, expr_rhs); then { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1f8bff8d71e..10a64769585 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ +use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use crate::utils::{SpanlessEq, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -197,8 +197,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { h.finish() }; - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) }; + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) }; for (i, j) in search_same(conds, hash, eq) { span_lint_and_note( @@ -222,7 +221,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not spawn warning if `IFS_SAME_COND` already produced it. - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) { + if eq_expr_value(cx, lhs, rhs) { return false; } SpanlessEq::new(cx).eq_expr(lhs, rhs) diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index bae7c4647d4..19f56195ec1 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. @@ -46,8 +46,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - let mut spanless_eq = SpanlessEq::new(cx).ignore_fn(); - if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) { + if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { return; } macro_rules! lint_double_comparison { diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 140cd21c34e..e16ec783fab 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq, + eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) { + if is_valid_operator(op) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 93f6ec92ec7..1b02cee126d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,7 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -363,8 +363,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; - if are_exprs_equal(cx, lmul_lhs, lmul_rhs); - if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + if eq_expr_value(cx, lmul_lhs, lmul_rhs); + if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); } @@ -502,8 +502,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -515,8 +515,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -524,10 +524,6 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } } -fn are_exprs_equal(cx: &LateContext<'_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool { - SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2) -} - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match constant_simple(cx, cx.typeck_results(), expr) { @@ -546,12 +542,12 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// returns None. fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind { - if are_exprs_equal(cx, expr1_negated, expr2) { + if eq_expr_value(cx, expr1_negated, expr2) { return Some((false, expr2)); } } if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind { - if are_exprs_equal(cx, expr1, expr2_negated) { + if eq_expr_value(cx, expr1, expr2_negated) { return Some((true, expr1)); } } @@ -614,7 +610,7 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_> args_a.len() == args_b.len() && ( ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || - method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1]) ); } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index fb12c565afd..dbc676ae224 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -7,8 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::sugg::Sugg; use crate::utils::{ - higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, - span_lint_and_sugg, SpanlessEq, + eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, + span_lint_and_sugg, }; declare_clippy_lint! { @@ -65,7 +65,7 @@ impl QuestionMark { if let ExprKind::Block(block, None) = &else_.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; - if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr); + if eq_expr_value(cx, subject, block_expr); then { replacement = Some(format!("Some({}?)", receiver_str)); } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 754f87e6b55..cc39f060fc7 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty, - SpanlessEq, + differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, + walk_ptrs_ty, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -92,8 +92,8 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if rhs2.segments.len() == 1; if ident.as_str() == rhs2.segments[0].ident.as_str(); - if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2); + if eq_expr_value(cx, tmp_init, lhs1); + if eq_expr_value(cx, rhs1, lhs2); then { if let ExprKind::Field(ref lhs1, _) = lhs1.kind { if let ExprKind::Field(ref lhs2, _) = lhs2.kind { @@ -193,7 +193,7 @@ enum Slice<'a> { fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) { + if eq_expr_value(cx, lhs1, lhs2) { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); if matches!(ty.kind, ty::Slice(_)) @@ -221,8 +221,8 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { if !differing_macro_contexts(first.span, second.span); if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0); + if eq_expr_value(cx, lhs0, rhs1); + if eq_expr_value(cx, lhs1, rhs0); then { let lhs0 = Sugg::hir_opt(cx, lhs0); let rhs0 = Sugg::hir_opt(cx, rhs0); diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 28fb6ed12a0..785c409260e 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -340,6 +340,11 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Checks if two expressions evaluate to the same value, and don't contain any side effects. +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { + SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) +} + /// Type used to hash an ast element. This is different from the `Hash` trait /// on ast types as this /// trait would consider IDs and spans. diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 6c235679914..0b8d0bd9e11 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,6 @@ -use crate::utils::SpanlessEq; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, + span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 223628cc610..530552f7940 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -21,7 +21,7 @@ pub mod sugg; pub mod usage; pub use self::attrs::*; pub use self::diagnostics::*; -pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::mem; -- cgit 1.4.1-3-g733a5 From 9b800b1e929d6023e1813b2b189336a4bddcffd7 Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 785c409260e..1014546ff89 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -23,9 +23,7 @@ pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, - /// If is true, never consider as equal expressions containing function - /// calls. - ignore_fn: bool, + allow_side_effects: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -33,13 +31,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - ignore_fn: false, + allow_side_effects: true, } } - pub fn ignore_fn(self) -> Self { + /// Consider expressions containing potential side effects as not equal. + pub fn deny_side_effects(self) -> Self { Self { - ignore_fn: true, + allow_side_effects: false, ..self } } @@ -67,7 +66,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { #[allow(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if self.ignore_fn && differing_macro_contexts(left.span, right.span) { + if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } @@ -108,7 +107,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { - !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { @@ -134,7 +133,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { - !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); @@ -342,7 +341,7 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - /// Checks if two expressions evaluate to the same value, and don't contain any side effects. pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { - SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) + SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) } /// Type used to hash an ast element. This is different from the `Hash` trait diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0b8d0bd9e11..8fa5d22210a 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -492,7 +492,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if let StmtKind::Semi(only_expr) = &stmts[0].kind; if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).ignore_fn(); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); then { match &*ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { -- cgit 1.4.1-3-g733a5 From d1dbf7913ad83954337e94b094f22f1d9d8b83f3 Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: Expresions with Assign / AssignOp have side effects --- clippy_lints/src/utils/hir_utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 1014546ff89..cacc9f8d6f2 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -89,10 +89,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { - self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { - lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { -- cgit 1.4.1-3-g733a5 From 4f4abf4e0640edbb1614f3dcb8ff62e8afc54801 Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index f662de122f9..5ce63c0a157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1690,6 +1690,7 @@ Released 2018-09-13 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 17501e8e6da..87c297e72eb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -284,6 +284,7 @@ mod reference; mod regex; mod repeat_once; mod returns; +mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; @@ -773,6 +774,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &repeat_once::REPEAT_ONCE, &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, + &self_assignment::SELF_ASSIGNMENT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1090,6 +1092,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1421,6 +1424,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1714,6 +1718,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs new file mode 100644 index 00000000000..e096c9aebc1 --- /dev/null +++ b/clippy_lints/src/self_assignment.rs @@ -0,0 +1,51 @@ +use crate::utils::{eq_expr_value, snippet, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit self-assignments. + /// + /// **Why is this bad?** Self-assignments are redundant and unlikely to be + /// intentional. + /// + /// **Known problems:** If expression contains any deref coercions or + /// indexing operations they are assumed not to have any side effects. + /// + /// **Example:** + /// + /// ```rust + /// struct Event { + /// id: usize, + /// x: i32, + /// y: i32, + /// } + /// + /// fn copy_position(a: &mut Event, b: &Event) { + /// a.x = b.x; + /// a.y = a.y; + /// } + /// ``` + pub SELF_ASSIGNMENT, + correctness, + "explicit self-assignment" +} + +declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]); + +impl<'tcx> LateLintPass<'tcx> for SelfAssignment { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind { + if eq_expr_value(cx, lhs, rhs) { + let lhs = snippet(cx, lhs.span, ""); + let rhs = snippet(cx, rhs.span, ""); + span_lint( + cx, + SELF_ASSIGNMENT, + expr.span, + &format!("self-assignment of `{}` to `{}`", rhs, lhs), + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3229c8da507..bf58c117aaa 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1956,6 +1956,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "self_assignment", + group: "correctness", + desc: "explicit self-assignment", + deprecation: None, + module: "self_assignment", + }, Lint { name: "serde_api_misuse", group: "correctness", diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs new file mode 100644 index 00000000000..a7cbb9cd78b --- /dev/null +++ b/tests/ui/self_assignment.rs @@ -0,0 +1,67 @@ +#![warn(clippy::self_assignment)] + +pub struct S<'a> { + a: i32, + b: [i32; 10], + c: Vec>, + e: &'a mut i32, + f: &'a mut i32, +} + +pub fn positives(mut a: usize, b: &mut u32, mut s: S) { + a = a; + *b = *b; + s = s; + s.a = s.a; + s.b[10] = s.b[5 + 5]; + s.c[0][1] = s.c[0][1]; + s.b[a] = s.b[a]; + *s.e = *s.e; + s.b[a + 10] = s.b[10 + a]; + + let mut t = (0, 1); + t.1 = t.1; + t.0 = (t.0); +} + +pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { + dbg!(&a); + a = *b; + dbg!(&a); + s.b[1] += s.b[1]; + s.b[1] = s.b[2]; + s.c[1][0] = s.c[0][1]; + s.b[a] = s.b[*b]; + s.b[a + 10] = s.b[a + 11]; + *s.e = *s.f; + + let mut t = (0, 1); + t.0 = t.1; +} + +#[allow(clippy::eval_order_dependence)] +pub fn negatives_side_effects() { + let mut v = vec![1, 2, 3, 4, 5]; + let mut i = 0; + v[{ + i += 1; + i + }] = v[{ + i += 1; + i + }]; + + fn next(n: &mut usize) -> usize { + let v = *n; + *n += 1; + v + } + + let mut w = vec![1, 2, 3, 4, 5]; + let mut i = 0; + let i = &mut i; + w[next(i)] = w[next(i)]; + w[next(i)] = w[next(i)]; +} + +fn main() {} diff --git a/tests/ui/self_assignment.stderr b/tests/ui/self_assignment.stderr new file mode 100644 index 00000000000..826e0d0ba88 --- /dev/null +++ b/tests/ui/self_assignment.stderr @@ -0,0 +1,70 @@ +error: self-assignment of `a` to `a` + --> $DIR/self_assignment.rs:12:5 + | +LL | a = a; + | ^^^^^ + | + = note: `-D clippy::self-assignment` implied by `-D warnings` + +error: self-assignment of `*b` to `*b` + --> $DIR/self_assignment.rs:13:5 + | +LL | *b = *b; + | ^^^^^^^ + +error: self-assignment of `s` to `s` + --> $DIR/self_assignment.rs:14:5 + | +LL | s = s; + | ^^^^^ + +error: self-assignment of `s.a` to `s.a` + --> $DIR/self_assignment.rs:15:5 + | +LL | s.a = s.a; + | ^^^^^^^^^ + +error: self-assignment of `s.b[5 + 5]` to `s.b[10]` + --> $DIR/self_assignment.rs:16:5 + | +LL | s.b[10] = s.b[5 + 5]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.c[0][1]` to `s.c[0][1]` + --> $DIR/self_assignment.rs:17:5 + | +LL | s.c[0][1] = s.c[0][1]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.b[a]` to `s.b[a]` + --> $DIR/self_assignment.rs:18:5 + | +LL | s.b[a] = s.b[a]; + | ^^^^^^^^^^^^^^^ + +error: self-assignment of `*s.e` to `*s.e` + --> $DIR/self_assignment.rs:19:5 + | +LL | *s.e = *s.e; + | ^^^^^^^^^^^ + +error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]` + --> $DIR/self_assignment.rs:20:5 + | +LL | s.b[a + 10] = s.b[10 + a]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `t.1` to `t.1` + --> $DIR/self_assignment.rs:23:5 + | +LL | t.1 = t.1; + | ^^^^^^^^^ + +error: self-assignment of `(t.0)` to `t.0` + --> $DIR/self_assignment.rs:24:5 + | +LL | t.0 = (t.0); + | ^^^^^^^^^^^ + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From 99ba290a14c6654164cea8339b827d8da0ff600d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 17 Aug 2020 08:36:02 +0900 Subject: Improve code style --- clippy_lints/src/loops.rs | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f7db2563d2b..df06031d999 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1145,29 +1145,24 @@ fn detect_same_item_push<'tcx>( let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id) { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); let node = cx.tcx.hir().get(hir_id); - if_chain! { - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - then { - match bind_ann { - BindingAnnotation::RefMut | BindingAnnotation::Mutable => {}, - _ => { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } - } - } + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) } } } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { -- cgit 1.4.1-3-g733a5 From df4d42fc2debf7a7d43226c79480c71037be42b8 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 18 Aug 2020 01:00:11 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 28fd55f6ff0..1d238b242b0 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, + in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, span_lint_and_then, sugg, }; use if_chain::if_chain; @@ -331,6 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { + // Avoid suggesting f32::(from|to)_bits in const contexts. + // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + let const_context = in_constant(cx, e.hir_id); + let from_ty = cx.typeck_results().expr_ty(&args[0]); let to_ty = cx.typeck_results().expr_ty(e); @@ -544,7 +548,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, ) }, - (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then( + (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_INT_TO_FLOAT, e.span, @@ -567,7 +571,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); }, ), - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then( + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_FLOAT_TO_INT, e.span, diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index bb853d23704..7caad34edb3 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,3 +1,4 @@ +#![feature(const_fn_transmute)] #![allow(dead_code)] extern crate core; @@ -81,9 +82,17 @@ fn int_to_bool() { } #[warn(clippy::transmute_int_to_float)] -fn int_to_float() { - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - let _: f32 = unsafe { std::mem::transmute(0_i32) }; +mod int_to_float { + fn test() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; + } + + // See issue #5747 + const VALUE: f32 = unsafe { std::mem::transmute(0_u32) }; + const fn from_bits(v: u32) -> f32 { + unsafe { std::mem::transmute(v) } + } } fn bytes_to_str(b: &[u8], mb: &mut [u8]) { diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 8582080498f..d817c08b52f 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 + --> $DIR/transmute.rs:20:20 | LL | let _: &'a T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,67 +7,67 @@ LL | let _: &'a T = core::intrinsics::transmute(t); = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:63:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +75,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:65:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:67:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:69:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:75:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +101,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:76:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:81:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,21 +115,21 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:85:27 + --> $DIR/transmute.rs:87:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` | = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:86:27 + --> $DIR/transmute.rs:88:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:90:28 + --> $DIR/transmute.rs:99:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,7 +137,7 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:91:32 + --> $DIR/transmute.rs:100:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index ce942751ada..8173944d959 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -1,4 +1,5 @@ -#[warn(clippy::transmute_float_to_int)] +#![feature(const_fn_transmute)] +#![warn(clippy::transmute_float_to_int)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -9,4 +10,12 @@ fn float_to_int() { let _: u64 = unsafe { std::mem::transmute(-1.0) }; } +mod issue_5747 { + const VALUE: u32 = unsafe { std::mem::transmute(1f32) }; + + const fn to_bits(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } +} + fn main() {} diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr index eb786bb39f9..5a40cf381d6 100644 --- a/tests/ui/transmute_float_to_int.stderr +++ b/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> $DIR/transmute_float_to_int.rs:4:27 + --> $DIR/transmute_float_to_int.rs:5:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -7,31 +7,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` error: transmute from a `f32` to a `i32` - --> $DIR/transmute_float_to_int.rs:5:27 + --> $DIR/transmute_float_to_int.rs:6:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:6:27 + --> $DIR/transmute_float_to_int.rs:7:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> $DIR/transmute_float_to_int.rs:7:27 + --> $DIR/transmute_float_to_int.rs:8:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:8:27 + --> $DIR/transmute_float_to_int.rs:9:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:9:27 + --> $DIR/transmute_float_to_int.rs:10:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` -- cgit 1.4.1-3-g733a5 From 6a12bae1941bdef7c2716ffefed5594e5d7e9f8e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 18 Aug 2020 22:19:30 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 1d238b242b0..50d9c93f9d4 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting f32::(from|to)_bits in const contexts. + // Avoid suggesting from/to bits in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. let const_context = in_constant(cx, e.hir_id); diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 7caad34edb3..9f1948359e7 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -86,12 +86,21 @@ mod int_to_float { fn test() { let _: f32 = unsafe { std::mem::transmute(0_u32) }; let _: f32 = unsafe { std::mem::transmute(0_i32) }; + let _: f64 = unsafe { std::mem::transmute(0_u64) }; + let _: f64 = unsafe { std::mem::transmute(0_i64) }; } - // See issue #5747 - const VALUE: f32 = unsafe { std::mem::transmute(0_u32) }; - const fn from_bits(v: u32) -> f32 { - unsafe { std::mem::transmute(v) } + mod issue_5747 { + const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + + const fn from_bits_32(v: i32) -> f32 { + unsafe { std::mem::transmute(v) } + } + + const fn from_bits_64(v: u64) -> f64 { + unsafe { std::mem::transmute(v) } + } } } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index d817c08b52f..ad9953d12bc 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -128,8 +128,20 @@ error: transmute from a `i32` to a `f32` LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +error: transmute from a `u64` to a `f64` + --> $DIR/transmute.rs:89:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` + +error: transmute from a `i64` to a `f64` + --> $DIR/transmute.rs:90:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` + error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:99:28 + --> $DIR/transmute.rs:108:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,10 +149,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:100:32 + --> $DIR/transmute.rs:109:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index 8173944d959..1040fee4b34 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -11,9 +11,14 @@ fn float_to_int() { } mod issue_5747 { - const VALUE: u32 = unsafe { std::mem::transmute(1f32) }; + const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - const fn to_bits(v: f32) -> u32 { + const fn to_bits_32(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } + + const fn to_bits_64(v: f64) -> i64 { unsafe { std::mem::transmute(v) } } } -- cgit 1.4.1-3-g733a5 From 902b28275ef6e06826f269bdf2459c4ba4562145 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 19 Aug 2020 22:31:34 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 11bdd27d9b1..4b6a0a6a0c9 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { /// ``` pub TO_STRING_IN_DISPLAY, correctness, - "to_string method used while implementing Display trait" + "`to_string` method used while implementing `Display` trait" } #[derive(Default)] @@ -80,7 +80,7 @@ impl LateLintPass<'_> for ToStringInDisplay { cx, TO_STRING_IN_DISPLAY, expr.span, - "Using to_string in fmt::Display implementation might lead to infinite recursion", + "using `to_string` in `fmt::Display` implementation might lead to infinite recursion", ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bf58c117aaa..c50c6b900ae 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2183,7 +2183,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "to_string_in_display", group: "correctness", - desc: "to_string method used while implementing Display trait", + desc: "`to_string` method used while implementing `Display` trait", deprecation: None, module: "to_string_in_display", }, diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr index cbc0a41036b..5f26ef413e2 100644 --- a/tests/ui/to_string_in_display.stderr +++ b/tests/ui/to_string_in_display.stderr @@ -1,4 +1,4 @@ -error: Using to_string in fmt::Display implementation might lead to infinite recursion +error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion --> $DIR/to_string_in_display.rs:25:25 | LL | write!(f, "{}", self.to_string()) -- cgit 1.4.1-3-g733a5 From c236c0fb5694948353b5fcbe46010cf04d5a7107 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 20 Aug 2020 06:34:48 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 4797771e7bd..c9d18c3cb72 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,4 +1,5 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -102,36 +103,36 @@ impl EarlyLintPass for Precedence { } } - if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind { - if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind { + if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind { + let mut arg = operand; + + let mut all_odd = true; + while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind { let path_segment_str = path_segment.ident.name.as_str(); - if let Some(slf) = args.first() { - if let ExprKind::Lit(ref lit) = slf.kind { - match lit.kind { - LitKind::Int(..) | LitKind::Float(..) => { - if ALLOWED_ODD_FUNCTIONS - .iter() - .any(|odd_function| **odd_function == *path_segment_str) - { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, rhs.span, "..", &mut applicability) - ), - applicability, - ); - }, - _ => (), - } - } + all_odd &= ALLOWED_ODD_FUNCTIONS + .iter() + .any(|odd_function| **odd_function == *path_segment_str); + arg = args.first().expect("A method always has a receiver."); + } + + if_chain! { + if !all_odd; + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 4d284ae1319..163bd044c17 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -(1.0_f64.cos().cos()); + let _ = -(1.0_f64.cos().sin()); + let _ = -(1.0_f64.sin().cos()); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d08e82f349..8c849e3209b 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -1.0_f64.cos().cos(); + let _ = -1.0_f64.cos().sin(); + let _ = -1.0_f64.sin().cos(); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.stderr b/tests/ui/precedence.stderr index a2ed5392bfc..03d585b3975 100644 --- a/tests/ui/precedence.stderr +++ b/tests/ui/precedence.stderr @@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call LL | -1f32.abs(); | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` -error: aborting due to 9 previous errors +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:52:13 + | +LL | let _ = -1.0_f64.cos().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:53:13 + | +LL | let _ = -1.0_f64.cos().sin(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:54:13 + | +LL | let _ = -1.0_f64.sin().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 2ecc2ac864739cff6aed2609021e2467dedb117a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 21 Aug 2020 00:07:56 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7e9190bef5e..3f5b3a5bcd5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -11,8 +11,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, - TraitItem, TraitItemKind, TyKind, UnOp, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -844,43 +844,54 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp Applicability::MaybeIncorrect, ); or = "or "; + applicability = Applicability::MaybeIncorrect; }); - let sugg = args_to_recover + + let arg_snippets: Vec = args_to_recover + .iter() + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + let arg_snippets_without_empty_blocks: Vec = args_to_recover .iter() .filter(|arg| !is_empty_block(arg)) - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) - ) - }) - .collect::>(); - let mut and = ""; - if !sugg.is_empty() { - let plural = if sugg.len() > 1 { "s" } else { "" }; - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg.join("\n")), - applicability, - ); - and = "...and " + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + if let Some(mut sugg) = snippet_opt(cx, expr.span) { + arg_snippets.iter().for_each(|arg| { + sugg = sugg.replacen(arg, "()", 1); + }); + sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + // expr is not in a block statement or result expression position, wrap in a block + sugg = format!("{{ {} }}", sugg); + } + + if arg_snippets_without_empty_blocks.is_empty() { + db.multipart_suggestion( + &format!("use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + } else { + let plural = arg_snippets_without_empty_blocks.len() > 1; + let empty_or_s = if plural { "s" } else { "" }; + let it_or_them = if plural { "them" } else { "it" }; + db.span_suggestion( + expr.span, + &format!( + "{}move the expression{} in front of the call and replace {} with the unit literal `()`", + or, empty_or_s, it_or_them + ), + sugg, + applicability, + ); + } } - db.multipart_suggestion( - &format!("{}use {}unit literal{} instead", and, singular, plural), - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); }, ); } @@ -2055,6 +2066,7 @@ impl PartialOrd for FullInt { }) } } + impl Ord for FullInt { #[must_use] fn cmp(&self, other: &Self) -> Ordering { diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2992abae775..2e2bd054e42 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,5 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] use std::fmt::Debug; @@ -47,6 +47,11 @@ fn bad() { foo(3); }, ); + // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block + None.or(Some(foo(2))); + // in this case, the suggestion can be inlined, no need for a surrounding block + // foo(()); foo(()) instead of { foo(()); foo(()) } + foo(foo(())) } fn ok() { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 56f6a855dfa..2a0cc1f18e2 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -11,16 +11,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; foo(()); | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:26:5 @@ -28,14 +24,10 @@ error: passing a unit value to a function LL | foo(foo(1)); | ^^^^^^^^^^^ | -help: move the expression in front of the call... - | -LL | foo(1); +help: move the expression in front of the call and replace it with the unit literal `()` | -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 | }; - | -help: ...and use a unit literal instead +LL | }; foo(()); | -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... - | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead +help: move the expressions in front of the call and replace them with the unit literal `()` | -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 | }; - | -help: ...and use unit literals instead +LL | }; taking_multiple_units((), ()); | -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:82:5 + --> $DIR/unit_arg.rs:51:13 | -LL | Some(foo(1)) +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... +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:87:5 + | +LL | Some(foo(1)) + | ^^^^^^^^^^^^ | -LL | foo(1); +help: move the expression in front of the call and replace it with the unit literal `()` | -help: ...and use a unit literal instead +LL | foo(1); Some(()) | -LL | Some(()) - | ^^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index bb58483584b..4cbbc8b8cd4 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -22,14 +22,10 @@ error: passing unit values to a function LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); - | -help: ...and use unit literals instead - | -LL | taking_two_units((), ()); - | ^^ ^^ +LL | foo(0); taking_two_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -37,15 +33,10 @@ error: passing unit values to a function LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... - | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | taking_three_units((), (), ()); - | ^^ ^^ ^^ +LL | foo(0); foo(1); taking_three_units((), (), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 11efd75aeb8637bd5444104a57f1051b86c0d62b Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 21 Aug 2020 07:23:04 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2498c48f067..22942d9fb0c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3421,7 +3421,12 @@ fn lint_option_as_ref_deref<'tcx>( ]; let is_deref = match map_args[1].kind { - hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)), + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_args[1].hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }), hir::ExprKind::Closure(_, _, body_id, _, _) => { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 076692e6445..07d7f0b45b0 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -38,4 +38,7 @@ fn main() { let _ = opt.as_deref(); let _ = opt.as_deref_mut(); + + // Issue #5927 + let _ = opt.as_deref(); } diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 3bf5f715f83..6ae059c9425 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -41,4 +41,7 @@ fn main() { let _ = opt.as_ref().map(|x| &**x); let _ = opt.as_mut().map(|x| &mut **x); + + // Issue #5927 + let _ = opt.as_ref().map(std::ops::Deref::deref); } diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index a106582a633..62f28232475 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -100,5 +100,11 @@ error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` -error: aborting due to 16 previous errors +error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:46:13 + | +LL | let _ = opt.as_ref().map(std::ops::Deref::deref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 146e352db4eade947f6f358cd38c308e95783d7b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 22 Aug 2020 00:59:42 +0200 Subject: run cargo dev fmt --- clippy_lints/src/unnested_or_patterns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 0dca6a5da0c..7f4f16f8faf 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -340,7 +340,7 @@ fn take_pat(from: &mut Pat) -> Pat { id: DUMMY_NODE_ID, kind: Wild, span: DUMMY_SP, - tokens: None + tokens: None, }; mem::replace(from, dummy) } -- cgit 1.4.1-3-g733a5 From 191b6c798fa40334ec8898d07b4d28fd76743120 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 22 Aug 2020 10:35:18 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 7948d99162b..92f42168a1e 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -2,6 +2,7 @@ use std::cmp; use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -155,8 +156,12 @@ impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { return; } for a in attrs { - if a.meta_item_list().is_some() && a.has_name(sym!(proc_macro_derive)) { - return; + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } } } }, diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index 316426f1cf1..e7e0a31febc 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -97,6 +97,24 @@ mod issue3992 { pub fn c(d: &u16) {} } +mod issue5876 { + // Don't lint here as it is always inlined + #[inline(always)] + fn foo_always(x: &i32) { + println!("{}", x); + } + + #[inline(never)] + fn foo_never(x: &i32) { + println!("{}", x); + } + + #[inline] + fn foo(x: &i32) { + println!("{}", x); + } +} + fn main() { let (mut foo, bar) = (Foo(0), Bar([0; 24])); let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index be0914e4a79..ccc3cdb2b74 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -94,5 +94,17 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method2(&self, _color: &Color); | ^^^^^^ help: consider passing by value instead: `Color` -error: aborting due to 15 previous errors +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:108:21 + | +LL | fn foo_never(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:113:15 + | +LL | fn foo(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 5b07b9ed61d1979320e9e63219b5d0d6f4b350f7 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Thu, 20 Aug 2020 22:49:39 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7eb7c2e980..717741129a8 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -195,13 +195,10 @@ impl WildcardImports { } } -// Allow "...prelude::*" imports. +// Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments - .iter() - .last() - .map_or(false, |ps| ps.ident.as_str() == "prelude") + segments.iter().filter(|ps| ps.ident.as_str() == "prelude").count() > 0 } // Allow "super::*" imports in tests. diff --git a/tests/ui/auxiliary/wildcard_imports_helper.rs b/tests/ui/auxiliary/wildcard_imports_helper.rs index 414477aedd7..d75cdd625f9 100644 --- a/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -19,3 +19,9 @@ mod extern_exports { A, } } + +pub mod prelude { + pub mod v1 { + pub struct PreludeModAnywhere; + } +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 3ad1a29aeba..1f261159f4a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); -- cgit 1.4.1-3-g733a5 From e615a26ae4fe293cda83961470907e78e4b2ee75 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Fri, 21 Aug 2020 14:03:42 +0200 Subject: Use more elegant way to check for prelude string --- clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 717741129a8..5683a71efea 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -198,7 +198,7 @@ impl WildcardImports { // Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments.iter().filter(|ps| ps.ident.as_str() == "prelude").count() > 0 + segments.iter().any(|ps| ps.ident.as_str() == "prelude") } // Allow "super::*" imports in tests. -- cgit 1.4.1-3-g733a5 From e8d33d73dc3f9d0daf9b3affe65a2431f5a3e13a Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 23 Aug 2020 07:50:59 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c5541e64b4..a6e4252a0c8 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -99,7 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for Return { |err| { err.span_label(local.span, "unnecessary `let` binding"); - if let Some(snippet) = snippet_opt(cx, initexpr.span) { + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + snippet.push_str(" as _"); + } err.multipart_suggestion( "return the expression directly", vec![ diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 09614b8c1ad..73e550b3df8 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -135,4 +135,25 @@ mod no_lint_if_stmt_borrows { } } +mod issue_5729 { + use std::sync::Arc; + + trait Foo {} + + trait FooStorage { + fn foo_cloned(&self) -> Arc; + } + + struct FooStorageImpl { + foo: Arc, + } + + impl FooStorage for FooStorageImpl { + fn foo_cloned(&self) -> Arc { + let clone = Arc::clone(&self.foo); + clone + } + } +} + fn main() {} diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index eacf948b392..fe878e5f206 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -27,5 +27,19 @@ LL | LL | 5 | -error: aborting due to 2 previous errors +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:154:13 + | +LL | let clone = Arc::clone(&self.foo); + | ---------------------------------- unnecessary `let` binding +LL | clone + | ^^^^^ + | +help: return the expression directly + | +LL | +LL | Arc::clone(&self.foo) as _ + | + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 8776db9f6d5163ca40232336dc51ad77eeb2b5e5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 23 Aug 2020 12:05:34 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 77c206002ea..c0890018d46 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -39,12 +39,12 @@ declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); impl<'tcx> LateLintPass<'tcx> for RepeatOnce { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind; if path.ident.name == sym!(repeat); - if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&args[1]); - if !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); + if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); if ty.is_str() { span_lint_and_sugg( cx, @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on str", "consider using `.to_string()` instead", - format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if ty.builtin_index().is_some() { @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on slice", "consider using `.to_vec()` instead", - format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on a string literal", "consider using `.clone()` instead", - format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs new file mode 100644 index 00000000000..5caf29c6197 --- /dev/null +++ b/tests/ui/crashes/ice-5944.rs @@ -0,0 +1,13 @@ +#![warn(clippy::repeat_once)] + +trait Repeat { + fn repeat(&self) {} +} + +impl Repeat for usize { + fn repeat(&self) {} +} + +fn main() { + let _ = 42.repeat(); +} -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 031d69e86a1..f1df634701d 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -211,8 +211,21 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { needs_check_adjustment = false; }, ExprKind::Field(..) => { - dereferenced_expr = parent_expr; needs_check_adjustment = true; + + // Check whether implicit dereferences happened; + // if so, no need to go further up + // because of the same reason as the `ExprKind::Unary` case. + if cx + .typeck_results() + .expr_adjustments(dereferenced_expr) + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(_))) + { + break; + } + + dereferenced_expr = parent_expr; }, ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index fef9f4f39f8..310769cd002 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -2,7 +2,7 @@ #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] use std::borrow::Cow; -use std::cell::Cell; +use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; @@ -30,6 +30,37 @@ impl Trait for u64 { const ATOMIC: AtomicUsize = AtomicUsize::new(9); } +// This is just a pointer that can be safely dereferended, +// it's semantically the same as `&'static T`; +// but it isn't allowed make a static reference from an arbitrary integer value at the moment. +// For more information, please see the issue #5918. +pub struct StaticRef { + ptr: *const T, +} + +impl StaticRef { + /// Create a new `StaticRef` from a raw pointer + /// + /// ## Safety + /// + /// Callers must pass in a reference to statically allocated memory which + /// does not overlap with other values. + pub const unsafe fn new(ptr: *const T) -> StaticRef { + StaticRef { ptr } + } +} + +impl std::ops::Deref for StaticRef { + type Target = T; + + fn deref(&self) -> &'static T { + unsafe { &*self.ptr } + } +} + +// use a tuple to make sure referencing a field behind a pointer isn't linted. +const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; + fn main() { ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability @@ -82,4 +113,6 @@ fn main() { assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. + + let _ = &CELL_REF.0; } diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index dc738064a17..1e0b3e4d20a 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:34:5 + --> $DIR/borrow_interior_mutable_const.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:35:16 + --> $DIR/borrow_interior_mutable_const.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:38:22 + --> $DIR/borrow_interior_mutable_const.rs:69:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:39:25 + --> $DIR/borrow_interior_mutable_const.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:40:27 + --> $DIR/borrow_interior_mutable_const.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:41:26 + --> $DIR/borrow_interior_mutable_const.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:52:14 + --> $DIR/borrow_interior_mutable_const.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:53:14 + --> $DIR/borrow_interior_mutable_const.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:54:19 + --> $DIR/borrow_interior_mutable_const.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:55:14 + --> $DIR/borrow_interior_mutable_const.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:56:13 + --> $DIR/borrow_interior_mutable_const.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:62:13 + --> $DIR/borrow_interior_mutable_const.rs:93:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:5 + --> $DIR/borrow_interior_mutable_const.rs:98:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:68:16 + --> $DIR/borrow_interior_mutable_const.rs:99:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:5 + --> $DIR/borrow_interior_mutable_const.rs:112:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:82:16 + --> $DIR/borrow_interior_mutable_const.rs:113:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From b2c226679251bb795d786d7cdd11d2e2c9d0e685 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 18 Aug 2020 23:00:21 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8aa478ea2d6..d8fad217e5a 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -95,12 +95,17 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { - struct ClosureUsageCount<'tcx> { + fn count_closure_usage<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + block: &'tcx hir::Block<'_>, + path: &'tcx hir::Path<'tcx>, + ) -> usize { + struct ClosureUsageCount<'a, 'tcx> { + cx: &'a LateContext<'tcx>, path: &'tcx hir::Path<'tcx>, count: usize, }; - impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { @@ -117,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::None + hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } }; - let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(cx, block, path) == 1; then { span_lint( cx, diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index e29a1dce0c7..1f4864b7289 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -24,4 +24,16 @@ fn main() { let shadowed_closure = || 2; i = shadowed_closure(); i = shadowed_closure(); + + // Fix FP in #5916 + let mut x; + let create = || 2 * 2; + x = create(); + fun(move || { + x = create(); + }) +} + +fn fun(mut f: T) { + f(); } -- cgit 1.4.1-3-g733a5 From 9fe0ac36a53dfba14f5f33f5bab2fd243fb2c18e Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 24 Aug 2020 10:11:53 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index d8fad217e5a..49cb2ffc4e3 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,7 +77,7 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 79f27663461..2735e41738f 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 644161d9f5d..afd704ef12a 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); -- cgit 1.4.1-3-g733a5 From 3d820f71fe820c0bb7a1204e28ce549407a937cc Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 24 Aug 2020 14:01:27 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 22942d9fb0c..4f0a533592c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2150,18 +2150,19 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; + let snippet = if in_macro(arg.span) { + snippet_with_macro_callsite(cx, arg.span, "_") + } else { + snippet(cx, arg.span, "_") + }; + span_lint_and_sugg( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!( - "{}::<{}>::clone(&{})", - caller_type, - subst.type_at(0), - snippet(cx, arg.span, "_") - ), + format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 2c9d4d39e6c..e785ac02feb 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -90,3 +90,21 @@ mod many_derefs { let _ = &encoded.clone(); } } + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + } +} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 113fab69009..5ffa6c4fd06 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -96,5 +96,11 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &<&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:108:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 680c68153bced747c90947e0265c92076edcb838 Mon Sep 17 00:00:00 2001 From: Bastian Date: Mon, 24 Aug 2020 16:31:51 +0200 Subject: Added a lint which corrects expressions like (a - b) < f32::EPSILON --- CHANGELOG.md | 1 + clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce63c0a157..a5da0f7b767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1498,6 +1498,7 @@ Released 2018-09-13 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs new file mode 100644 index 00000000000..c8e53ce6f95 --- /dev/null +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -0,0 +1,112 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or + /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// + /// **Why is this bad?** The code without `.abs()` is more likely to have a bug. + /// + /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is + /// technically unneccessary. However, it will make the code more robust and doesn't have any + /// large performance implications. If the abs call was deliberately left out for performance + /// reasons, it is probably better to state this explicitly in the code, which then can be done + /// with an allow. + /// + /// **Example:** + /// + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b) < f32::EPSILON + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b).abs() < f32::EPSILON + /// } + /// ``` + pub FLOAT_EQUALITY_WITHOUT_ABS, + correctness, + "float equality check without `.abs()`" +} + +declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); + +impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let lhs; + let rhs; + + // check if expr is a binary expression with a lt or gt operator + if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + match op.node { + BinOpKind::Lt => { + lhs = left; + rhs = right; + }, + BinOpKind::Gt => { + lhs = right; + rhs = left; + }, + _ => return, + }; + } else { + return; + } + + if_chain! { + + // left hand side is a substraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, + .. + }, + val_l, + val_r, + ) = lhs.kind; + + // right hand side matches either f32::EPSILON or f64::EPSILON + if let ExprKind::Path(ref epsilon_path) = rhs.kind; + if match_qpath(epsilon_path, &["f32", "EPSILON"]) || match_qpath(epsilon_path, &["f64", "EPSILON"]); + + // values of the substractions on the left hand side are of the type float + let t_val_l = cx.typeck_results().expr_ty(val_l); + let t_val_r = cx.typeck_results().expr_ty(val_r); + if let ty::Float(_) = t_val_l.kind; + if let ty::Float(_) = t_val_r.kind; + + then { + // get the snippet string + let lhs_string = snippet( + cx, + lhs.span, + "(...)", + ); + // format the suggestion + let suggestion = if lhs_string.starts_with('(') { + format!("{}.abs()", lhs_string) + } else { + format!("({}).abs()", lhs_string) + }; + // spans the lint + span_lint_and_sugg( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d45394ab8d2..f78de7a175f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -193,6 +193,7 @@ mod excessive_bools; mod exit; mod explicit_write; mod fallible_impl_from; +mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; @@ -549,6 +550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, + &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, &float_literal::EXCESSIVE_PRECISION, &float_literal::LOSSY_FLOAT_LITERAL, &floating_point_arithmetic::IMPRECISE_FLOPS, @@ -1093,6 +1095,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1268,6 +1271,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(&explicit_write::EXPLICIT_WRITE), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&float_literal::EXCESSIVE_PRECISION), LintId::of(&format::USELESS_FORMAT), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), @@ -1686,6 +1690,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(&eq_op::EQ_OP), LintId::of(&erasing_op::ERASING_OP), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&if_let_mutex::IF_LET_MUTEX), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c50c6b900ae..eadd2621a40 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -661,6 +661,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "float_equality_without_abs", + group: "correctness", + desc: "float equality check without `.abs()`", + deprecation: None, + module: "float_equality_without_abs", + }, Lint { name: "fn_address_comparisons", group: "correctness", diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs new file mode 100644 index 00000000000..d40fa00c315 --- /dev/null +++ b/tests/ui/float_equality_without_abs.rs @@ -0,0 +1,31 @@ +#![warn(clippy::float_equality_without_abs)] + +pub fn is_roughly_equal(a: f32, b: f32) -> bool { + (a - b) < f32::EPSILON +} + +pub fn main() { + // all errors + is_roughly_equal(1.0, 2.0); + let a = 0.05; + let b = 0.0500001; + + let _ = (a - b) < f32::EPSILON; + let _ = a - b < f32::EPSILON; + let _ = a - b.abs() < f32::EPSILON; + let _ = (a as f64 - b as f64) < f64::EPSILON; + let _ = 1.0 - 2.0 < f32::EPSILON; + + let _ = f32::EPSILON > (a - b); + let _ = f32::EPSILON > a - b; + let _ = f32::EPSILON > a - b.abs(); + let _ = f64::EPSILON > (a as f64 - b as f64); + let _ = f32::EPSILON > 1.0 - 2.0; + + // those are correct + let _ = (a - b).abs() < f32::EPSILON; + let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + + let _ = f32::EPSILON > (a - b).abs(); + let _ = f64::EPSILON > (a as f64 - b as f64).abs(); +} diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr new file mode 100644 index 00000000000..74b9078afe8 --- /dev/null +++ b/tests/ui/float_equality_without_abs.stderr @@ -0,0 +1,70 @@ +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:4:5 + | +LL | (a - b) < f32::EPSILON + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | + = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:13:13 + | +LL | let _ = (a - b) < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:14:13 + | +LL | let _ = a - b < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:15:13 + | +LL | let _ = a - b.abs() < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:16:13 + | +LL | let _ = (a as f64 - b as f64) < f64::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:17:13 + | +LL | let _ = 1.0 - 2.0 < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:19:13 + | +LL | let _ = f32::EPSILON > (a - b); + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:20:13 + | +LL | let _ = f32::EPSILON > a - b; + | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:21:13 + | +LL | let _ = f32::EPSILON > a - b.abs(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:22:13 + | +LL | let _ = f64::EPSILON > (a as f64 - b as f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:23:13 + | +LL | let _ = f32::EPSILON > 1.0 - 2.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From e9964f2e8a1903d46e576f67122e234f719a90f0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 21 Aug 2020 02:03:57 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 22c49a20451..99e4b293ac6 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -86,6 +86,7 @@ struct LintDetection { slice_name: String, method: SortingKind, method_args: String, + slice_type: String, } fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { @@ -93,10 +94,10 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; if let Some(slice) = &args.get(0); if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); - if is_slice_of_primitives(cx, slice); + if let Some(slice_type) = is_slice_of_primitives(cx, slice); then { let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); - Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type }) } else { None } @@ -111,9 +112,10 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "used {} instead of {}", + "used {} instead of {} to sort primitive type `{}`", detection.method.stable_name(), - detection.method.unstable_name() + detection.method.unstable_name(), + detection.slice_type, ) .as_str(), "try", diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..25301d6dede 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1409,11 +1409,13 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Returns true iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function). -pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +/// Returns Option where String is a textual representation of the type encapsulated in the +/// slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and None otherwise. +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); - match expr_type.kind { + let expr_kind = &expr_type.kind; + let is_primitive = match expr_kind { ty::Slice(ref element_type) | ty::Ref( _, @@ -1424,7 +1426,24 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _, ) => is_recursively_primitive_type(element_type), _ => false, + }; + + if is_primitive { + // if we have wrappers like Array, Slice or Tuple, print these + // and get the type enclosed in the slice ref + match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind { + ty::Slice(..) => return Some("slice".into()), + ty::Array(..) => return Some("array".into()), + ty::Tuple(..) => return Some("tuple".into()), + _ => { + // is_recursively_primitive_type() should have taken care + // of the rest and we can rely on the type that is found + let refs_peeled = expr_type.peel_refs(); + return Some(refs_peeled.walk().last().unwrap().to_string()); + }, + } } + None } #[macro_export] diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b73012a4691..780389f32bc 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); -- cgit 1.4.1-3-g733a5 From 179df0bd15f9f866b4b3e275ed97aa8168a5b702 Mon Sep 17 00:00:00 2001 From: Bastian Date: Tue, 25 Aug 2020 13:41:39 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index c8e53ce6f95..dc1c3bfc9ff 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { // right hand side matches either f32::EPSILON or f64::EPSILON if let ExprKind::Path(ref epsilon_path) = rhs.kind; - if match_qpath(epsilon_path, &["f32", "EPSILON"]) || match_qpath(epsilon_path, &["f64", "EPSILON"]); + if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); // values of the substractions on the left hand side are of the type float let t_val_l = cx.typeck_results().expr_ty(val_l); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index b6a1e0a6aa9..d44854aefe9 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -35,6 +35,8 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; +pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"]; +pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; -- cgit 1.4.1-3-g733a5 From 3b1e5d6ff79f93e0215c6fb4c802167834ea4e15 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 25 Aug 2020 12:05:02 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e5daa30f8ca..b691d363d2f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -260,17 +260,6 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. - fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - higher::range(expr).map_or(false, |_| { - !cx.tcx - .features() - .declared_lib_features - .iter() - .any(|(name, _)| name.as_str() == "range_is_empty") - }) - } - /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { if let ty::AssocKind::Fn = item.kind { @@ -296,10 +285,6 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - if should_skip_range(cx, expr) { - return false; - } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index d81676a3d9f..1f3b8ac99b1 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index ecdba921a5d..dc21de0001b 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed index eee3db9b7d4..79781766242 100644 --- a/tests/ui/len_zero_ranges.fixed +++ b/tests/ui/len_zero_ranges.fixed @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).is_empty(); } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).is_empty(); + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs index be2e0f38fd1..a0eb51cc976 100644 --- a/tests/ui/len_zero_ranges.rs +++ b/tests/ui/len_zero_ranges.rs @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).len() == 0; } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).len() == 0; + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr index acb85f7100a..d0defb5a79e 100644 --- a/tests/ui/len_zero_ranges.stderr +++ b/tests/ui/len_zero_ranges.stderr @@ -1,10 +1,16 @@ error: length comparison to zero - --> $DIR/len_zero_ranges.rs:11:17 + --> $DIR/len_zero_ranges.rs:9:17 | LL | let _ = (0..42).len() == 0; | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` | = note: `-D clippy::len-zero` implied by `-D warnings` -error: aborting due to previous error +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:13:17 + | +LL | let _ = (0_u8..=42).len() == 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 370fc45a0a2af502d43f0e1831def314e0580a92 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Tue, 25 Aug 2020 22:20:35 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4f0a533592c..1ef54d285f6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2150,11 +2150,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; - let snippet = if in_macro(arg.span) { - snippet_with_macro_callsite(cx, arg.span, "_") - } else { - snippet(cx, arg.span, "_") - }; + let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From 9d18c24b56b6079fc0b66d93b8679c760cd364fb Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 26 Aug 2020 16:03:35 +0900 Subject: Fix typo --- clippy_lints/src/utils/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 25301d6dede..e6be5c4588f 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -574,7 +574,7 @@ pub fn snippet_block<'a, T: LintContext>( } /// Same as `snippet_block`, but adapts the applicability level by the rules of -/// `snippet_with_applicabiliy`. +/// `snippet_with_applicability`. pub fn snippet_block_with_applicability<'a, T: LintContext>( cx: &T, span: Span, @@ -1304,7 +1304,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// check if expr is calling method or function with #[must_use] attribyte +// check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { ExprKind::Call(ref path, _) => if_chain! { -- cgit 1.4.1-3-g733a5 From 2d853148d72d49956052cf1cdb5f3be18c85a9fc Mon Sep 17 00:00:00 2001 From: Bastian Date: Wed, 26 Aug 2020 16:39:30 +0200 Subject: Changed the location of the suggestion as well as the way the suggestion is assembled --- clippy_lints/src/float_equality_without_abs.rs | 30 ++++++++---------- tests/ui/float_equality_without_abs.stderr | 44 +++++++++++++++++++------- 2 files changed, 47 insertions(+), 27 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index dc1c3bfc9ff..9ac5a45eb45 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,5 +1,6 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; use if_chain::if_chain; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -84,27 +85,24 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { if let ty::Float(_) = t_val_r.kind; then { - // get the snippet string - let lhs_string = snippet( - cx, - lhs.span, - "(...)", - ); + let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); // format the suggestion - let suggestion = if lhs_string.starts_with('(') { - format!("{}.abs()", lhs_string) - } else { - format!("({}).abs()", lhs_string) - }; + let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); // spans the lint - span_lint_and_sugg( + span_lint_and_then( cx, FLOAT_EQUALITY_WITHOUT_ABS, expr.span, "float equality check without `.abs()`", - "add `.abs()`", - suggestion, - Applicability::MaybeIncorrect, + | diag | { + diag.span_suggestion( + lhs.span, + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } ); } } diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr index 74b9078afe8..b34c8159da0 100644 --- a/tests/ui/float_equality_without_abs.stderr +++ b/tests/ui/float_equality_without_abs.stderr @@ -2,7 +2,9 @@ error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:4:5 | LL | (a - b) < f32::EPSILON - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` | = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` @@ -10,61 +12,81 @@ error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:13:13 | LL | let _ = (a - b) < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:14:13 | LL | let _ = a - b < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -----^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:15:13 | LL | let _ = a - b.abs() < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + | -----------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:16:13 | LL | let _ = (a as f64 - b as f64) < f64::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:17:13 | LL | let _ = 1.0 - 2.0 < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + | ---------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:19:13 | LL | let _ = f32::EPSILON > (a - b); - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | ^^^^^^^^^^^^^^^------- + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:20:13 | LL | let _ = f32::EPSILON > a - b; - | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | ^^^^^^^^^^^^^^^----- + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:21:13 | LL | let _ = f32::EPSILON > a - b.abs(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + | ^^^^^^^^^^^^^^^----------- + | | + | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:22:13 | LL | let _ = f64::EPSILON > (a as f64 - b as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + | ^^^^^^^^^^^^^^^--------------------- + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:23:13 | LL | let _ = f32::EPSILON > 1.0 - 2.0; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + | ^^^^^^^^^^^^^^^--------- + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 5f88dcb188a..e653240d049 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -297,13 +297,14 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable), - ); + // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed + #[allow(clippy::option_if_let_else)] + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From 91024f1fdee2d2c2febfc7c76127d68d2b6e629e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Wed, 26 Aug 2020 16:31:49 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index a5da0f7b767..137b561028a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1778,6 +1778,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f78de7a175f..577ce6523b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -314,6 +314,7 @@ mod unused_io_amount; mod unused_self; mod unused_unit; mod unwrap; +mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; @@ -850,6 +851,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, + &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, @@ -1094,6 +1096,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); @@ -1133,6 +1136,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs new file mode 100644 index 00000000000..1c7e62ecd3d --- /dev/null +++ b/clippy_lints/src/unwrap_in_result.rs @@ -0,0 +1,140 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` + /// + /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. + /// + /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors. + /// + /// **Example:** + /// Before: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .expect("cannot divide the input by three"); + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// After: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .map_err(|e| format!("cannot divide the input by three: {}", e))?; + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + pub UNWRAP_IN_RESULT, + restriction, + "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" +} + +declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindExpectUnwrap<'a, 'tcx> { + lcx: &'a LateContext<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // check for `expect` + if let Some(arglists) = method_chain_args(expr, &["expect"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let mut fpu = FindExpectUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + UNWRAP_IN_RESULT, + impl_span, + "used unwrap or expect in a function that returns result or option", + move |diag| { + diag.help( + "unwrap and expect should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "potential non-recoverable error(s)"); + }); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index eadd2621a40..687fac7baa8 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2516,6 +2516,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "unused_unit", }, + Lint { + name: "unwrap_in_result", + group: "restriction", + desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`", + deprecation: None, + module: "unwrap_in_result", + }, Lint { name: "unwrap_used", group: "restriction", diff --git a/tests/ui/unwrap_in_result.rs b/tests/ui/unwrap_in_result.rs new file mode 100644 index 00000000000..2aa842adc85 --- /dev/null +++ b/tests/ui/unwrap_in_result.rs @@ -0,0 +1,44 @@ +#![warn(clippy::unwrap_in_result)] + +struct A; + +impl A { + // should not be detected + fn good_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i_result = i_str.parse::(); + match i_result { + Err(_e) => Err("Not a number".to_string()), + Ok(i) => { + if i % 3 == 0 { + return Ok(true); + } + Err("Number is not divisible by 3".to_string()) + }, + } + } + + // should be detected + fn bad_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i = i_str.parse::().unwrap(); + if i % 3 == 0 { + Ok(true) + } else { + Err("Number is not divisible by 3".to_string()) + } + } + + fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().expect("not a number"); + if i % 3 == 0 { + return Some(true); + } + None + } +} + +fn main() { + A::bad_divisible_by_3("3".to_string()); + A::good_divisible_by_3("3".to_string()); +} diff --git a/tests/ui/unwrap_in_result.stderr b/tests/ui/unwrap_in_result.stderr new file mode 100644 index 00000000000..56bc2f2d1c0 --- /dev/null +++ b/tests/ui/unwrap_in_result.stderr @@ -0,0 +1,41 @@ +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:22:5 + | +LL | / fn bad_divisible_by_3(i_str: String) -> Result { +LL | | // checks whether a string represents a number divisible by 3 +LL | | let i = i_str.parse::().unwrap(); +LL | | if i % 3 == 0 { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::unwrap-in-result` implied by `-D warnings` + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:24:17 + | +LL | let i = i_str.parse::().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:32:5 + | +LL | / fn example_option_expect(i_str: String) -> Option { +LL | | let i = i_str.parse::().expect("not a number"); +LL | | if i % 3 == 0 { +LL | | return Some(true); +LL | | } +LL | | None +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:33:17 + | +LL | let i = i_str.parse::().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 04bff17668be1305d9efe235665a32727ff3e0b5 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 27 Aug 2020 23:37:47 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 4b6a0a6a0c9..006d7a3a12d 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,6 +1,7 @@ -use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, Item, ItemKind}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -45,11 +46,15 @@ declare_clippy_lint! { #[derive(Default)] pub struct ToStringInDisplay { in_display_impl: bool, + self_hir_id: Option, } impl ToStringInDisplay { pub fn new() -> Self { - Self { in_display_impl: false } + Self { + in_display_impl: false, + self_hir_id: None, + } } } @@ -65,16 +70,33 @@ impl LateLintPass<'_> for ToStringInDisplay { fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if is_display_impl(cx, item) { self.in_display_impl = false; + self.self_hir_id = None; + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + if_chain! { + if self.in_display_impl; + if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + let body = cx.tcx.hir().body(*body_id); + if !body.params.is_empty(); + then { + let self_param = &body.params[0]; + self.self_hir_id = Some(self_param.pat.hir_id); + } } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind; + if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if match_trait_method(cx, expr, &paths::TO_STRING); if self.in_display_impl; - + if let ExprKind::Path(ref qpath) = args[0].kind; + if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id); + if let Some(self_hir_id) = self.self_hir_id; + if hir_id == self_hir_id; then { span_lint( cx, diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs index 3b46324704e..eb8105c6b6d 100644 --- a/tests/ui/to_string_in_display.rs +++ b/tests/ui/to_string_in_display.rs @@ -44,6 +44,20 @@ impl fmt::Display for C { } } +enum D { + E(String), + F, +} + +impl std::fmt::Display for D { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::E(string) => write!(f, "E {}", string.to_string()), + Self::F => write!(f, "F"), + } + } +} + fn main() { let a = A; a.to_string(); -- cgit 1.4.1-3-g733a5 From 2a3ee5fa854b49530008582900c6ea7fac120d1c Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 7 Jul 2020 22:47:32 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1ef54d285f6..8e91cbb3cdf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,6 +15,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{FnRetTy, FnSig, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -28,11 +29,11 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + is_ctor_or_promotable_const_function, is_expn_of, is_self_ty, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1631,6 +1632,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1670,6 +1676,48 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); + if !item.span.from_expansion(); + if item.ident.name == sym!(new); + if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; + if let FnRetTy::Return(ret_ty) = &decl.output; + + then { + let mut visitor = HasSelfVisitor { has_self_ty: false }; + visitor.visit_ty(ret_ty); + if !visitor.has_self_ty { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } + } + } + } +} + +struct HasSelfVisitor { + pub has_self_ty: bool, +} + +impl<'tcx> intravisit::Visitor<'tcx> for HasSelfVisitor { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + if is_self_ty(ty) { + self.has_self_ty = true; + } else { + intravisit::walk_ty(self, ty); + } + } + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } } /// Checks for the `OR_FUN_CALL` lint. diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index 2c2d1e27589..e98360ea691 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -210,3 +210,133 @@ impl<'a> WithLifetime<'a> { unimplemented!(); } } + +mod issue5435 { + struct V; + + pub trait TraitRetSelf { + // should not trigger lint + fn new() -> Self; + } + + pub trait TraitRet { + // should trigger lint as we are in trait definition + fn new() -> String; + } + pub struct StructRet; + impl TraitRet for StructRet { + // should not trigger lint as we are in the impl block + fn new() -> String { + unimplemented!(); + } + } + + pub trait TraitRet2 { + // should trigger lint + fn new(_: String) -> String; + } + + trait TupleReturnerOk { + // should not trigger lint + fn new() -> (Self, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + fn new() -> (u32, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + fn new() -> (Self, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerBad { + // should trigger lint + fn new() -> (u32, u32) { + unimplemented!(); + } + } + + trait MutPointerReturnerOk { + // should not trigger lint + fn new() -> *mut Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerOk2 { + // should not trigger lint + fn new() -> *const Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerBad { + // should trigger lint + fn new() -> *mut V { + unimplemented!(); + } + } + + trait GenericReturnerOk { + // should not trigger lint + fn new() -> Option + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk { + // should not trigger lint + fn new() -> (Option, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk2 { + // should not trigger lint + fn new() -> ((Self, u32), u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk3 { + // should not trigger lint + fn new() -> Option<(Self, u32)> + where + Self: Sized, + { + unimplemented!(); + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index dd5a24bcbe7..8217bc6187f 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -48,5 +48,33 @@ LL | | unimplemented!(); LL | | } | |_____^ -error: aborting due to 6 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:224:9 + | +LL | fn new() -> String; + | ^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:236:9 + | +LL | fn new(_: String) -> String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:271:9 + | +LL | / fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:298:9 + | +LL | / fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 3cb75c2e5cdd4f450f2974c5e052d569674d95fd Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 25 Aug 2020 09:16:08 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8e91cbb3cdf..2388310628f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -725,6 +725,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** + /// In an impl block: /// ```rust /// # struct Foo; /// # struct NotAFoo; @@ -737,25 +738,40 @@ declare_clippy_lint! { /// /// ```rust /// # struct Foo; - /// # struct FooError; + /// struct Bar(Foo); /// impl Foo { - /// // Good. Return type contains `Self` - /// fn new() -> Result { - /// # Ok(Foo) + /// // Bad. The type name must contain `Self` + /// fn new() -> Bar { + /// # Bar(Foo) /// } /// } /// ``` /// /// ```rust /// # struct Foo; - /// struct Bar(Foo); + /// # struct FooError; /// impl Foo { - /// // Bad. The type name must contain `Self`. - /// fn new() -> Bar { - /// # Bar(Foo) + /// // Good. Return type contains `Self` + /// fn new() -> Result { + /// # Ok(Foo) /// } /// } /// ``` + /// + /// Or in a trait definition: + /// ```rust + /// pub trait Trait { + /// // Bad. The type name must contain `Self` + /// fn new(); + /// } + /// ``` + /// + /// ```rust + /// pub trait Trait { + /// // Good. Return type contains `Self` + /// fn new() -> Self; + /// } + /// ``` pub NEW_RET_NO_SELF, style, "not returning type containing `Self` in a `new` method" @@ -1679,8 +1695,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); - if !item.span.from_expansion(); if item.ident.name == sym!(new); if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; if let FnRetTy::Return(ret_ty) = &decl.output; diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index e98360ea691..e82873629a5 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -137,9 +137,9 @@ impl MutPointerReturnerOk { } } -struct MutPointerReturnerOk2; +struct ConstPointerReturnerOk2; -impl MutPointerReturnerOk2 { +impl ConstPointerReturnerOk2 { // should not trigger lint pub fn new() -> *const Self { unimplemented!(); @@ -283,7 +283,7 @@ mod issue5435 { } } - trait MutPointerReturnerOk2 { + trait ConstPointerReturnerOk2 { // should not trigger lint fn new() -> *const Self where -- cgit 1.4.1-3-g733a5 From 504612622f2801b43dbe3e6788d2404d394376df Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 27 Aug 2020 18:24:59 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2388310628f..63e0c183113 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,12 +15,11 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FnRetTy, FnSig, TraitItem, TraitItemKind}; +use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -28,8 +27,8 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_self_ty, is_type_diagnostic_item, iter_input_pats, + contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, + is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, @@ -1656,16 +1655,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); - let contains_self_ty = |ty: Ty<'tcx>| { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty), - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) - }; - // walk the return type and check for Self (this does not check associated types) - if contains_self_ty(ret_ty) { + if contains_ty(ret_ty, self_ty) { return; } @@ -1675,7 +1666,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates { if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self - if contains_self_ty(projection_predicate.ty) { + if contains_ty(projection_predicate.ty, self_ty) { return; } } @@ -1696,44 +1687,23 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { if item.ident.name == sym!(new); - if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; - if let FnRetTy::Return(ret_ty) = &decl.output; + if let TraitItemKind::Fn(_, _) = item.kind; + let ret_ty = return_ty(cx, item.hir_id); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + if !contains_ty(ret_ty, self_ty); then { - let mut visitor = HasSelfVisitor { has_self_ty: false }; - visitor.visit_ty(ret_ty); - if !visitor.has_self_ty { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); - } + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); } } } } -struct HasSelfVisitor { - pub has_self_ty: bool, -} - -impl<'tcx> intravisit::Visitor<'tcx> for HasSelfVisitor { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { - if is_self_ty(ty) { - self.has_self_ty = true; - } else { - intravisit::walk_ty(self, ty); - } - } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e6be5c4588f..07ec59f452a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -42,7 +42,8 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; @@ -866,6 +867,14 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(&ret_ty) } +/// Walk into `ty` and returns `true` if any inner type is the same as `other_ty` +pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind { -- cgit 1.4.1-3-g733a5 From f3ccbef2af24d5d83f82f1fb50bd97a9b75e609f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 01:40:02 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3f5b3a5bcd5..16e48d91916 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, }; declare_clippy_lint! { @@ -802,6 +802,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } +#[allow(clippy::too_many_lines)] fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -856,18 +857,38 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); + let indent = indent_of(cx, expr.span).unwrap_or(0); - if let Some(mut sugg) = snippet_opt(cx, expr.span) { - arg_snippets.iter().for_each(|arg| { - sugg = sugg.replacen(arg, "()", 1); - }); - sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + if let Some(expr_str) = snippet_opt(cx, expr.span) { + let expr_with_replacements = arg_snippets + .iter() + .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); + + // expr is not in a block statement or result expression position, wrap in a block let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { - // expr is not in a block statement or result expression position, wrap in a block - sugg = format!("{{ {} }}", sugg); + let wrap_in_block = + !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); + + let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; + let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); + stmts_and_call.push(expr_with_replacements); + let mut stmts_and_call_str = stmts_and_call + .into_iter() + .enumerate() + .map(|(i, v)| { + let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; + trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() + }) + .collect::>() + .join(";\n"); + + if wrap_in_block { + stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; + stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); } + let sugg = stmts_and_call_str; + if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( &format!("use {}unit literal{} instead", singular, plural), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..d20b33c4a1d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -662,7 +662,7 @@ pub fn expr_block<'a, T: LintContext>( /// Trim indentation from a multiline string with possibility of ignoring the /// first line. -fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { +pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); trim_multiline_inner(s_tab, ignore_first, indent, ' ') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2e2bd054e42..fec115ff29d 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,11 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] +#![allow( + clippy::no_effect, + unused_must_use, + unused_variables, + clippy::unused_unit, + clippy::or_fun_call +)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 2a0cc1f18e2..90fee3aab23 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:23:5 + --> $DIR/unit_arg.rs:29:5 | LL | / foo({ LL | | 1; @@ -15,22 +15,24 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:26:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); foo(()); - | ^^^^^^^^^^^^^^^ +LL | foo(1); +LL | foo(()); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:33:5 | LL | / foo({ LL | | foo(1); @@ -47,11 +49,12 @@ help: or move the expression in front of the call and replace it with the unit l LL | { LL | foo(1); LL | foo(2); -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:38:5 | LL | / b.bar({ LL | | 1; @@ -66,22 +69,25 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; b.bar(()); +LL | }; +LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:41:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_multiple_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_multiple_units((), ()); + | error: passing unit values to a function - --> $DIR/unit_arg.rs:36:5 + --> $DIR/unit_arg.rs:42:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -95,14 +101,16 @@ LL | foo(2) | help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); { +LL | foo(0); +LL | { LL | foo(1); LL | foo(2); -LL | }; taking_multiple_units((), ()); +LL | }; +LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:46:5 | LL | / taking_multiple_units( LL | | { @@ -124,53 +132,50 @@ LL | foo(3) help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; { -LL | foo(2); -LL | foo(3); +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); ... -error: use of `or` followed by a function call - --> $DIR/unit_arg.rs:51:10 - | -LL | None.or(Some(foo(2))); - | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` - | - = note: `-D clippy::or-fun-call` implied by `-D warnings` - error: passing a unit value to a function - --> $DIR/unit_arg.rs:51:13 + --> $DIR/unit_arg.rs:57:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | None.or({ foo(2); Some(()) }); - | ^^^^^^^^^^^^^^^^^^^^ +LL | None.or({ +LL | foo(2); +LL | Some(()) +LL | }); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:54:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(()); foo(()) +LL | foo(()); +LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:93:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); Some(()) +LL | foo(1); +LL | Some(()) | -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 4cbbc8b8cd4..456b12a2c6b 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -24,8 +24,9 @@ LL | taking_two_units({}, foo(0)); | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); taking_two_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | taking_two_units((), ()); + | error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -35,8 +36,10 @@ LL | taking_three_units({}, foo(0), foo(1)); | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_three_units((), (), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_three_units((), (), ()); + | error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 459969f88ff95c94b7b34043a7f0e13de91de4f8 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:18:05 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..7af3b666cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,6 +1651,7 @@ Released 2018-09-13 [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic +[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..b70d126af5b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,6 +267,7 @@ mod open_options; mod option_env_unwrap; mod option_if_let_else; mod overflow_check_conditional; +mod panic_in_result; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; @@ -747,6 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, + &panic_in_result::PANIC_IN_RESULT, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::TODO, @@ -1086,6 +1088,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); + store.register_late_pass(|| box panic_in_result::PanicInResult); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1128,6 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(&panic_in_result::PANIC_IN_RESULT), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs new file mode 100644 index 00000000000..3a71a0db6fe --- /dev/null +++ b/clippy_lints/src/panic_in_result.rs @@ -0,0 +1,100 @@ +use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. + /// + /// **Why is this bad?** For some codebases, + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn option_with_panic() -> Option // should emit lint + /// { + /// panic!("error"); + /// } + /// ``` + + pub PANIC_IN_RESULT, + restriction, + "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` " +} + +declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for PanicInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindPanicUnimplementedUnreachable { + result: Vec, +} + +impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if is_expn_of(expr.span, "unimplemented").is_some() { + self.result.push(expr.span); + } else if is_expn_of(expr.span, "unreachable").is_some() { + self.result.push(expr.span); + } else if is_expn_of(expr.span, "panic").is_some() { + self.result.push(expr.span); + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let mut fpu = FindPanicUnimplementedUnreachable { + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT, + impl_span, + "used unimplemented, unreachable or panic in a function that returns result or option", + move |diag| { + diag.help( + "unimplemented, unreachable or panic should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "will cause the application to crash."); + }); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..ad57146048e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1704,6 +1704,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "panic_unimplemented", }, + Lint { + name: "panic_in_result", + group: "restriction", + desc: "default lint description", + deprecation: None, + module: "panic_in_result", + }, Lint { name: "panic_params", group: "style", diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs new file mode 100644 index 00000000000..21e9efca87b --- /dev/null +++ b/tests/ui/panic_in_result.rs @@ -0,0 +1,62 @@ +#![warn(clippy::panic_in_result)] + +struct A; + +impl A { + fn result_with_panic() -> Result // should emit lint + { + panic!("error"); + } + + fn result_with_unimplemented() -> Result // should emit lint + { + unimplemented!(); + } + + fn result_with_unreachable() -> Result // should emit lint + { + unreachable!(); + } + + fn option_with_unreachable() -> Option // should emit lint + { + unreachable!(); + } + + fn option_with_unimplemented() -> Option // should emit lint + { + unimplemented!(); + } + + fn option_with_panic() -> Option // should emit lint + { + panic!("error"); + } + + fn other_with_panic() // should not emit lint + { + panic!(""); + } + + fn other_with_unreachable() // should not emit lint + { + unreachable!(); + } + + fn other_with_unimplemented() // should not emit lint + { + unimplemented!(); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + Ok(true) + } + + fn option_without_banned_functions() -> Option // should not emit lint + { + Some(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr new file mode 100644 index 00000000000..74273bd9abb --- /dev/null +++ b/tests/ui/panic_in_result.stderr @@ -0,0 +1,105 @@ +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:6:5 + | +LL | / fn result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result` implied by `-D warnings` + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:8:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:11:5 + | +LL | / fn result_with_unimplemented() -> Result // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:13:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:16:5 + | +LL | / fn result_with_unreachable() -> Result // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:18:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:21:5 + | +LL | / fn option_with_unreachable() -> Option // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:23:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:26:5 + | +LL | / fn option_with_unimplemented() -> Option // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:28:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:31:5 + | +LL | / fn option_with_panic() -> Option // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:33:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From ceab1a9167655eba9f9556f8766f8702e49dfef3 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:19:24 -0700 Subject: removed unnecessary comment --- clippy_lints/src/panic_in_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 3a71a0db6fe..4e08c991bd0 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// fn option_with_panic() -> Option // should emit lint + /// fn option_with_panic() -> Option /// { /// panic!("error"); /// } -- cgit 1.4.1-3-g733a5 From 8462cce96081b87eba7a5bc89130a1a09fe1f6d0 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:22:37 -0700 Subject: edited documentation --- clippy_lints/src/panic_in_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 4e08c991bd0..239b4bdbdb1 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -9,7 +9,7 @@ use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. /// - /// **Why is this bad?** For some codebases, + /// **Why is this bad?** For some codebases, it is desirable for functions of type option/result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. /// /// **Known problems:** None. /// -- cgit 1.4.1-3-g733a5 From b006522393a3c3c2656e1ccdfbb0076ff1bd7e99 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:55:23 -0700 Subject: 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 | 22 ++++---------- tests/ui/panic_in_result.stderr | 60 ++++++++----------------------------- 4 files changed, 32 insertions(+), 78 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 239b4bdbdb1..2901f393fc6 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -7,16 +7,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type option/result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// fn option_with_panic() -> Option + /// fn result_with_panic() -> Result /// { /// panic!("error"); /// } @@ -24,7 +24,7 @@ declare_clippy_lint! { pub PANIC_IN_RESULT, restriction, - "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " } declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); @@ -35,8 +35,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { // first check if it's a method or function if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)); then { lint_impl_body(cx, impl_item.span, impl_item); } @@ -55,14 +54,13 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if is_expn_of(expr.span, "unimplemented").is_some() { - self.result.push(expr.span); - } else if is_expn_of(expr.span, "unreachable").is_some() { - self.result.push(expr.span); - } else if is_expn_of(expr.span, "panic").is_some() { + if is_expn_of(expr.span, "unimplemented").is_some() + || is_expn_of(expr.span, "unreachable").is_some() + || is_expn_of(expr.span, "panic").is_some() + || is_expn_of(expr.span, "todo").is_some() + { self.result.push(expr.span); } - // and check sub-expressions intravisit::walk_expr(self, expr); } @@ -88,10 +86,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc cx, PANIC_IN_RESULT, impl_span, - "used unimplemented, unreachable or panic in a function that returns result or option", + "used unimplemented, unreachable, todo or panic in a function that returns result", move |diag| { diag.help( - "unimplemented, unreachable or panic should not be used in a function that returns result or option" ); + "unimplemented, unreachable, todo or panic should not be used in a function that returns result" ); diag.span_note(fpu.result, "will cause the application to crash."); }); } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b4f20ca7f14..1f56c56f081 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1707,7 +1707,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "panic_in_result", group: "restriction", - desc: "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` ", + desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ", deprecation: None, module: "panic_in_result", }, diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs index 21e9efca87b..056778995a4 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result.rs @@ -18,19 +18,9 @@ impl A { unreachable!(); } - fn option_with_unreachable() -> Option // should emit lint + fn result_with_todo() -> Result // should emit lint { - unreachable!(); - } - - fn option_with_unimplemented() -> Option // should emit lint - { - unimplemented!(); - } - - fn option_with_panic() -> Option // should emit lint - { - panic!("error"); + todo!("Finish this"); } fn other_with_panic() // should not emit lint @@ -48,14 +38,14 @@ impl A { unimplemented!(); } - fn result_without_banned_functions() -> Result // should not emit lint + fn other_with_todo() // should not emit lint { - Ok(true) + todo!("finish this") } - fn option_without_banned_functions() -> Option // should not emit lint + fn result_without_banned_functions() -> Result // should not emit lint { - Some(true) + Ok(true) } } diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr index 74273bd9abb..3b9ac69f20d 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result.stderr @@ -1,4 +1,4 @@ -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:8:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:13:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:18:9 | @@ -50,56 +50,22 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:21:5 | -LL | / fn option_with_unreachable() -> Option // should emit lint +LL | / fn result_with_todo() -> Result // should emit lint LL | | { -LL | | unreachable!(); +LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:23:9 | -LL | unreachable!(); - | ^^^^^^^^^^^^^^^ - = 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"); - | ^^^^^^^^^^^^^^^^ +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 6 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From e8be047c5b2d5b1522a16b4b52cc7acfa4581ca3 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Fri, 28 Aug 2020 09:31:12 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 07ec59f452a..d9598c4abbd 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -868,7 +868,7 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> } /// Walk into `ty` and returns `true` if any inner type is the same as `other_ty` -pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { +pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, -- cgit 1.4.1-3-g733a5 From ffaadae8e48699f115eafd2853c252f546a69a28 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Fri, 28 Aug 2020 09:31:29 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d9598c4abbd..82005257115 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -867,7 +867,7 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(&ret_ty) } -/// Walk into `ty` and returns `true` if any inner type is the same as `other_ty` +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), -- cgit 1.4.1-3-g733a5 From 73b1ee1a614aaad7dd43958280ff4a444c8b737e Mon Sep 17 00:00:00 2001 From: Thibaud Date: Fri, 28 Aug 2020 09:33:05 +0200 Subject: Update clippy_lints/src/methods/mod.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/methods/mod.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 63e0c183113..9996df69470 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1686,6 +1686,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); if item.ident.name == sym!(new); if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); -- cgit 1.4.1-3-g733a5 From 5574182b4d2d08c848a88a1ac5633fc194e0465e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:40:22 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..b37273af44d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1444,6 +1444,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs new file mode 100644 index 00000000000..229536bd673 --- /dev/null +++ b/clippy_lints/src/create_dir.rs @@ -0,0 +1,50 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. + /// + /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// std::fs::create_dir("foo") + /// ``` + /// Use instead: + /// ```rust + /// std::fs::create_dir_all("foo") + /// ``` + pub CREATE_DIR, + restriction, + "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`" +} + +declare_lint_pass!(CreateDir => [CREATE_DIR]); + +impl LateLintPass<'_> for CreateDir { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["std", "fs", "create_dir"]); + then { + span_lint_and_sugg( + cx, + CREATE_DIR, + expr.span, + "calling `std::fs::create_dir` where there may be a better way", + "consider calling `std::fs::create_dir_all` instead", + format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + Applicability::MachineApplicable, + ) + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..7943be34c62 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod collapsible_if; mod comparison_chain; mod copies; mod copy_iterator; +mod create_dir; mod dbg_macro; mod default_trait_access; mod dereference; @@ -511,6 +512,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, + &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, @@ -1042,6 +1044,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); @@ -1104,6 +1107,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&exit::EXIT), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..e5134510922 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -290,6 +290,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copy_iterator", }, + Lint { + name: "create_dir", + group: "restriction", + desc: "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`", + deprecation: None, + module: "create_dir", + }, Lint { name: "crosspointer_transmute", group: "complexity", diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed new file mode 100644 index 00000000000..50f31f0c9c5 --- /dev/null +++ b/tests/ui/create_dir.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir_all("foo"); + std::fs::create_dir_all("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs new file mode 100644 index 00000000000..00ef37f413f --- /dev/null +++ b/tests/ui/create_dir.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir("foo"); + std::fs::create_dir("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr new file mode 100644 index 00000000000..3ae4680d929 --- /dev/null +++ b/tests/ui/create_dir.stderr @@ -0,0 +1,16 @@ +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:8:5 + | +LL | std::fs::create_dir("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | + = note: `-D clippy::create-dir` implied by `-D warnings` + +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:9:5 + | +LL | std::fs::create_dir("bar").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 607905d126c55422668007737c22d7a7a89c0d57 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:53:15 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 229536bd673..7ba6facda6a 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::*; @@ -33,7 +33,7 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["std", "fs", "create_dir"]); + if match_qpath(path, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index d44854aefe9..65320d6a0e0 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -110,6 +110,7 @@ pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; -- cgit 1.4.1-3-g733a5 From 34e302e67c08c9b97d58d062ea83cc1fd860c56e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 19:35:04 +0900 Subject: Fix clippy error --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 7ba6facda6a..4fede8857c7 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,7 +1,7 @@ use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -- cgit 1.4.1-3-g733a5 From eebd2483654456e332d7cf53218b56b9cbd6f2f5 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 19:56:03 +0900 Subject: Fix errors --- clippy_lints/src/create_dir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 4fede8857c7..bf47c1f84a2 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -15,11 +15,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// std::fs::create_dir("foo") + /// std::fs::create_dir("foo"); /// ``` /// Use instead: /// ```rust - /// std::fs::create_dir_all("foo") + /// std::fs::create_dir_all("foo"); /// ``` pub CREATE_DIR, restriction, -- cgit 1.4.1-3-g733a5 From 7a66e6502dc3c7085b3f4078c01d4957d96175ed Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 29 Aug 2020 01:18:42 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 82005257115..fe2ee093157 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -899,7 +899,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..5fb568672d3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); - let mut map = HashMap::::new(); - map.entry(42).or_insert_with(String::new); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert_with(String::new); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..737b0f7e55b 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..b8a436993f3 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,35 +60,23 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 - | -LL | map.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 - | -LL | btree.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:62:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:87:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:91:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 5b7590f841974255f74c64d573189aecc7a30b2e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 29 Aug 2020 14:20:01 +0900 Subject: Downgrade applicability of `create_dir` --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index bf47c1f84a2..1042eb45524 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) } } -- cgit 1.4.1-3-g733a5 From 4972989b616cbf96c015cd9fdf1f4b4464ecaace Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 14 Aug 2020 17:30:48 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d48821023..99a8b1a6293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1512,6 +1512,7 @@ Released 2018-09-13 [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs new file mode 100644 index 00000000000..ae347fcd3e8 --- /dev/null +++ b/clippy_lints/src/async_yields_async.rs @@ -0,0 +1,88 @@ +use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for async blocks that yield values of types that can themselves + /// be awaited. + /// + /// **Why is this bad?** + /// An await is likely missing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo() + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo().await + /// }; + /// } + /// ``` + pub ASYNC_YIELDS_ASYNC, + correctness, + "async blocks that return a type that can be awaited" +} + +declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); + +impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + use AsyncGeneratorKind::{Block, Closure}; + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind { + if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + let expr_ty = typeck_results.expr_ty(&body.value); + + if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { + let return_expr_span = match &body.value.kind { + // XXXkhuey there has to be a better way. + ExprKind::Block(block, _) => block.expr.map(|e| e.span), + ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), + _ => None, + }; + if let Some(return_expr_span) = return_expr_span { + span_lint_and_then( + cx, + ASYNC_YIELDS_ASYNC, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body.value.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..0eb1d331366 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -154,6 +154,7 @@ mod arithmetic; mod as_conversions; mod assertions_on_constants; mod assign_ops; +mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; @@ -483,6 +484,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, + &async_yields_async::ASYNC_YIELDS_ASYNC, &atomic_ordering::INVALID_ATOMIC_ORDERING, &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, &attrs::DEPRECATED_CFG_ATTR, @@ -1099,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1232,6 +1235,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_CFG_ATTR), @@ -1675,6 +1679,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..dff19ef440f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -52,6 +52,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "assign_ops", }, + Lint { + name: "async_yields_async", + group: "correctness", + desc: "async blocks that return a type that can be awaited", + deprecation: None, + module: "async_yields_async", + }, Lint { name: "await_holding_lock", group: "pedantic", diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed new file mode 100644 index 00000000000..cadc6494c76 --- /dev/null +++ b/tests/ui/async_yields_async.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + }.await + }; + let _i = async { + CustomFutureType.await + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + }.await + }; + let _k = async || { + CustomFutureType.await + }; + let _l = async || CustomFutureType.await; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType.await + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs new file mode 100644 index 00000000000..898fe1a9561 --- /dev/null +++ b/tests/ui/async_yields_async.rs @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + } + }; + let _i = async { + CustomFutureType + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + } + }; + let _k = async || { + CustomFutureType + }; + let _l = async || CustomFutureType; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr new file mode 100644 index 00000000000..112984cdccb --- /dev/null +++ b/tests/ui/async_yields_async.stderr @@ -0,0 +1,96 @@ +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:34:9 + | +LL | let _h = async { + | ____________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | + = note: `-D clippy::async-yields-async` implied by `-D warnings` +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:39:9 + | +LL | let _i = async { + | ____________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:45:9 + | +LL | let _j = async || { + | _______________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:50:9 + | +LL | let _k = async || { + | _______________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:52:23 + | +LL | let _l = async || CustomFutureType; + | ^^^^^^^^^^^^^^^^ + | | + | outer async construct + | awaitable value not awaited + | help: consider awaiting this value: `CustomFutureType.await` + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:58:9 + | +LL | let _m = async || { + | _______________________- +LL | | println!("I'm bored"); +LL | | // Some more stuff +LL | | +LL | | // Finally something to await +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 04912ca115ff153a97d80b604435b10dcb155dd0 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 22 Aug 2020 21:40:01 -0700 Subject: Formatting changes requested by ThibsG. --- clippy_lints/src/async_yields_async.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index ae347fcd3e8..88d9d3b5a26 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -5,12 +5,10 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** - /// Checks for async blocks that yield values of types that can themselves - /// be awaited. + /// **What it does:** Checks for async blocks that yield values of types + /// that can themselves be awaited. /// - /// **Why is this bad?** - /// An await is likely missing. + /// **Why is this bad?** An await is likely missing. /// /// **Known problems:** None. /// -- cgit 1.4.1-3-g733a5 From a424a2c1676a29c147252873037e8943d54941d3 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Sat, 29 Aug 2020 16:17:53 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 2901f393fc6..11fefc12316 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -1,6 +1,8 @@ use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; +use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -21,7 +23,6 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` - pub PANIC_IN_RESULT, restriction, "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " @@ -30,22 +31,26 @@ declare_clippy_lint! { declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for PanicInResult { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + /* + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + span: Span, + hir_id: hir::HirId, + ) { if_chain! { - // first check if it's a method or function - if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; - // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)); - then { - lint_impl_body(cx, impl_item.span, impl_item); + if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); + then + { + lint_impl_body(cx, span, body); } } - } + }*/ } -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; - struct FindPanicUnimplementedUnreachable { result: Vec, } @@ -70,29 +75,21 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if_chain! { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - then { - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindPanicUnimplementedUnreachable { - result: Vec::new(), - }; - fpu.visit_expr(&body.value); - - // if we've found one, lint - if !fpu.result.is_empty() { - span_lint_and_then( - cx, - PANIC_IN_RESULT, - impl_span, - "used unimplemented, unreachable, todo or panic in a function that returns result", - move |diag| { - diag.help( - "unimplemented, unreachable, todo or panic should not be used in a function that returns result" ); - diag.span_note(fpu.result, "will cause the application to crash."); - }); - } - } +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { + let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; + panics.visit_expr(&body.value); + if !panics.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT, + impl_span, + "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + move |diag| { + diag.help( + "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + ); + diag.span_note(panics.result, "return Err() instead of panicking"); + }, + ); } } diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs index 056778995a4..f6fb2f1ab61 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result.rs @@ -49,4 +49,22 @@ impl A { } } -fn main() {} +fn function_result_with_panic() -> Result // should emit lint +{ + panic!("error"); +} + +fn todo() { + println!("something"); +} + +fn function_result_with_custom_todo() -> Result // should not emit lint +{ + todo(); + Ok(true) +} + +fn main() -> Result<(), String> { + todo!("finish main method"); + Ok(()) +} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr index 3b9ac69f20d..9faedf82986 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result.stderr @@ -1,4 +1,4 @@ -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,15 +8,15 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:8:9 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,15 +25,15 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:13:9 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,15 +42,15 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:18:9 | LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:21:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,13 +59,47 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:23:9 | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result.rs:52:1 + | +LL | / fn function_result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result.rs:54:5 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result.rs:67:1 + | +LL | / fn main() -> Result<(), String> { +LL | | todo!("finish main method"); +LL | | Ok(()) +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result.rs:68:5 + | +LL | todo!("finish main method"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 73a3288282e733bfc5893e9920d29f1de5a21591 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Sat, 29 Aug 2020 16:22:15 -0700 Subject: uncommented fn --- clippy_lints/src/panic_in_result.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 11fefc12316..8ba365cb00c 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -31,7 +31,6 @@ declare_clippy_lint! { declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for PanicInResult { - /* fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -48,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { lint_impl_body(cx, span, body); } } - }*/ + } } struct FindPanicUnimplementedUnreachable { -- cgit 1.4.1-3-g733a5 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: Syntax-highlight `single_char_push_str` lint --- clippy_lints/src/methods/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9996df69470..7c4a78cbdcd 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1324,20 +1324,20 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using push_str with a single-character string literal, - /// and push with a char would work fine. + /// **What it does:** Warns when using `push_str` with a single-character string literal, + /// and `push` with a `char` would work fine. /// - /// **Why is this bad?** It's less clear that we are pushing a single character + /// **Why is this bad?** It's less clear that we are pushing a single character. /// /// **Known problems:** None /// /// **Example:** - /// ``` + /// ```rust /// let mut string = String::new(); /// string.push_str("R"); /// ``` /// Could be written as - /// ``` + /// ```rust /// let mut string = String::new(); /// string.push('R'); /// ``` -- cgit 1.4.1-3-g733a5 From 451ef7880392f3f06088ff7a7b957e3485f4bc6c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 31 Aug 2020 22:40:47 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 1042eb45524..f80a0efa7a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -33,7 +33,8 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &paths::STD_FS_CREATE_DIR); + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 50f31f0c9c5..0e28f87e33d 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir_all("foo"); std::fs::create_dir_all("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 00ef37f413f..1f226298c0d 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir("foo"); std::fs::create_dir("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 3ae4680d929..0c97bdd0f7a 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,5 +1,5 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:8:5 + --> $DIR/create_dir.rs:9:5 | LL | std::fs::create_dir("foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` @@ -7,7 +7,7 @@ LL | std::fs::create_dir("foo"); = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:10:5 | LL | std::fs::create_dir("bar").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` -- cgit 1.4.1-3-g733a5 From 001f9e45f24c5617d816e7d9dfbca4dc1a694dd9 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 00:05:53 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 2955f8d8e59..811fde388d1 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -132,7 +132,11 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let snippet = snippet(cx, expr.span, default); + let snippet = if expr.span.from_expansion() { + snippet_with_macro_callsite(cx, expr.span, default) + } else { + snippet(cx, expr.span, default) + }; match expr.kind { ast::ExprKind::AddrOf(..) diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 561283fc8e7..efd4187947b 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -135,4 +135,7 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) && matches!(true, true) {} } diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index dc9d9b451c0..657f32d38a3 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -149,4 +149,9 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) { + if matches!(true, true) {} + } } diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index f56dd65b9dd..acd1ec3f2ca 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -118,5 +118,13 @@ LL | println!("Hello world!"); LL | } | -error: aborting due to 7 previous errors +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:154:5 + | +LL | / if matches!(true, true) { +LL | | if matches!(true, true) {} +LL | | } + | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 8b0aa6a00b19b8e47a72157ec8e8f9e9060cb2fb Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 00:31:53 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 067ea903bdd..0b0a1307876 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(..) = expr_ty.kind { - let replacement = format!("{}::default()", expr_ty); + if let ty::Adt(def, ..) = expr_ty.kind { + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed new file mode 100644 index 00000000000..d05567a3f82 --- /dev/null +++ b/tests/ui/default_trait_access.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = std::string::String::default(); + + let s2 = String::default(); + + let s3: String = std::string::String::default(); + + let s4: String = std::string::String::default(); + + let s5 = string::String::default(); + + let s6: String = std::string::String::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault = GenericDerivedDefault::default(); + + let s12 = GenericDerivedDefault::::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = TupleDerivedDefault::default(); + + let s15: ArrayDerivedDefault = ArrayDerivedDefault::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = ::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly() -> T { + Default::default() + } + + pub fn make_t_nicely() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 2f1490a7036..447e70c0bbb 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,4 +1,7 @@ -#![warn(clippy::default_trait_access)] +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] use std::default; use std::default::Default as D2; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 26b2057548b..df8a5b94ddc 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,49 +1,53 @@ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:8:22 + --> $DIR/default_trait_access.rs:11:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | - = note: `-D clippy::default-trait-access` implied by `-D warnings` +note: the lint level is defined here + --> $DIR/default_trait_access.rs:4:9 + | +LL | #![deny(clippy::default_trait_access)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:12:22 + --> $DIR/default_trait_access.rs:15:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:14:22 + --> $DIR/default_trait_access.rs:17:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:18:22 + --> $DIR/default_trait_access.rs:21:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:28:46 +error: calling `GenericDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:31:46 | LL | let s11: GenericDerivedDefault = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:34:36 + --> $DIR/default_trait_access.rs:37:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:36:36 + --> $DIR/default_trait_access.rs:39:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:40:42 + --> $DIR/default_trait_access.rs:43:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` -- cgit 1.4.1-3-g733a5 From f9fcbbea03edb735c22311522b55d7b854bd6ac0 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Mon, 31 Aug 2020 13:32:05 -0700 Subject: fixed bug --- clippy_lints/src/panic_in_result.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 8ba365cb00c..8b8e211cb72 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -34,12 +34,15 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: FnKind<'tcx>, + fn_kind: FnKind<'tcx>, _: &'tcx hir::FnDecl<'tcx>, body: &'tcx hir::Body<'tcx>, span: Span, hir_id: hir::HirId, ) { + if let FnKind::Closure(_) = fn_kind { + return; + } if_chain! { if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); then -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 50d9c93f9d4..789d124eae2 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,8 +331,9 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting from/to bits in const contexts. + // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. let const_context = in_constant(cx, e.hir_id); let from_ty = cx.typeck_results().expr_ty(&args[0]); @@ -486,7 +487,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { Applicability::Unspecified, ); } else { - if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + if (cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty)) + && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 0d8a322f2b2..26b03bdc740 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -51,4 +51,12 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; } +// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) +const _: &() = { + struct ZST; + let zst = &ZST; + + unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } +}; + fn main() {} -- cgit 1.4.1-3-g733a5 From 2e4b4cebbbb37efa5dc69dd2616f3b7a288b92aa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 12:09:32 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index cfcc1b3c5f3..c8f153e7201 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -71,8 +71,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `extern crate` and `use` items annotated with /// lint attributes. /// - /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]` and - /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on + /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, + /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and + /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on /// `extern crate` items with a `#[macro_use]` attribute. /// /// **Why is this bad?** Lint attributes have no effect on crate imports. Most @@ -318,7 +319,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { if let Some(ident) = attr.ident() { match &*ident.as_str() { "allow" | "warn" | "deny" | "forbid" => { - // permit `unused_imports`, `deprecated` and `unreachable_pub` for `use` items + // permit `unused_imports`, `deprecated`, `unreachable_pub`, + // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { @@ -327,6 +329,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym!(deprecated)) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) + || extract_clippy_lint(lint) + .map_or(false, |s| s == "wildcard_imports") + || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") { return; } @@ -387,24 +392,25 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { - fn extract_name(lint: &NestedMetaItem) -> Option { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - then { - return Some(lint_name.as_str()); - } +/// Returns the lint name if it is clippy lint. +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { + if_chain! { + if let Some(meta_item) = lint.meta_item(); + if meta_item.path.segments.len() > 1; + if let tool_name = meta_item.path.segments[0].ident; + if tool_name.as_str() == "clippy"; + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + then { + return Some(lint_name.as_str()); } - None } + None +} +fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { let lint_store = cx.lints(); for lint in items { - if let Some(lint_name) = extract_name(lint) { + if let Some(lint_name) = extract_clippy_lint(lint) { if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) { diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index b222e2f7976..a5fcde768f1 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 3422eace4ab..0396d39e3d5 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 57ba976730c..d0194e4bbbe 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:53:5 + --> $DIR/useless_attribute.rs:61:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` -- cgit 1.4.1-3-g733a5 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: Allow GraphQL in doc without backticks --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 292dbd7ad6b..9c5a12ea9c8 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -122,7 +122,7 @@ define_Conf! { "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", - "OAuth", + "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "TensorFlow", -- cgit 1.4.1-3-g733a5 From aa7ffa5257667edb284de16b529df3d4111d70ab Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 22:39:09 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c95e43a9430..25345f8fa31 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,43 +1140,51 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + let ty = cx.typeck_results().expr_ty(pushed_item); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) } } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bfe27e02044..0903a873826 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -94,4 +94,10 @@ fn main() { vec13.push(item); item += 10; } + + // Fix #5979 + let mut vec14: Vec = Vec::new(); + for _ in 0..10 { + vec14.push(std::fs::File::open("foobar").unwrap()); + } } -- cgit 1.4.1-3-g733a5 From e49a29933be3bd988ccb75b053f480d9c99a7ff5 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 16:26:59 -0400 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0eb1d331366..8e80779377b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod map_clone; +mod map_err_ignore; mod map_identity; mod map_unit_fn; mod match_on_vec_items; @@ -624,6 +625,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, + &map_err_ignore::MAP_ERR_IGNORE, &map_identity::MAP_IDENTITY, &map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN, @@ -916,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box methods::Methods); store.register_late_pass(|| box map_clone::MapClone); + store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); store.register_late_pass(|| box types::LetUnitValue); store.register_late_pass(|| box types::UnitCmp); @@ -1327,6 +1330,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1534,6 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs new file mode 100644 index 00000000000..c63c201a9f3 --- /dev/null +++ b/clippy_lints/src/map_err_ignore.rs @@ -0,0 +1,108 @@ +use crate::utils::span_lint_and_sugg; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, CaptureBy, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` + /// + /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to bubble the original error + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// enum Errors { + /// Ignore + ///} + ///fn main() -> Result<(), Errors> { + /// + /// let x = u32::try_from(-123_i32); + /// + /// println!("{:?}", x.map_err(|_| Errors::Ignore)); + /// + /// Ok(()) + ///} + /// ``` + /// Use instead: + /// ```rust + /// enum Errors { + /// WithContext(TryFromIntError) + ///} + ///fn main() -> Result<(), Errors> { + /// + /// let x = u32::try_from(-123_i32); + /// + /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); + /// + /// Ok(()) + ///} + /// ``` + pub MAP_ERR_IGNORE, + style, + "`map_err` should not ignore the original error" +} + +declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]); + +impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { + // do not try to lint if this is from a macro or desugaring + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if e.span.from_expansion() { + return; + } + + // check if this is a method call (e.g. x.foo()) + if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { + // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] Enum::Variant[2])) + if method.ident.as_str() == "map_err" && args.len() == 2 { + // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields + if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { + // check if this is by Reference (meaning there's no move statement) + if capture == CaptureBy::Ref { + // Get the closure body to check the parameters and values + let closure_body = cx.tcx.hir().body(body_id); + // make sure there's only one parameter (`|_|`) + if closure_body.params.len() == 1 { + // make sure that parameter is the wild token (`_`) + if let PatKind::Wild = closure_body.params[0].pat.kind { + // Check the value of the closure to see if we can build the enum we are throwing away the error for + // make sure this is a Path + if let ExprKind::Path(q_path) = &closure_body.value.kind { + // this should be a resolved path, only keep the path field + if let QPath::Resolved(_, path) = q_path { + // finally get the idents for each path segment collect them as a string and join them with the path separator ("::"") + let closure_fold: String = path.segments.iter().map(|x| x.ident.as_str().to_string()).collect::>().join("::"); + //Span the body of the closure (the |...| bit) and suggest the fix by taking the error and encapsulating it in the enum + span_lint_and_sugg( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_err` has thrown away the original error", + "Allow the error enum to encapsulate the original error", + format!("|e| {}(e)", closure_fold), + Applicability::HasPlaceholders, + ); + } + } else { + //If we cannot build the enum in a human readable way just suggest not throwing way the error + span_lint_and_sugg( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_err` has thrown away the original error", + "Allow the error enum to encapsulate the original error", + "|e|".to_string(), + Applicability::HasPlaceholders, + ); + } + } + } + } + } + } + } + } +} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From 202a80c927412a548180eca5990ee764d76ed643 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 16:59:37 -0400 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index c63c201a9f3..43bfcf0b8f1 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,6 +1,6 @@ use crate::utils::span_lint_and_sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, CaptureBy, PatKind, QPath}; +use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -18,7 +18,7 @@ declare_clippy_lint! { /// Ignore ///} ///fn main() -> Result<(), Errors> { - /// + /// /// let x = u32::try_from(-123_i32); /// /// println!("{:?}", x.map_err(|_| Errors::Ignore)); @@ -32,7 +32,7 @@ declare_clippy_lint! { /// WithContext(TryFromIntError) ///} ///fn main() -> Result<(), Errors> { - /// + /// /// let x = u32::try_from(-123_i32); /// /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]); impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { - // do not try to lint if this is from a macro or desugaring + // do not try to lint if this is from a macro or desugaring fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if e.span.from_expansion() { return; @@ -56,26 +56,34 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { // check if this is a method call (e.g. x.foo()) if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { - // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] Enum::Variant[2])) + // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] + // Enum::Variant[2])) if method.ident.as_str() == "map_err" && args.len() == 2 { // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields - if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { + if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { // check if this is by Reference (meaning there's no move statement) - if capture == CaptureBy::Ref { - // Get the closure body to check the parameters and values + if capture == CaptureBy::Ref { + // Get the closure body to check the parameters and values let closure_body = cx.tcx.hir().body(body_id); // make sure there's only one parameter (`|_|`) - if closure_body.params.len() == 1 { - // make sure that parameter is the wild token (`_`) + if closure_body.params.len() == 1 { + // make sure that parameter is the wild token (`_`) if let PatKind::Wild = closure_body.params[0].pat.kind { - // Check the value of the closure to see if we can build the enum we are throwing away the error for - // make sure this is a Path + // Check the value of the closure to see if we can build the enum we are throwing away + // the error for make sure this is a Path if let ExprKind::Path(q_path) = &closure_body.value.kind { // this should be a resolved path, only keep the path field if let QPath::Resolved(_, path) = q_path { - // finally get the idents for each path segment collect them as a string and join them with the path separator ("::"") - let closure_fold: String = path.segments.iter().map(|x| x.ident.as_str().to_string()).collect::>().join("::"); - //Span the body of the closure (the |...| bit) and suggest the fix by taking the error and encapsulating it in the enum + // finally get the idents for each path segment collect them as a string and + // join them with the path separator ("::"") + let closure_fold: String = path + .segments + .iter() + .map(|x| x.ident.as_str().to_string()) + .collect::>() + .join("::"); + //Span the body of the closure (the |...| bit) and suggest the fix by taking + // the error and encapsulating it in the enum span_lint_and_sugg( cx, MAP_ERR_IGNORE, @@ -84,10 +92,11 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { "Allow the error enum to encapsulate the original error", format!("|e| {}(e)", closure_fold), Applicability::HasPlaceholders, - ); + ); } } else { - //If we cannot build the enum in a human readable way just suggest not throwing way the error + //If we cannot build the enum in a human readable way just suggest not throwing way + // the error span_lint_and_sugg( cx, MAP_ERR_IGNORE, @@ -96,13 +105,13 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { "Allow the error enum to encapsulate the original error", "|e|".to_string(), Applicability::HasPlaceholders, - ); + ); } } } - } + } } } } } -} \ No newline at end of file +} diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 9181d789d4f..6b5bcdaa78e 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,5 +1,6 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] +#![allow(clippy::map_err_ignore)] use std::mem::drop; diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 35ae88b78a4..7974bf56d44 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:9:5 + --> $DIR/drop_ref.rs:10:5 | LL | drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::drop-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:9:10 + --> $DIR/drop_ref.rs:10:10 | LL | drop(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:12:5 + --> $DIR/drop_ref.rs:13:5 | LL | drop(&owned1); | ^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:12:10 + --> $DIR/drop_ref.rs:13:10 | LL | drop(&owned1); | ^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:13:5 + --> $DIR/drop_ref.rs:14:5 | LL | drop(&&owned1); | ^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/drop_ref.rs:13:10 + --> $DIR/drop_ref.rs:14:10 | LL | drop(&&owned1); | ^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:14:5 + --> $DIR/drop_ref.rs:15:5 | LL | drop(&mut owned1); | ^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:14:10 + --> $DIR/drop_ref.rs:15:10 | LL | drop(&mut owned1); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:18:5 + --> $DIR/drop_ref.rs:19:5 | LL | drop(reference1); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:18:10 + --> $DIR/drop_ref.rs:19:10 | LL | drop(reference1); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:21:5 + --> $DIR/drop_ref.rs:22:5 | LL | drop(reference2); | ^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:21:10 + --> $DIR/drop_ref.rs:22:10 | LL | drop(reference2); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:24:5 + --> $DIR/drop_ref.rs:25:5 | LL | drop(reference3); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:24:10 + --> $DIR/drop_ref.rs:25:10 | LL | drop(reference3); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:29:5 + --> $DIR/drop_ref.rs:30:5 | LL | drop(&val); | ^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/drop_ref.rs:29:10 + --> $DIR/drop_ref.rs:30:10 | LL | drop(&val); | ^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:37:5 + --> $DIR/drop_ref.rs:38:5 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:37:20 + --> $DIR/drop_ref.rs:38:20 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs new file mode 100644 index 00000000000..f3a74ad95cd --- /dev/null +++ b/tests/ui/map_err.rs @@ -0,0 +1,24 @@ +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +enum Errors { + Ignored, +} + +impl Error for Errors {} + +impl fmt::Display for Errors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +fn main() -> Result<(), Errors> { + let x = u32::try_from(-123_i32); + + println!("{:?}", x.map_err(|_| Errors::Ignored)); + + Ok(()) +} diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr new file mode 100644 index 00000000000..8cd37d8c025 --- /dev/null +++ b/tests/ui/map_err.stderr @@ -0,0 +1,10 @@ +error: `map_err` has thrown away the original error + --> $DIR/map_err.rs:21:32 + | +LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); + | ^^^ help: Allow the error enum to encapsulate the original error: `|e| Errors::Ignored(e)` + | + = note: `-D clippy::map-err-ignore` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 337729137bdec31b55665653ef0cf7dc124b0eaa Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 17:05:40 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a8b1a6293..dfe45a46f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1675,6 +1675,7 @@ Released 2018-09-13 [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten [`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8e80779377b..3794cae091a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1538,7 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index dff19ef440f..6725c97f793 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1165,6 +1165,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "entry", }, + Lint { + name: "map_err_ignore", + group: "style", + desc: "`map_err` should not ignore the original error", + deprecation: None, + module: "map_err_ignore", + }, Lint { name: "map_flatten", group: "pedantic", -- cgit 1.4.1-3-g733a5 From a5754a1fad6eeb765bc825dcc657134985576787 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 2 Sep 2020 08:57:00 +0200 Subject: Run cargo dev fmt --- clippy_lints/src/utils/ast_utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 3c3f8b26e3a..fa8dd210eba 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -191,7 +191,9 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r), (Empty, Empty) => true, - (MacCall(l), MacCall(r)) => l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)), + (MacCall(l), MacCall(r)) => { + l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + }, _ => false, } } -- cgit 1.4.1-3-g733a5 From b220ddf146f4c11011b2e1b7f37ecb8e5485555b Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 2 Sep 2020 23:30:40 +0200 Subject: unit-arg - pr remarks --- clippy_lints/src/types.rs | 82 +++++++++++++++++++++--------------- clippy_lints/src/utils/mod.rs | 96 ++++++++++++++++++++++++------------------- 2 files changed, 103 insertions(+), 75 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 16e48d91916..e83d491fac3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -31,8 +31,8 @@ use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, + qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -802,7 +802,45 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } -#[allow(clippy::too_many_lines)] +fn fmt_stmts_and_call( + cx: &LateContext<'_>, + call_expr: &Expr<'_>, + call_snippet: &str, + args_snippets: &[impl AsRef], + non_empty_block_args_snippets: &[impl AsRef], +) -> String { + let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); + let call_snippet_with_replacements = args_snippets + .iter() + .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1)); + + let mut stmts_and_call = non_empty_block_args_snippets + .iter() + .map(|it| it.as_ref().to_owned()) + .collect::>(); + stmts_and_call.push(call_snippet_with_replacements); + stmts_and_call = stmts_and_call + .into_iter() + .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned()) + .collect(); + + let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); + // expr is not in a block statement or result expression position, wrap in a block + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let block_indent = call_expr_indent + 4; + stmts_and_call_snippet = + reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); + stmts_and_call_snippet = format!( + "{{\n{}{}\n{}}}", + " ".repeat(block_indent), + &stmts_and_call_snippet, + " ".repeat(call_expr_indent) + ); + } + stmts_and_call_snippet +} + fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -857,37 +895,15 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); - let indent = indent_of(cx, expr.span).unwrap_or(0); - - if let Some(expr_str) = snippet_opt(cx, expr.span) { - let expr_with_replacements = arg_snippets - .iter() - .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); - - // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - let wrap_in_block = - !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); - - let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; - let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); - stmts_and_call.push(expr_with_replacements); - let mut stmts_and_call_str = stmts_and_call - .into_iter() - .enumerate() - .map(|(i, v)| { - let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; - trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() - }) - .collect::>() - .join(";\n"); - - if wrap_in_block { - stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; - stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); - } - let sugg = stmts_and_call_str; + if let Some(call_snippet) = snippet_opt(cx, expr.span) { + let sugg = fmt_stmts_and_call( + cx, + expr, + &call_snippet, + &arg_snippets, + &arg_snippets_without_empty_blocks, + ); if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4ce4cdeefb4..f394b980127 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -19,6 +19,7 @@ pub mod paths; pub mod ptr; pub mod sugg; pub mod usage; + pub use self::attrs::*; pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; @@ -108,6 +109,7 @@ pub fn in_macro(span: Span) -> bool { false } } + // If the snippet is empty, it's an attribute that was inserted during macro // expansion and we want to ignore those, because they could come from external // sources that the user has no control over. @@ -571,7 +573,7 @@ pub fn snippet_block<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet(cx, span, default); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of @@ -585,7 +587,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet_with_applicability(cx, span, default, applicability); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -661,16 +663,16 @@ pub fn expr_block<'a, T: LintContext>( } } -/// Trim indentation from a multiline string with possibility of ignoring the -/// first line. -pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { - let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); - let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); - trim_multiline_inner(s_tab, ignore_first, indent, ' ') +/// Reindent a multiline string with possibility of ignoring the first line. +#[allow(clippy::needless_pass_by_value)] +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { + let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); + let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); + reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into() } -fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { - let mut x = s +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, ch: char) -> String { + let x = s .lines() .skip(ignore_first as usize) .filter_map(|l| { @@ -683,26 +685,20 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option 0 { - Cow::Owned( - s.lines() - .enumerate() - .map(|(i, l)| { - if (ignore_first && i == 0) || l.is_empty() { - l - } else { - l.split_at(x).1 - } - }) - .collect::>() - .join("\n"), - ) - } else { - s - } + let indent = indent.unwrap_or(0); + s.lines() + .enumerate() + .map(|(i, l)| { + if (ignore_first && i == 0) || l.is_empty() { + l.to_owned() + } else if x > indent { + l.split_at(x - indent).1.to_owned() + } else { + " ".repeat(indent - x) + l + } + }) + .collect::>() + .join("\n") } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -1475,26 +1471,26 @@ macro_rules! unwrap_cargo_metadata { #[cfg(test)] mod test { - use super::{trim_multiline, without_block_comments}; + use super::{reindent_multiline, without_block_comments}; #[test] - fn test_trim_multiline_single_line() { - assert_eq!("", trim_multiline("".into(), false, None)); - assert_eq!("...", trim_multiline("...".into(), false, None)); - assert_eq!("...", trim_multiline(" ...".into(), false, None)); - assert_eq!("...", trim_multiline("\t...".into(), false, None)); - assert_eq!("...", trim_multiline("\t\t...".into(), false, None)); + fn test_reindent_multiline_single_line() { + assert_eq!("", reindent_multiline("".into(), false, None)); + assert_eq!("...", reindent_multiline("...".into(), false, None)); + assert_eq!("...", reindent_multiline(" ...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t\t...".into(), false, None)); } #[test] #[rustfmt::skip] - fn test_trim_multiline_block() { + fn test_reindent_multiline_block() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { z @@ -1504,7 +1500,7 @@ mod test { \ty } else { \tz - }", trim_multiline(" if x { + }", reindent_multiline(" if x { \ty } else { \tz @@ -1513,14 +1509,14 @@ mod test { #[test] #[rustfmt::skip] - fn test_trim_multiline_empty_line() { + fn test_reindent_multiline_empty_line() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { @@ -1528,6 +1524,22 @@ mod test { }".into(), false, None)); } + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_lines_deeper() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline("\ + if x { + y + } else { + z + }".into(), true, Some(8))); + } + #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); -- cgit 1.4.1-3-g733a5 From 2387f68e437bf2ff5f117f63936257ce64052cfa Mon Sep 17 00:00:00 2001 From: Ricky Date: Wed, 2 Sep 2020 19:21:34 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 43bfcf0b8f1..9211113ed04 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,6 +1,6 @@ -use crate::utils::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind, QPath}; +use crate::utils::span_lint_and_help; + +use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -12,33 +12,58 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// + /// Before: /// ```rust + /// use std::convert::TryFrom; + /// + /// #[derive(Debug)] /// enum Errors { - /// Ignore - ///} - ///fn main() -> Result<(), Errors> { + /// Ignored + /// } /// - /// let x = u32::try_from(-123_i32); + /// fn divisible_by_3(inp: i32) -> Result { + /// let i = u32::try_from(inp).map_err(|_| Errors::Ignored)?; /// - /// println!("{:?}", x.map_err(|_| Errors::Ignore)); + /// Ok(i) + /// } + /// ``` /// - /// Ok(()) - ///} - /// ``` - /// Use instead: - /// ```rust - /// enum Errors { - /// WithContext(TryFromIntError) - ///} - ///fn main() -> Result<(), Errors> { + /// After: + /// ```rust + /// use std::convert::TryFrom; + /// use std::num::TryFromIntError; + /// use std::fmt; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum ParseError { + /// Indivisible { + /// source: TryFromIntError, + /// input: String, + /// } + /// } + /// + /// impl fmt::Display for ParseError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// match &self { + /// ParseError::Indivisible{source: _, input} => write!(f, "Error: {}", input) + /// } + /// } + /// } + /// + /// impl Error for ParseError {} /// - /// let x = u32::try_from(-123_i32); + /// impl ParseError { + /// fn new(source: TryFromIntError, input: String) -> ParseError { + /// ParseError::Indivisible{source, input} + /// } + /// } /// - /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); + /// fn divisible_by_3(inp: i32) -> Result { + /// let i = u32::try_from(inp).map_err(|e| ParseError::new(e, e.to_string()))?; /// - /// Ok(()) - ///} + /// Ok(i) + /// } /// ``` pub MAP_ERR_IGNORE, style, @@ -69,44 +94,16 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { if closure_body.params.len() == 1 { // make sure that parameter is the wild token (`_`) if let PatKind::Wild = closure_body.params[0].pat.kind { - // Check the value of the closure to see if we can build the enum we are throwing away - // the error for make sure this is a Path - if let ExprKind::Path(q_path) = &closure_body.value.kind { - // this should be a resolved path, only keep the path field - if let QPath::Resolved(_, path) = q_path { - // finally get the idents for each path segment collect them as a string and - // join them with the path separator ("::"") - let closure_fold: String = path - .segments - .iter() - .map(|x| x.ident.as_str().to_string()) - .collect::>() - .join("::"); - //Span the body of the closure (the |...| bit) and suggest the fix by taking - // the error and encapsulating it in the enum - span_lint_and_sugg( - cx, - MAP_ERR_IGNORE, - body_span, - "`map_err` has thrown away the original error", - "Allow the error enum to encapsulate the original error", - format!("|e| {}(e)", closure_fold), - Applicability::HasPlaceholders, - ); - } - } else { - //If we cannot build the enum in a human readable way just suggest not throwing way - // the error - span_lint_and_sugg( - cx, - MAP_ERR_IGNORE, - body_span, - "`map_err` has thrown away the original error", - "Allow the error enum to encapsulate the original error", - "|e|".to_string(), - Applicability::HasPlaceholders, - ); - } + // span the area of the closure capture and warn that the + // original error will be thrown away + span_lint_and_help( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_else(|_|...` ignores the original error", + None, + "Consider wrapping the error in an enum variant", + ); } } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8cd37d8c025..7a269ab95ab 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,10 +1,11 @@ -error: `map_err` has thrown away the original error +error: `map_else(|_|...` ignores the original error --> $DIR/map_err.rs:21:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); - | ^^^ help: Allow the error enum to encapsulate the original error: `|e| Errors::Ignored(e)` + | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` + = help: Consider wrapping the error in an enum variant error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From cf1cc7c449e881f0ac5d8474209222bc269df250 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 4 Sep 2020 05:15:31 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 0b0a1307876..320a2a257bd 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -38,37 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; then { - match qpath { - QPath::Resolved(..) => { - if_chain! { - // Detect and ignore ::default() because these calls do - // explicitly name the type. - if let ExprKind::Call(ref method, ref _args) = expr.kind; - if let ExprKind::Path(ref p) = method.kind; - if let QPath::Resolved(Some(_ty), _path) = p; - then { - return; - } - } - - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind { - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - }, - QPath::TypeRelative(..) | QPath::LangItem(..) => {}, + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } } -- cgit 1.4.1-3-g733a5 From 7bcf40a13d90952d9a59ed547832155439e3bcf7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 23:30:06 +0200 Subject: Fix fallout from rustup --- clippy_lints/src/default_trait_access.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 320a2a257bd..3048436d9a7 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let QPath::Resolved(None, _path) = qpath; then { let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind { + if let ty::Adt(def, ..) = expr_ty.kind() { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); -- cgit 1.4.1-3-g733a5 From 2905fff93659d17f1c6b1cf270b5731e04ebe46d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 23:30:55 +0200 Subject: Run cargo dev fmt --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 96e9d0f32f8..bea0a4d2459 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1432,7 +1432,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option false, }; -- cgit 1.4.1-3-g733a5 From c31d4730b0d40c62934839405d0c25e2ffa3fad1 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 4 Sep 2020 15:55:13 -0700 Subject: update example to be more idiomatic --- clippy_lints/src/map_err_ignore.rs | 87 ++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 27 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 9211113ed04..649de4133e0 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -14,55 +14,88 @@ declare_clippy_lint! { /// **Example:** /// Before: /// ```rust - /// use std::convert::TryFrom; + /// use std::fmt; /// /// #[derive(Debug)] - /// enum Errors { - /// Ignored + /// enum Error { + /// Indivisible, + /// Remainder(u8), + /// } + /// + /// 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 + /// ), + /// } + /// } /// } /// - /// fn divisible_by_3(inp: i32) -> Result { - /// let i = u32::try_from(inp).map_err(|_| Errors::Ignored)?; + /// impl std::error::Error for Error {} /// - /// 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)) + /// } + /// }) /// } /// ``` /// /// 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, -- cgit 1.4.1-3-g733a5 From 04ba07bdc3c82aa17297e3e91fdc884983be7cad Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 19:26:35 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 89fde1d509d..50b39cf4ea7 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -374,7 +374,12 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines") + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!("this function has too many lines ({}/{})", line_count, self.max_lines), + ) } } -- cgit 1.4.1-3-g733a5 From 96b31a5b36ed06b6781804d3384a3a84a52b8ce6 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 6 Sep 2020 00:02:35 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 25345f8fa31..b65ae15edcd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,7 +1140,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - let ty = cx.typeck_results().expr_ty(pushed_item); + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); if cx .tcx .lang_items() diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0903a873826..0928820892b 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -100,4 +100,15 @@ fn main() { for _ in 0..10 { vec14.push(std::fs::File::open("foobar").unwrap()); } + // Fix #5979 + #[derive(Clone)] + struct S {} + + trait T {} + impl T for S {} + + let mut vec15: Vec> = Vec::new(); + for _ in 0..10 { + vec15.push(Box::new(S {})); + } } -- cgit 1.4.1-3-g733a5 From 390a13b06c79d4177b829097b06453e38188081f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 23:22:32 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4df6827d77f..ab6e34d201c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,13 +1,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_trait_ref, walk_ty, NestedVisitorMap, + Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, - TyKind, WhereClause, WherePredicate, + ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, + TraitRef, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; @@ -15,7 +16,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; -use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -127,6 +129,14 @@ fn check_fn_inner<'tcx>( return; } + // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not + // support nested elision sites in a fn item. + if FnPointerOrClosureTraitBoundFinder::find_in_generics(cx, generics) + || FnPointerOrClosureTraitBoundFinder::find_in_fn_decl(cx, decl) + { + return; + } + let mut bounds_lts = Vec::new(); let types = generics .params @@ -523,3 +533,54 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { NestedVisitorMap::None } } + +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + +struct FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: bool, +} + +impl<'a, 'tcx> FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + fn find_in_generics(cx: &'a LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> bool { + let mut finder = Self { cx, found: false }; + finder.visit_generics(generics); + finder.found + } + + fn find_in_fn_decl(cx: &'a LateContext<'tcx>, generics: &'tcx FnDecl<'tcx>) -> bool { + let mut finder = Self { cx, found: false }; + finder.visit_fn_decl(generics); + finder.found + } +} + +impl<'a, 'tcx> Visitor<'tcx> for FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_trait_ref(&mut self, tref: &'tcx TraitRef<'tcx>) { + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| tref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + self.found = true; + } + walk_trait_ref(self, tref); + } + + fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { + match ty.kind { + TyKind::BareFn(..) => self.found = true, + TyKind::OpaqueDef(item_id, _) => { + let map = self.cx.tcx.hir(); + let item = map.expect_item(item_id.id); + self.visit_item(item); + }, + _ => (), + } + walk_ty(self, ty); + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index d44854aefe9..9837759fd5e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,6 +42,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; +pub const FN: [&str; 3] = ["core", "ops", "Fn"]; +pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; +pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 913cd004f19..bc725a645ac 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,4 +259,36 @@ mod issue4291 { } } +mod nested_elision_sites { + // Don't lint these cases, they cause FPs. + // The lint does not support nested elision sites. + + fn nested_fn_trait_bound<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + + fn nested_fn_mut_trait_bound<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + + fn nested_fn_once_trait_bound<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + fn nested_generic_fn_trait_bound<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + + fn nested_where_clause_fn_trait_bound<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + + fn nested_pointer_fn<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b5..b1943bf9d70 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -36,12 +36,6 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:86:1 - | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:120:5 | @@ -102,5 +96,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 9fa9208bd5c3725f98dd88f761956c01ce5fd527 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 11:56:54 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 8b00d29acb5..9b6a9075a29 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ use crate::utils; -use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -171,12 +170,22 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, + // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested + // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more + // than one level of references would add some extra complexity as we would have to compensate + // in the closure body. + if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC); + let vec_ty = cx.typeck_results().expr_ty(vec); + if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec + if !matches!(&ty.kind(), ty::Ref(..)); + if utils::is_copy(cx, ty); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 31c2ba0f9c5..ad0d0387db0 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index a3c8ae468ed..9746f6e6849 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } -- cgit 1.4.1-3-g733a5 From 3d30ef7818b9ed562f2c48f3d6d15d90253ae732 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Sep 2020 22:17:31 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f13e369907b..f417e3a0caf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1153,27 +1153,70 @@ fn detect_same_item_push<'tcx>( let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } + match qpath_res(cx, qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + if_chain! { + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + }, + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ), + _ => {}, } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + } else if let ExprKind::Lit(..) = pushed_item.kind { + // literal span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0928820892b..bd4792c4a76 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -1,5 +1,7 @@ #![warn(clippy::same_item_push)] +const VALUE: u8 = 7; + fn mutate_increment(x: &mut u8) -> u8 { *x += 1; *x @@ -111,4 +113,15 @@ fn main() { for _ in 0..10 { vec15.push(Box::new(S {})); } + + let mut vec16 = Vec::new(); + for _ in 0..20 { + vec16.push(VALUE); + } + + let mut vec17 = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec17.push(item); + } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index ddc5d48cd41..4896479791a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,22 +1,14 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:16:9 - | -LL | spaces.push(vec![b' ']); - | ^^^^^^ - | - = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) - -error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:22:9 + --> $DIR/same_item_push.rs:24:9 | LL | vec2.push(item); | ^^^^ | + = note: `-D clippy::same-item-push` implied by `-D warnings` = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:28:9 + --> $DIR/same_item_push.rs:30:9 | LL | vec3.push(item); | ^^^^ @@ -24,12 +16,28 @@ LL | vec3.push(item); = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:33:9 + --> $DIR/same_item_push.rs:35:9 | LL | vec4.push(13); | ^^^^ | = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) -error: aborting due to 4 previous errors +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:119:9 + | +LL | vec16.push(VALUE); + | ^^^^^ + | + = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:125:9 + | +LL | vec17.push(item); + | ^^^^^ + | + = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 619ca76731e7209288fd3ebf1ae243c050486c12 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:25:31 +0900 Subject: Refactoring: use inner function --- clippy_lints/src/loops.rs | 69 +++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f417e3a0caf..c27acdd2236 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1150,8 +1150,6 @@ fn detect_same_item_push<'tcx>( { // Make sure that the push does not involve possibly mutating values if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant @@ -1167,33 +1165,11 @@ fn detect_same_item_push<'tcx>( then { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } _ => {}, @@ -1202,37 +1178,34 @@ fn detect_same_item_push<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } } else if let ExprKind::Lit(..) = pushed_item.kind { // literal - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } } } } + + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } /// Checks for looping over a range and then indexing a sequence with it. -- cgit 1.4.1-3-g733a5 From 5d085ad011a602e004e7d33fa0b9074c7dab36fc Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:34:51 +0900 Subject: Some refactoring --- clippy_lints/src/loops.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c27acdd2236..c59185bd7bd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,10 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + if !matches!(pat.kind, PatKind::Wild) { + return; + } + // Determine whether it is safe to lint the body let mut same_item_push_visitor = SameItemPushVisitor { should_lint: true, @@ -1149,8 +1153,8 @@ fn detect_same_item_push<'tcx>( .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - if let ExprKind::Path(ref qpath) = pushed_item.kind { + match pushed_item.kind { + ExprKind::Path(ref qpath) => { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant Res::Local(hir_id) => { @@ -1161,7 +1165,7 @@ fn detect_same_item_push<'tcx>( if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); let parent_node = cx.tcx.hir().get_parent_node(hir_id); if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + if let Some(init) = parent_let_expr.init; then { match init.kind { // immutable bindings that are initialized with literal @@ -1181,10 +1185,9 @@ fn detect_same_item_push<'tcx>( Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } - } else if let ExprKind::Lit(..) = pushed_item.kind { - // literal - emit_lint(cx, vec, pushed_item); - } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, } } } -- cgit 1.4.1-3-g733a5 From a60e5de90c7370d4fb3e6561d3cb55495cda2e2a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 8 Sep 2020 02:39:39 +0200 Subject: needless-lifetime / nested elision sites / PR remarks --- clippy_lints/src/lifetimes.rs | 222 +++++++-------------- tests/ui/crashes/ice-2774.stderr | 10 + .../crashes/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ab6e34d201c..1d17fbd3725 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,23 +1,22 @@ +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_trait_ref, walk_ty, NestedVisitorMap, - Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, + NestedVisitorMap, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, - TraitRef, Ty, TyKind, WhereClause, WherePredicate, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; - -use crate::utils::paths; -use crate::utils::{get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -110,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } /// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash, Debug, Clone)] enum RefLt { Unnamed, Static, @@ -129,15 +128,6 @@ fn check_fn_inner<'tcx>( return; } - // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not - // support nested elision sites in a fn item. - if FnPointerOrClosureTraitBoundFinder::find_in_generics(cx, generics) - || FnPointerOrClosureTraitBoundFinder::find_in_fn_decl(cx, decl) - { - return; - } - - let mut bounds_lts = Vec::new(); let types = generics .params .iter() @@ -166,13 +156,12 @@ fn check_fn_inner<'tcx>( if bound.name != LifetimeName::Static && !bound.is_elided() { return; } - bounds_lts.push(bound); } } } } } - if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { + if could_use_elision(cx, decl, body, &generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -191,7 +180,6 @@ fn could_use_elision<'tcx>( func: &'tcx FnDecl<'_>, body: Option, named_generics: &'tcx [GenericParam<'_>], - bounds_lts: Vec<&'tcx Lifetime>, ) -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -214,15 +202,31 @@ fn could_use_elision<'tcx>( if let Return(ref ty) = func.output { output_visitor.visit_ty(ty); } + for lt in named_generics { + input_visitor.visit_generic_param(lt) + } - let input_lts = match input_visitor.into_vec() { - Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), - None => return false, - }; - let output_lts = match output_visitor.into_vec() { - Some(val) => val, - None => return false, - }; + if input_visitor.abort() || output_visitor.abort() { + return false; + } + + if allowed_lts + .intersection(&FxHashSet::from_iter( + input_visitor + .nested_elision_site_lts + .iter() + .chain(output_visitor.nested_elision_site_lts.iter()) + .cloned() + .filter(|v| matches!(v, RefLt::Named(_))), + )) + .next() + .is_some() + { + return false; + } + + let input_lts = input_visitor.lts; + let output_lts = output_visitor.lts; if let Some(body_id) = body { let mut checker = BodyLifetimeChecker { @@ -287,27 +291,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { allowed_lts } -fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { - for lt in bounds_lts { - if lt.name != LifetimeName::Static { - vec.push(RefLt::Named(lt.name.ident().name)); - } - } - - vec -} - /// Number of unique lifetimes in the given vector. #[must_use] fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() } +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, - abort: bool, + nested_elision_site_lts: Vec, + unelided_trait_object_lifetime: bool, } impl<'a, 'tcx> RefVisitor<'a, 'tcx> { @@ -315,7 +312,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - abort: false, + nested_elision_site_lts: Vec::new(), + unelided_trait_object_lifetime: false, } } @@ -335,40 +333,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } - fn into_vec(self) -> Option> { - if self.abort { - None - } else { - Some(self.lts) - } + fn all_lts(&self) -> Vec { + self.lts + .iter() + .chain(self.nested_elision_site_lts.iter()) + .cloned() + .collect::>() } - fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { - if let Some(ref last_path_segment) = last_path_segment(qpath).args { - if !last_path_segment.parenthesized - && !last_path_segment - .args - .iter() - .any(|arg| matches!(arg, GenericArg::Lifetime(_))) - { - let hir_id = ty.hir_id; - match self.cx.qpath_res(qpath, hir_id) { - Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => { - let generics = self.cx.tcx.generics_of(def_id); - for _ in generics.params.as_slice() { - self.record(&None); - } - }, - Res::Def(DefKind::Trait, def_id) => { - let trait_def = self.cx.tcx.trait_def(def_id); - for _ in &self.cx.tcx.generics_of(trait_def.def_id).params { - self.record(&None); - } - }, - _ => (), - } - } - } + fn abort(&self) -> bool { + self.unelided_trait_object_lifetime } } @@ -380,30 +354,36 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { self.record(&Some(*lifetime)); } + fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) { + let trait_ref = &poly_tref.trait_ref; + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_trait_ref(trait_ref); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + } else { + walk_poly_trait_ref(self, poly_tref, tbm); + } + } + fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::Rptr(ref lt, _) if lt.is_elided() => { - self.record(&None); - }, - TyKind::Path(ref path) => { - self.collect_anonymous_lifetimes(path, ty); - }, TyKind::OpaqueDef(item, _) => { let map = self.cx.tcx.hir(); - if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { - for bound in exist_ty.bounds { - if let GenericBound::Outlives(_) = *bound { - self.record(&None); - } - } - } else { - unreachable!() - } + let item = map.expect_item(item.id); + walk_item(self, item); walk_ty(self, ty); }, + TyKind::BareFn(&BareFnTy { decl, .. }) => { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_fn_decl(decl); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { - self.abort = true; + self.unelided_trait_object_lifetime = true; } for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); @@ -440,16 +420,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - match visitor.into_vec() { - None => return false, - Some(lts) => { - for lt in lts { - if !allowed_lts.contains(<) { - return true; - } - } - }, - } + return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); @@ -533,54 +504,3 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { NestedVisitorMap::None } } - -const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; - -struct FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: bool, -} - -impl<'a, 'tcx> FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - fn find_in_generics(cx: &'a LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> bool { - let mut finder = Self { cx, found: false }; - finder.visit_generics(generics); - finder.found - } - - fn find_in_fn_decl(cx: &'a LateContext<'tcx>, generics: &'tcx FnDecl<'tcx>) -> bool { - let mut finder = Self { cx, found: false }; - finder.visit_fn_decl(generics); - finder.found - } -} - -impl<'a, 'tcx> Visitor<'tcx> for FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_trait_ref(&mut self, tref: &'tcx TraitRef<'tcx>) { - if CLOSURE_TRAIT_BOUNDS - .iter() - .any(|trait_path| tref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) - { - self.found = true; - } - walk_trait_ref(self, tref); - } - - fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { - match ty.kind { - TyKind::BareFn(..) => self.found = true, - TyKind::OpaqueDef(item_id, _) => { - let map = self.cx.tcx.hir(); - let item = map.expect_item(item_id.id); - self.visit_item(item); - }, - _ => (), - } - walk_ty(self, ty); - } -} diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr new file mode 100644 index 00000000000..fd502cba73a --- /dev/null +++ b/tests/ui/crashes/ice-2774.stderr @@ -0,0 +1,10 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/ice-2774.rs:17:1 + | +LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr new file mode 100644 index 00000000000..02b86397ed5 --- /dev/null +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -0,0 +1,14 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes_impl_trait.rs:17:5 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/needless_lifetimes_impl_trait.rs:3:9 + | +LL | #![deny(clippy::needless_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index b1943bf9d70..d3a360ed8b5 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -36,6 +36,12 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:86:1 + | +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:120:5 | @@ -96,5 +102,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 1d8ae3aa12567b8461436ac10ba9fc4da55adb29 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:12:04 +0900 Subject: Address `items_after_statement` --- clippy_lints/src/loops.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c59185bd7bd..6c54c07869a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,23 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + if !matches!(pat.kind, PatKind::Wild) { return; } @@ -1192,23 +1209,6 @@ fn detect_same_item_push<'tcx>( } } } - - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } } /// Checks for looping over a range and then indexing a sequence with it. -- cgit 1.4.1-3-g733a5 From a899ad2e125e8dd3a0a339df1f8457ef8cab3cc7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Tue, 8 Sep 2020 23:35:19 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index f80a0efa7a5..4002fb655a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { expr.span, "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", - format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + format!("create_dir_all({})", snippet(cx, args[0].span, "..")), Applicability::MaybeIncorrect, ) } diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 0e28f87e33d..8ed53a56ac0 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { // Should be warned - std::fs::create_dir_all("foo"); - std::fs::create_dir_all("bar").unwrap(); + create_dir_all("foo"); + create_dir_all("bar").unwrap(); // Shouldn't be warned create_dir(); diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 1f226298c0d..19c8fc24ba2 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,6 +2,8 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 0c97bdd0f7a..67298fc4709 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,16 +1,16 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:11:5 | LL | std::fs::create_dir("foo"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` | = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:10:5 + --> $DIR/create_dir.rs:12:5 | LL | std::fs::create_dir("bar").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 4f1c4a99d4d98f1acea3c9c7cc55355aa46119aa Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 8 Sep 2020 12:05:18 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 649de4133e0..fa59b6ec370 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` /// - /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to bubble the original error + /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error /// /// **Known problems:** None. /// @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { cx, MAP_ERR_IGNORE, body_span, - "`map_else(|_|...` ignores the original error", + "`map_err(|_|...` ignores the original error", None, "Consider wrapping the error in an enum variant", ); diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 7a269ab95ab..e4e6a289ba0 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,4 +1,4 @@ -error: `map_else(|_|...` ignores the original error +error: `map_err(|_|...` ignores the original error --> $DIR/map_err.rs:21:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); -- cgit 1.4.1-3-g733a5 From d719b485434eac557e65bf55cca79e63f7b83d5b Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 8 Sep 2020 19:37:14 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3794cae091a..797ab62cb54 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1179,6 +1179,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), @@ -1330,7 +1331,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1538,7 +1538,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index fa59b6ec370..5298e16a04d 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -99,7 +99,7 @@ declare_clippy_lint! { /// } /// ``` pub MAP_ERR_IGNORE, - style, + pedantic, "`map_err` should not ignore the original error" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6725c97f793..5105e953162 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1167,7 +1167,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "map_err_ignore", - group: "style", + group: "pedantic", desc: "`map_err` should not ignore the original error", deprecation: None, module: "map_err_ignore", diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index f3a74ad95cd..617b6422872 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,3 +1,4 @@ +#![warn(clippy::map_err_ignore)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index e4e6a289ba0..7273f460380 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` ignores the original error - --> $DIR/map_err.rs:21:32 + --> $DIR/map_err.rs:22:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 7423107e8f9..fa3a99dad9d 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -12,8 +12,10 @@ declare_clippy_lint! { /// `BtreeSet` rely on either the hash or the order of keys be unchanging, /// so having types with interior mutability is a bad idea. /// - /// **Known problems:** We don't currently account for `Rc` or `Arc`, so - /// this may yield false positives. + /// **Known problems:** It's correct to use a struct, that contains interior mutability, + /// as a key; when its `Hash` implementation doesn't access any these interior mutable types. + /// However, this lint is unable to recognise it so cause a false positive. + /// `bytes` ctate is a great example of this. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From 4db10297c1dcd2fc368bd61a24aa0291a6b6c61a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 9 Sep 2020 16:53:45 +0200 Subject: link to the Box docs in related lint documentation. --- clippy_lints/src/types.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c82deaa43b2..550ae2927a8 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -37,6 +37,7 @@ use crate::utils::{ declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` it, you just add another level of indirection @@ -65,6 +66,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. @@ -167,6 +169,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `&Box` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** Any `&Box` can also be a `&T`, which is more /// general. -- cgit 1.4.1-3-g733a5 From de195f2d3d3a2039cb8c4141aa37d060780beaa7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 9 Sep 2020 17:59:13 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 1f9ae8c931a..076ef235b8b 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -9,7 +9,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -51,7 +51,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `result.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -197,7 +197,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", map_type, function_type ) } diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 1312c70b6d5..d7d45ef9b0b 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); @@ -136,7 +136,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing);} diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 467e00263cd..4f3a8c6b792 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:37:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_result_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:47:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:70:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index b23cc608621..88e4efdb0f0 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); @@ -8,7 +8,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:25:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); @@ -16,7 +16,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { @@ -30,7 +30,7 @@ LL | || }); | |_______| | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:33:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); @@ -38,7 +38,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:37:5 | LL | "12".parse::().map(diverge); @@ -46,7 +46,7 @@ LL | "12".parse::().map(diverge); | | | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:43:5 | LL | y.map(do_nothing); -- cgit 1.4.1-3-g733a5 From 3550568a548091ec4ae738827b50a84e9ddf0b8f Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Wed, 9 Sep 2020 14:02:34 -0700 Subject: removing if chain and renaming lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 8 +-- clippy_lints/src/panic_in_result.rs | 97 ------------------------------ clippy_lints/src/panic_in_result_fn.rs | 90 ++++++++++++++++++++++++++++ src/lintlist/mod.rs | 4 +- tests/ui/panic_in_result.rs | 70 ---------------------- tests/ui/panic_in_result.stderr | 105 --------------------------------- tests/ui/panic_in_result_fn.rs | 70 ++++++++++++++++++++++ tests/ui/panic_in_result_fn.stderr | 105 +++++++++++++++++++++++++++++++++ 9 files changed, 272 insertions(+), 279 deletions(-) delete mode 100644 clippy_lints/src/panic_in_result.rs create mode 100644 clippy_lints/src/panic_in_result_fn.rs delete mode 100644 tests/ui/panic_in_result.rs delete mode 100644 tests/ui/panic_in_result.stderr create mode 100644 tests/ui/panic_in_result_fn.rs create mode 100644 tests/ui/panic_in_result_fn.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af3b666cca..d4f0ff4ba78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,7 +1651,7 @@ Released 2018-09-13 [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic -[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b70d126af5b..dbe17e6bbfe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,7 +267,7 @@ mod open_options; mod option_env_unwrap; mod option_if_let_else; mod overflow_check_conditional; -mod panic_in_result; +mod panic_in_result_fn; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; @@ -748,7 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - &panic_in_result::PANIC_IN_RESULT, + &panic_in_result_fn::PANIC_IN_RESULT_FN, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::TODO, @@ -1088,7 +1088,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); - store.register_late_pass(|| box panic_in_result::PanicInResult); + store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { @@ -1132,7 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), - LintId::of(&panic_in_result::PANIC_IN_RESULT), + LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs deleted file mode 100644 index 8b8e211cb72..00000000000 --- a/clippy_lints/src/panic_in_result.rs +++ /dev/null @@ -1,97 +0,0 @@ -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}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **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 result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// fn result_with_panic() -> Result - /// { - /// panic!("error"); - /// } - /// ``` - pub PANIC_IN_RESULT, - restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " -} - -declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); - -impl<'tcx> LateLintPass<'tcx> for PanicInResult { - fn check_fn( - &mut self, - cx: &LateContext<'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 - { - lint_impl_body(cx, span, body); - } - } - } -} - -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() - || 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); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; - panics.visit_expr(&body.value); - if !panics.result.is_empty() { - 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/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs new file mode 100644 index 00000000000..4077aba6ef1 --- /dev/null +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -0,0 +1,90 @@ +use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::Expr; +use rustc_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!`, `todo!` or `unreachable!` in a function of type result. + /// + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn result_with_panic() -> Result + /// { + /// panic!("error"); + /// } + /// ``` + pub PANIC_IN_RESULT_FN, + restriction, + "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " +} + +declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); + +impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + span: Span, + hir_id: hir::HirId, + ) { + 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); + } + } +} + +struct FindPanicUnimplementedUnreachable { + result: Vec, +} + +impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if ["unimplemented", "unreachable", "panic", "todo"] + .iter() + .any(|fun| is_expn_of(expr.span, fun).is_some()) + { + self.result.push(expr.span); + } + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { + let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; + panics.visit_expr(&body.value); + if !panics.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT_FN, + 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/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1f56c56f081..4d6c45761e6 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1705,11 +1705,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "panic_unimplemented", }, Lint { - name: "panic_in_result", + name: "panic_in_result_fn", group: "restriction", desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ", deprecation: None, - module: "panic_in_result", + module: "panic_in_result_fn", }, Lint { name: "panic_params", diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs deleted file mode 100644 index f6fb2f1ab61..00000000000 --- a/tests/ui/panic_in_result.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![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 result_with_todo() -> Result // should emit lint - { - todo!("Finish this"); - } - - 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 other_with_todo() // should not emit lint - { - todo!("finish this") - } - - fn result_without_banned_functions() -> Result // should not emit lint - { - Ok(true) - } -} - -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 deleted file mode 100644 index 9faedf82986..00000000000 --- a/tests/ui/panic_in_result.stderr +++ /dev/null @@ -1,105 +0,0 @@ -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 -LL | | { -LL | | panic!("error"); -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` 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` - --> $DIR/panic_in_result.rs:11:5 - | -LL | / fn result_with_unimplemented() -> Result // should emit lint -LL | | { -LL | | unimplemented!(); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -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` - --> $DIR/panic_in_result.rs:16:5 - | -LL | / fn result_with_unreachable() -> Result // should emit lint -LL | | { -LL | | unreachable!(); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -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` - --> $DIR/panic_in_result.rs:21:5 - | -LL | / fn result_with_todo() -> Result // should emit lint -LL | | { -LL | | todo!("Finish this"); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -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: 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 - diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs new file mode 100644 index 00000000000..287726f7a2d --- /dev/null +++ b/tests/ui/panic_in_result_fn.rs @@ -0,0 +1,70 @@ +#![warn(clippy::panic_in_result_fn)] + +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 result_with_todo() -> Result // should emit lint + { + todo!("Finish this"); + } + + 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 other_with_todo() // should not emit lint + { + todo!("finish this") + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + Ok(true) + } +} + +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_fn.stderr b/tests/ui/panic_in_result_fn.stderr new file mode 100644 index 00000000000..c6936fd8692 --- /dev/null +++ b/tests/ui/panic_in_result_fn.stderr @@ -0,0 +1,105 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:6:5 + | +LL | / fn result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:8:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:11:5 + | +LL | / fn result_with_unimplemented() -> Result // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $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_fn.rs:16:5 + | +LL | / fn result_with_unreachable() -> Result // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $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_fn.rs:21:5 + | +LL | / fn result_with_todo() -> Result // should emit lint +LL | | { +LL | | todo!("Finish this"); +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $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_fn.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_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_fn.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_fn.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 + -- cgit 1.4.1-3-g733a5 From 6211599ccafee5b2429bfb1bbeb48ead32a48484 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:00:31 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 2d964ac2b9f..748f45f47c2 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for usage of invalid atomic - /// ordering in atomic loads/stores and memory fences. + /// ordering in atomic loads/stores/exchanges and memory fences /// /// **Why is this bad?** Using an invalid atomic ordering /// will cause a panic at run-time. @@ -29,10 +29,13 @@ declare_clippy_lint! { /// /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); + /// + /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); + /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, - "usage of invalid atomic ordering in atomic loads/stores and memory fences" + "usage of invalid atomic ordering in atomic loads/stores/exchanges ane memory fences" } declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); @@ -127,9 +130,84 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option { + if let ExprKind::Path(ref ord_qpath) = ord_arg.kind { + cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id() + } else { + None + } +} +fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + let method = method_path.ident.name.as_str(); + if type_is_atomic(cx, &args[0]); + if method == "compare_exchange" || method == "compare_exchange_weak"; + let failure_order_arg = &args[4]; + if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); + then { + // Helper type holding on to some checking and error reporting data. Has + // - (success ordering name, + // - list of failure orderings forbidden by the success order, + // - suggestion message) + type OrdLintInfo = (&'static str, &'static [&'static str], &'static str); + let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`"); + let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`"); + let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`"); + let release = ("Release", relaxed.1, relaxed.2); + let acqrel = ("AcqRel", acquire.1, acquire.2); + let search = [relaxed, acquire, seq_cst, release, acqrel]; + + let success_lint_info = opt_ordering_defid(cx, &args[3]) + .and_then(|success_ord_def_id| -> Option { + search + .iter() + .find(|(ordering, ..)| { + match_def_path(cx, success_ord_def_id, + &["core", "sync", "atomic", "Ordering", ordering]) + }) + .copied() + }); + + if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) { + // If we don't know the success order is, use what we'd suggest + // if it were maximally permissive. + let suggested = success_lint_info.unwrap_or(seq_cst).2; + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + failure_order_arg.span, + &format!( + "{}'s failure ordering may not be `Release` or `AcqRel`", + method, + ), + None, + &format!("consider using {} instead", suggested), + ); + } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info { + if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) { + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + failure_order_arg.span, + &format!( + "{}'s failure ordering may not stronger than the success ordering of `{}`", + method, + success_ord_name, + ), + None, + &format!("consider using {} instead", suggested), + ); + } + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for AtomicOrdering { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { check_atomic_load_store(cx, expr); check_memory_fence(cx, expr); + check_atomic_compare_exchange(cx, expr); } } diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs new file mode 100644 index 00000000000..2f60a98f037 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange.rs @@ -0,0 +1,84 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn main() { + // `compare_exchange` (not weak) testing + let x = AtomicUsize::new(0); + + // Allowed ordering combos + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); + + // compare_exchange_weak tests + + // Allowed ordering combos + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); +} diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr new file mode 100644 index 00000000000..26ec15be518 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -0,0 +1,259 @@ +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:21:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:22:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:23:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:24:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:25:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:28:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:29:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:30:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:31:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:32:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:35:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:36:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:39:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:40:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange.rs:43:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:44:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:60:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:61:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:62:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:63:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:64:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:67:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:68:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:69:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:70:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:71:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:74:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:75:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:78:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:79:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange.rs:82:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:83:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 32 previous errors + -- cgit 1.4.1-3-g733a5 From 61671a2268903e1b5c28fcb3c713c27e84ea3e9b Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:12:14 -0700 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 748f45f47c2..ff2c281ec9d 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -8,7 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for usage of invalid atomic - /// ordering in atomic loads/stores/exchanges and memory fences + /// ordering in atomic loads/stores/exchanges/updates and + /// memory fences. /// /// **Why is this bad?** Using an invalid atomic ordering /// will cause a panic at run-time. @@ -32,10 +33,11 @@ declare_clippy_lint! { /// /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); + /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val ^ val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, - "usage of invalid atomic ordering in atomic loads/stores/exchanges ane memory fences" + "usage of invalid atomic ordering in atomic operations and memory fences" } declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); @@ -142,8 +144,12 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); - if method == "compare_exchange" || method == "compare_exchange_weak"; - let failure_order_arg = &args[4]; + if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update"; + let (success_order_arg, failure_order_arg) = if method == "fetch_update" { + (&args[1], &args[2]) + } else { + (&args[3], &args[4]) + }; if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); then { // Helper type holding on to some checking and error reporting data. Has @@ -158,7 +164,7 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { let acqrel = ("AcqRel", acquire.1, acquire.2); let search = [relaxed, acquire, seq_cst, release, acqrel]; - let success_lint_info = opt_ordering_defid(cx, &args[3]) + let success_lint_info = opt_ordering_defid(cx, success_order_arg) .and_then(|success_ord_def_id| -> Option { search .iter() diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index dff19ef440f..5dead1b9b69 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -923,7 +923,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "invalid_atomic_ordering", group: "correctness", - desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences", + desc: "usage of invalid atomic ordering in atomic operations and memory fences", deprecation: None, module: "atomic_ordering", }, diff --git a/tests/ui/atomic_ordering_fetch_update.rs b/tests/ui/atomic_ordering_fetch_update.rs new file mode 100644 index 00000000000..550bdb001e4 --- /dev/null +++ b/tests/ui/atomic_ordering_fetch_update.rs @@ -0,0 +1,45 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicIsize, Ordering}; + +fn main() { + // `fetch_update` testing + let x = AtomicIsize::new(0); + + // Allowed ordering combos + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old + 1)); + + // AcqRel is always forbidden as a failure ordering + let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); + + // Release is always forbidden as a failure ordering + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); +} diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr new file mode 100644 index 00000000000..362e104a244 --- /dev/null +++ b/tests/ui/atomic_ordering_fetch_update.stderr @@ -0,0 +1,131 @@ +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:21:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:22:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:23:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:24:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:25:46 + | +LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:28:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:29:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:30:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:31:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:32:46 + | +LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_fetch_update.rs:35:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_fetch_update.rs:36:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_fetch_update.rs:39:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_fetch_update.rs:40:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_fetch_update.rs:43:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:44:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 16 previous errors + -- cgit 1.4.1-3-g733a5 From b65745545f410b31c5ecdd33a8299a65da578af2 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:40:43 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index ff2c281ec9d..d4e070b0c24 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -18,22 +18,22 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,no_run - /// # use std::sync::atomic::{self, AtomicBool, Ordering}; + /// # use std::sync::atomic::{self, AtomicU8, Ordering}; /// - /// let x = AtomicBool::new(true); + /// let x = AtomicU8::new(0); /// /// let _ = x.load(Ordering::Release); /// let _ = x.load(Ordering::AcqRel); /// - /// x.store(false, Ordering::Acquire); - /// x.store(false, Ordering::AcqRel); + /// x.store(1, Ordering::Acquire); + /// x.store(2, Ordering::AcqRel); /// /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); /// - /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); - /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); - /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val ^ val)); + /// let _ = x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::SeqCst); + /// let _ = x.compare_exchange_weak(2, 3, Ordering::SeqCst, Ordering::Release); + /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val + val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, -- cgit 1.4.1-3-g733a5 From 4b5326b0d62104801f0b33c4d8f3749d93eebc02 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 23:12:57 -0700 Subject: Address small review comments --- clippy_lints/src/atomic_ordering.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index d4e070b0c24..614e33a06da 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -139,6 +139,7 @@ fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option None } } + fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; @@ -197,7 +198,7 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { INVALID_ATOMIC_ORDERING, failure_order_arg.span, &format!( - "{}'s failure ordering may not stronger than the success ordering of `{}`", + "{}'s failure ordering may not be stronger than the success ordering of `{}`", method, success_ord_name, ), -- cgit 1.4.1-3-g733a5 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: fix some use of `snippet` in `types.rs` --- clippy_lints/src/types.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6c6188d61ad..b6d405cca77 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -321,14 +321,15 @@ impl Types { if let Some(def_id) = res.opt_def_id() { if Some(def_id) == cx.tcx.lang_items().owned_box() { if let Some(span) = match_borrows_parameter(cx, qpath) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Box<&T>`", "try", - snippet(cx, span, "..").to_string(), - Applicability::MachineApplicable, + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, ); return; // don't recurse into the type } @@ -345,14 +346,15 @@ impl Types { } } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), - Applicability::MachineApplicable, + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, ); return; // don't recurse into the type } @@ -368,26 +370,31 @@ impl Types { GenericArg::Type(ty) => ty.span, _ => return, }; + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - format!("Rc<{}>", snippet(cx, inner_span, "..")), - Applicability::MachineApplicable, + format!( + "Rc<{}>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + applicability, ); return; // don't recurse into the type } if let Some(span) = match_borrows_parameter(cx, qpath) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc<&T>`", "try", - snippet(cx, span, "..").to_string(), - Applicability::MachineApplicable, + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, ); return; // don't recurse into the type } @@ -546,7 +553,6 @@ impl Types { // details. return; } - let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, BORROWED_BOX, @@ -556,8 +562,12 @@ impl Types { format!( "&{}{}", ltopt, - &snippet_with_applicability(cx, inner.span, "..", &mut applicability) + &snippet(cx, inner.span, "..") ), + // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item + // because the trait impls of it will break otherwise; + // and there may be other cases that result in invalid code. + // For example, type coercion doesn't work nicely. Applicability::Unspecified, ); return; // don't recurse into the type -- cgit 1.4.1-3-g733a5 From 2d56512580919483268c3e3bf2e028c8614805f2 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Sep 2020 14:18:05 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 2b6ddadd4c1..fb891866364 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -21,11 +21,8 @@ declare_clippy_lint! { "assignments to temporaries" } -fn is_temporary(_cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Struct(..) | ExprKind::Tup(..) => true, - _ => false, - } +fn is_temporary(expr: &Expr<'_>) -> bool { + matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..)) } declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]); @@ -37,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment { while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { base = f; } - if is_temporary(cx, base) && !is_adjusted(cx, base) { + if is_temporary(base) && !is_adjusted(cx, base) { span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary"); } } diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index d6f56d40c5d..ac4c1bc6597 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; @@ -54,11 +53,6 @@ fn main() { ArrayStruct { array: [0] }.array[0] = 1; (0, 0).0 = 1; - A.0 = 2; - B.field = 2; - C.structure.field = 2; - D.array[0] = 2; - // no error s.field = 1; t.0 = 1; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f05..7d79901a28d 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From f1775f05b7a20c83dd018bc716e1eb5578cfceb0 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 10 Sep 2020 22:39:08 +0900 Subject: Fix typo --- clippy_lints/src/await_holding_lock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index f18e7e5d997..367534499fd 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -10,7 +10,7 @@ declare_clippy_lint! { /// **What it does:** Checks for calls to await while holding a /// non-async-aware MutexGuard. /// - /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot + /// **Why is this bad?** The Mutex types found in std::sync and parking_lot /// are not designed to operate in an async context across await points. /// /// There are two potential solutions. One is to use an asynx-aware Mutex -- cgit 1.4.1-3-g733a5 From 36a864854f41dfd25c45fa4881812bd005bd5054 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 10 Sep 2020 17:08:10 +0200 Subject: Fix spelling of "Known problems section" of `interior_mutable_key` --- clippy_lints/src/mut_key.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index fa3a99dad9d..0826ad0ab55 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -12,10 +12,10 @@ declare_clippy_lint! { /// `BtreeSet` rely on either the hash or the order of keys be unchanging, /// so having types with interior mutability is a bad idea. /// - /// **Known problems:** It's correct to use a struct, that contains interior mutability, - /// as a key; when its `Hash` implementation doesn't access any these interior mutable types. - /// However, this lint is unable to recognise it so cause a false positive. - /// `bytes` ctate is a great example of this. + /// **Known problems:** It's correct to use a struct, that contains interior mutability + /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types. + /// However, this lint is unable to recognize this, so it causes a false positive in theses cases. + /// The `bytes` crate is a great example of this. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From 09f7a377a663043c6f63ded70436ac0969e4abc7 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Thu, 10 Sep 2020 14:50:10 -0700 Subject: Add comments to the invalid_atomic_ordering example --- clippy_lints/src/atomic_ordering.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 614e33a06da..703d8a6f62b 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -22,18 +22,27 @@ declare_clippy_lint! { /// /// let x = AtomicU8::new(0); /// + /// // Bad: `Release` and `AcqRel` cannot be used for `load`. /// let _ = x.load(Ordering::Release); /// let _ = x.load(Ordering::AcqRel); /// + /// // Bad: `Acquire` and `AcqRel` cannot be used for `store`. /// x.store(1, Ordering::Acquire); /// x.store(2, Ordering::AcqRel); /// + /// // Bad: `Relaxed` cannot be used as a fence's ordering. /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); /// - /// let _ = x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::SeqCst); - /// let _ = x.compare_exchange_weak(2, 3, Ordering::SeqCst, Ordering::Release); - /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val + val)); + /// // Bad: `Release` and `AcqRel` are both always invalid + /// // for the failure ordering (the last arg). + /// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release); + /// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel); + /// + /// // Bad: The failure ordering is not allowed to be + /// // stronger than the success order, and `SeqCst` is + /// // stronger than `Relaxed`. + /// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, -- cgit 1.4.1-3-g733a5 From 2487f8f461134f93459415642f730ac6c4a2d659 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 11 Sep 2020 16:52:25 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ba69c8266b1..98e027b6d22 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3374,7 +3374,7 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_ INTO_ITER_ON_REF, method_span, &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`", + "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", method_name, kind, ), "call directly", diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 1cd6400b019..28003b365bb 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,4 +1,4 @@ -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() @@ -6,157 +6,157 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() | = note: `-D clippy::into-iter-on-ref` implied by `-D warnings` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:15:46 | LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:16:41 | LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:17:44 | LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:19:32 | LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:20:36 | LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:21:40 | LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option` --> $DIR/into_iter_on_ref.rs:23:24 | LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option` --> $DIR/into_iter_on_ref.rs:24:28 | LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result` --> $DIR/into_iter_on_ref.rs:25:32 | LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result` --> $DIR/into_iter_on_ref.rs:26:37 | LL | let _ = (&mut Err::(7)).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:27:34 | LL | let _ = (&Vec::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:28:38 | LL | let _ = (&mut Vec::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap` --> $DIR/into_iter_on_ref.rs:29:44 | LL | let _ = (&BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap` --> $DIR/into_iter_on_ref.rs:30:48 | LL | let _ = (&mut BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque` --> $DIR/into_iter_on_ref.rs:31:39 | LL | let _ = (&VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque` --> $DIR/into_iter_on_ref.rs:32:43 | LL | let _ = (&mut VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList` --> $DIR/into_iter_on_ref.rs:33:41 | LL | let _ = (&LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList` --> $DIR/into_iter_on_ref.rs:34:45 | LL | let _ = (&mut LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap` --> $DIR/into_iter_on_ref.rs:35:43 | LL | let _ = (&HashMap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap` --> $DIR/into_iter_on_ref.rs:36:47 | LL | let _ = (&mut HashMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet` --> $DIR/into_iter_on_ref.rs:38:39 | LL | let _ = (&BTreeSet::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap` --> $DIR/into_iter_on_ref.rs:39:41 | LL | let _ = (&BinaryHeap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet` --> $DIR/into_iter_on_ref.rs:40:38 | LL | let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path` --> $DIR/into_iter_on_ref.rs:41:43 | LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf` --> $DIR/into_iter_on_ref.rs:42:47 | LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:44:26 | LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() -- cgit 1.4.1-3-g733a5 From 7ba1a8fec42ca11c169bcff3650f9c1e108b6743 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 13 Sep 2020 12:42:14 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 615440e15f3..4e4a206a583 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), None, "consider removing `.try_into()`", ); @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), None, &hint, ); @@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), &sugg_msg, sugg.to_string(), Applicability::MachineApplicable, // snippet diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index f1e880d2696..11c6efb25cc 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,61 +10,61 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: useless conversion to the same type +error: useless conversion to the same type: `i32` --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: useless conversion to the same type +error: useless conversion to the same type: `std::str::Lines` --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: useless conversion to the same type +error: useless conversion to the same type: `i32` --> $DIR/useless_conversion.rs:71:13 | LL | let _ = i32::from(a + b) * 3; diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index b765727c168..2e0d9129bfb 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,4 +1,4 @@ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion_try.rs:6:13 | LL | let _ = T::try_from(val).unwrap(); @@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider removing `T::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion_try.rs:7:5 | LL | val.try_into().unwrap() @@ -19,7 +19,7 @@ LL | val.try_into().unwrap() | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:29:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); @@ -27,7 +27,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); @@ -35,7 +35,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | = help: consider removing `TryFrom::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); @@ -43,7 +43,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); | = help: consider removing `String::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); @@ -51,7 +51,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | = help: consider removing `String::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:33:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); @@ -59,7 +59,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:34:21 | LL | let _: String = "".to_owned().try_into().unwrap(); @@ -67,7 +67,7 @@ LL | let _: String = "".to_owned().try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:35:27 | LL | let _: String = match String::from("_").try_into() { -- cgit 1.4.1-3-g733a5 From 9ff7e5d98413d10dd74e3b9509c7a58f6dd6818b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 13 Sep 2020 23:23:16 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 81a34021e8a..a4ee54076ee 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -90,7 +90,7 @@ declare_clippy_lint! { /// if x & 0b1111 == 0 { } /// ``` pub VERBOSE_BIT_MASK, - style, + pedantic, "expressions where a bit mask is less readable than the corresponding method call" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1795fd10fa1..c017c5cb5d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1157,6 +1157,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), @@ -1254,7 +1255,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::USELESS_ATTRIBUTE), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&booleans::LOGIC_BUG), @@ -1512,7 +1512,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), - LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 04d486438b1..a7d38c93433 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2637,7 +2637,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "verbose_bit_mask", - group: "style", + group: "pedantic", desc: "expressions where a bit mask is less readable than the corresponding method call", deprecation: None, module: "bit_mask", diff --git a/tests/ui/trailing_zeros.rs b/tests/ui/trailing_zeros.rs index 1cef8c2cfc9..fbdc977b769 100644 --- a/tests/ui/trailing_zeros.rs +++ b/tests/ui/trailing_zeros.rs @@ -1,4 +1,5 @@ #![allow(unused_parens)] +#![warn(clippy::verbose_bit_mask)] fn main() { let x: i32 = 42; diff --git a/tests/ui/trailing_zeros.stderr b/tests/ui/trailing_zeros.stderr index 320d9cc3f64..79855111830 100644 --- a/tests/ui/trailing_zeros.stderr +++ b/tests/ui/trailing_zeros.stderr @@ -1,5 +1,5 @@ error: bit mask could be simplified with a call to `trailing_zeros` - --> $DIR/trailing_zeros.rs:5:13 + --> $DIR/trailing_zeros.rs:6:13 | LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4` @@ -7,7 +7,7 @@ LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` error: bit mask could be simplified with a call to `trailing_zeros` - --> $DIR/trailing_zeros.rs:6:13 + --> $DIR/trailing_zeros.rs:7:13 | LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5` -- cgit 1.4.1-3-g733a5 From d1f0f04a488d027fdf91e08cdf25df00fb677205 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 14 Sep 2020 06:11:35 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 285a2ff8060..a6fafdf5357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1672,6 +1672,7 @@ Released 2018-09-13 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c017c5cb5d0..38ddc69c8cb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod macro_use; mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; +mod manual_strip; mod map_clone; mod map_identity; mod map_unit_fn; @@ -626,6 +627,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &manual_strip::MANUAL_STRIP, &map_clone::MAP_CLONE, &map_identity::MAP_IDENTITY, &map_unit_fn::OPTION_MAP_UNIT_FN, @@ -1109,6 +1111,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_late_pass(|| box manual_strip::ManualStrip); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1335,6 +1338,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1626,6 +1630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), + LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs new file mode 100644 index 00000000000..127938aecd6 --- /dev/null +++ b/clippy_lints/src/manual_strip.rs @@ -0,0 +1,246 @@ +use crate::consts::{constant, Constant}; +use crate::utils::usage::mutated_variables; +use crate::utils::{ + eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, +}; + +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::BinOpKind; +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using + /// the pattern's length. + /// + /// **Why is this bad?** + /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no + /// slicing which may panic and the compiler does not need to insert this panic code. It is + /// also sometimes more readable as it removes the need for duplicating or storing the pattern + /// used by `str::{starts,ends}_with` and in the slicing. + /// + /// **Known problems:** + /// None. + /// + /// **Example:** + /// + /// ```rust + /// let s = "hello, world!"; + /// if s.starts_with("hello, ") { + /// assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + /// } + /// ``` + /// Use instead: + /// ```rust + /// let s = "hello, world!"; + /// if let Some(end) = s.strip_prefix("hello, ") { + /// assert_eq!(end.to_uppercase(), "WORLD!"); + /// } + /// ``` + pub MANUAL_STRIP, + complexity, + "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing" +} + +declare_lint_pass!(ManualStrip => [MANUAL_STRIP]); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum StripKind { + Prefix, + Suffix, +} + +impl<'tcx> LateLintPass<'tcx> for ManualStrip { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let Some((cond, then, _)) = higher::if_block(&expr); + if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id); + if let ExprKind::Path(target_path) = &target_arg.kind; + then { + let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { + StripKind::Prefix + } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { + StripKind::Suffix + } else { + return; + }; + let target_res = qpath_res(cx, &target_path, target_arg.hir_id); + if target_res == Res::Err { + return; + }; + + if_chain! { + if let Res::Local(hir_id) = target_res; + if let Some(used_mutably) = mutated_variables(then, cx); + if used_mutably.contains(&hir_id); + then { + return; + } + } + + let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); + if !strippings.is_empty() { + + let kind_word = match strip_kind { + StripKind::Prefix => "prefix", + StripKind::Suffix => "suffix", + }; + + let test_span = expr.span.until(then.span); + span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| { + diag.span_note(test_span, &format!("the {} was tested here", kind_word)); + multispan_sugg( + diag, + &format!("try using the `strip_{}` method", kind_word), + vec![(test_span, + format!("if let Some() = {}.strip_{}({}) ", + snippet(cx, target_arg.span, ".."), + kind_word, + snippet(cx, pattern.span, "..")))] + .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), + ) + }); + } + } + } + } +} + +// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. +fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, method_def_id, &paths::STR_LEN); + then { + Some(arg) + } + else { + None + } + } +} + +// Returns the length of the `expr` if it's a constant string or char. +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + let (value, _) = constant(cx, cx.typeck_results(), expr)?; + match value { + Constant::Str(value) => Some(value.len() as u128), + Constant::Char(value) => Some(value.len_utf8() as u128), + _ => None, + } +} + +// Tests if `expr` equals the length of the pattern. +fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Lit(Spanned { + node: LitKind::Int(n, _), + .. + }) = expr.kind + { + constant_length(cx, pattern).map_or(false, |length| length == n) + } else { + len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg)) + } +} + +// Tests if `expr` is a `&str`. +fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.typeck_results().expr_ty_adjusted(&expr).kind() { + ty::Ref(_, ty, _) => ty.is_str(), + _ => false, + } +} + +// Removes the outer `AddrOf` expression if needed. +fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> { + if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind { + unref + } else { + expr + } +} + +// Find expressions where `target` is stripped using the length of `pattern`. +// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` +// method. +fn find_stripping<'tcx>( + cx: &LateContext<'tcx>, + strip_kind: StripKind, + target: Res, + pattern: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) -> Vec { + struct StrippingFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + strip_kind: StripKind, + target: Res, + pattern: &'tcx Expr<'tcx>, + results: Vec, + } + + impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + if_chain! { + if is_ref_str(self.cx, ex); + let unref = peel_ref(ex); + if let ExprKind::Index(indexed, index) = &unref.kind; + if let Some(range) = higher::range(index); + if let higher::Range { start, end, .. } = range; + if let ExprKind::Path(path) = &indexed.kind; + if qpath_res(self.cx, path, ex.hir_id) == self.target; + then { + match (self.strip_kind, start, end) { + (StripKind::Prefix, Some(start), None) => { + if eq_pattern_length(self.cx, self.pattern, start) { + self.results.push(ex.span); + return; + } + }, + (StripKind::Suffix, None, Some(end)) => { + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; + if let Some(left_arg) = len_arg(self.cx, left); + if let ExprKind::Path(left_path) = &left_arg.kind; + if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target; + if eq_pattern_length(self.cx, self.pattern, right); + then { + self.results.push(ex.span); + return; + } + } + }, + _ => {} + } + } + } + + walk_expr(self, ex); + } + } + + let mut finder = StrippingFinder { + cx, + strip_kind, + target, + pattern, + results: vec![], + }; + walk_expr(&mut finder, expr); + finder.results +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 65320d6a0e0..f0f7719e2fd 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -115,6 +115,9 @@ pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; +pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7d38c93433..8bceef80abf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1144,6 +1144,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "manual_strip", + group: "complexity", + desc: "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing", + deprecation: None, + module: "manual_strip", + }, Lint { name: "manual_swap", group: "complexity", diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs new file mode 100644 index 00000000000..d1b4772c7de --- /dev/null +++ b/tests/ui/manual_strip.rs @@ -0,0 +1,59 @@ +#![warn(clippy::manual_strip)] + +fn main() { + let s = "abc"; + + if s.starts_with("ab") { + str::to_string(&s["ab".len()..]); + s["ab".len()..].to_string(); + + str::to_string(&s[2..]); + s[2..].to_string(); + } + + if s.ends_with("bc") { + str::to_string(&s[..s.len() - "bc".len()]); + s[..s.len() - "bc".len()].to_string(); + + str::to_string(&s[..s.len() - 2]); + s[..s.len() - 2].to_string(); + } + + // Character patterns + if s.starts_with('a') { + str::to_string(&s[1..]); + s[1..].to_string(); + } + + // Variable prefix + let prefix = "ab"; + if s.starts_with(prefix) { + str::to_string(&s[prefix.len()..]); + } + + // Constant prefix + const PREFIX: &str = "ab"; + if s.starts_with(PREFIX) { + str::to_string(&s[PREFIX.len()..]); + str::to_string(&s[2..]); + } + + // Constant target + const TARGET: &str = "abc"; + if TARGET.starts_with(prefix) { + str::to_string(&TARGET[prefix.len()..]); + } + + // String target - not mutated. + let s1: String = "abc".into(); + if s1.starts_with("ab") { + s1[2..].to_uppercase(); + } + + // String target - mutated. (Don't lint.) + let mut s2: String = "abc".into(); + if s2.starts_with("ab") { + s2.push('d'); + s2[2..].to_uppercase(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr new file mode 100644 index 00000000000..1352a8713d4 --- /dev/null +++ b/tests/ui/manual_strip.stderr @@ -0,0 +1,132 @@ +error: stripping a prefix manually + --> $DIR/manual_strip.rs:7:24 + | +LL | str::to_string(&s["ab".len()..]); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/manual_strip.rs:6:5 + | +LL | if s.starts_with("ab") { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("ab") { +LL | str::to_string(); +LL | .to_string(); +LL | +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a suffix manually + --> $DIR/manual_strip.rs:15:24 + | +LL | str::to_string(&s[..s.len() - "bc".len()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the suffix was tested here + --> $DIR/manual_strip.rs:14:5 + | +LL | if s.ends_with("bc") { + | ^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_suffix` method + | +LL | if let Some() = s.strip_suffix("bc") { +LL | str::to_string(); +LL | .to_string(); +LL | +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:24:24 + | +LL | str::to_string(&s[1..]); + | ^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:23:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix('a') { +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:31:24 + | +LL | str::to_string(&s[prefix.len()..]); + | ^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:30:5 + | +LL | if s.starts_with(prefix) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix(prefix) { +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:37:24 + | +LL | str::to_string(&s[PREFIX.len()..]); + | ^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:36:5 + | +LL | if s.starts_with(PREFIX) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix(PREFIX) { +LL | str::to_string(); +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:44:24 + | +LL | str::to_string(&TARGET[prefix.len()..]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:43:5 + | +LL | if TARGET.starts_with(prefix) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = TARGET.strip_prefix(prefix) { +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:50:9 + | +LL | s1[2..].to_uppercase(); + | ^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:49:5 + | +LL | if s1.starts_with("ab") { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s1.strip_prefix("ab") { +LL | .to_uppercase(); + | + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 15244a88df5cfd475df010ad945474c658749192 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 14 Sep 2020 06:11:35 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 50121a054c7..62bb70af06e 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -534,7 +534,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { return false; } - let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s }; + let s = s.strip_suffix('s').unwrap_or(s); s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6c54c07869a..8f5675a61b9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2601,11 +2601,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont span, NEEDLESS_COLLECT_MSG, |diag| { - let (arg, pred) = if contains_arg.starts_with('&') { - ("x", &contains_arg[1..]) - } else { - ("&x", &*contains_arg) - }; + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); diag.span_suggestion( span, "replace with", diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 02789735c17..9cb1cfb915d 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -377,8 +377,8 @@ impl EarlyLintPass for MiscEarlyLints { if let PatKind::Ident(_, ident, None) = arg.pat.kind { let arg_name = ident.to_string(); - if arg_name.starts_with('_') { - if let Some(correspondence) = registered_names.get(&arg_name[1..]) { + if let Some(arg_name) = arg_name.strip_prefix('_') { + if let Some(correspondence) = registered_names.get(arg_name) { span_lint( cx, DUPLICATE_UNDERSCORE_ARGUMENT, @@ -386,7 +386,7 @@ impl EarlyLintPass for MiscEarlyLints { &format!( "`{}` already exists, having another argument having almost the same \ name makes code comprehension and documentation more difficult", - arg_name[1..].to_owned() + arg_name ), ); } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 57a45e628db..1a7f36fbdad 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -239,10 +239,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { ); let mut app = Applicability::MaybeIncorrect; - let mut call_snip = &snip[dot + 1..]; + let call_snip = &snip[dot + 1..]; // Machine applicable when `call_snip` looks like `foobar()` - if call_snip.ends_with("()") { - call_snip = call_snip[..call_snip.len()-2].trim(); + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { app = Applicability::MachineApplicable; } diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 802beeb4be6..32a67f181df 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -33,6 +33,7 @@ fn issue985_alt() -> i32 { x } +#[allow(clippy::manual_strip)] fn issue975() -> String { let mut udn = "dummy".to_string(); if udn.starts_with("uuid:") { diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index c53a63a541b..7de560c7348 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:63:5 + --> $DIR/let_if_seq.rs:64:5 | LL | / let mut foo = 0; LL | | if f() { @@ -11,7 +11,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:68:5 + --> $DIR/let_if_seq.rs:69:5 | LL | / let mut bar = 0; LL | | if f() { @@ -25,7 +25,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:76:5 + --> $DIR/let_if_seq.rs:77:5 | LL | / let quz; LL | | if f() { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:105:5 + --> $DIR/let_if_seq.rs:106:5 | LL | / let mut baz = 0; LL | | if f() { -- cgit 1.4.1-3-g733a5 From 1b5317f68b2b55803d5051e9945f9a33817fccef Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Sun, 13 Sep 2020 21:52:25 -0600 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 285a2ff8060..8922f5e7027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1775,6 +1775,7 @@ Released 2018-09-13 [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c017c5cb5d0..239eeb10bb4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -837,6 +837,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, &types::UNIT_ARG, @@ -1804,6 +1805,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(&transmute::USELESS_TRANSMUTE), + LintId::of(&types::RC_BUFFER), LintId::of(&use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b6d405cca77..5f3a2e0b6d4 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -215,11 +215,41 @@ declare_clippy_lint! { "redundant allocation" } +declare_clippy_lint! { + /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// + /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since + /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// + /// While mutating a buffer type would still be possible with Rc::get_mut(), it only + /// works if there are no additional references yet, which defeats the purpose of + /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner + /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// be used. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// # use std::rc::Rc; + /// fn foo(interned: Rc) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// fn foo(interned: Rc) { ... } + /// ``` + pub RC_BUFFER, + nursery, + "shared ownership of a buffer type" +} + pub struct Types { vec_box_size_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { @@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str]) None } +fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { + if match_type_parameter(cx, qpath, &paths::STRING).is_some() { + return Some("str"); + } + if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() { + return Some("std::ffi::OsStr"); + } + if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() { + return Some("std::path::Path"); + } + None +} + fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); if_chain! { @@ -385,6 +428,45 @@ impl Types { ); return; // don't recurse into the type } + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc` when T is a buffer type", + "try", + format!("Rc<{}>", alternate), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc` when T is a buffer type", + "try", + format!( + "Rc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } if let Some(span) = match_borrows_parameter(cx, qpath) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 65320d6a0e0..2df11d2efcf 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -113,6 +113,7 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; +pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7d38c93433..d0c6a1d63d9 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1851,6 +1851,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "ranges", }, + Lint { + name: "rc_buffer", + group: "nursery", + desc: "shared ownership of a buffer type", + deprecation: None, + module: "types", + }, Lint { name: "redundant_allocation", group: "perf", diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs new file mode 100644 index 00000000000..c8c2bec67ee --- /dev/null +++ b/tests/ui/rc_buffer.rs @@ -0,0 +1,13 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; + +#[warn(clippy::rc_buffer)] +struct S { + a: Rc, + b: Rc, + c: Rc>, + d: Rc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr new file mode 100644 index 00000000000..641a13a2251 --- /dev/null +++ b/tests/ui/rc_buffer.stderr @@ -0,0 +1,28 @@ +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:7:8 + | +LL | a: Rc, + | ^^^^^^^^^^ help: try: `Rc` + | + = note: `-D clippy::rc-buffer` implied by `-D warnings` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:8:8 + | +LL | b: Rc, + | ^^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:9:8 + | +LL | c: Rc>, + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:10:8 + | +LL | d: Rc, + | ^^^^^^^^^^^^ help: try: `Rc` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 2dd7175d60e070c7ee2b4609bdb17eae16e381f0 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Sun, 13 Sep 2020 22:17:10 -0600 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5f3a2e0b6d4..da04d07885b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -480,6 +480,46 @@ impl Types { ); return; // don't recurse into the type } + } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc` when T is a buffer type", + "try", + format!("Arc<{}>", alternate), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc` when T is a buffer type", + "try", + format!( + "Arc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) { if_chain! { // Get the _ part of Vec<_> diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs new file mode 100644 index 00000000000..a878b0ab336 --- /dev/null +++ b/tests/ui/rc_buffer_arc.rs @@ -0,0 +1,13 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::sync::Arc; + +#[warn(clippy::rc_buffer)] +struct S { + a: Arc, + b: Arc, + c: Arc>, + d: Arc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr new file mode 100644 index 00000000000..c4b01621046 --- /dev/null +++ b/tests/ui/rc_buffer_arc.stderr @@ -0,0 +1,28 @@ +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:7:8 + | +LL | a: Arc, + | ^^^^^^^^^^^ help: try: `Arc` + | + = note: `-D clippy::rc-buffer` implied by `-D warnings` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:8:8 + | +LL | b: Arc, + | ^^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:9:8 + | +LL | c: Arc>, + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:10:8 + | +LL | d: Arc, + | ^^^^^^^^^^^^^ help: try: `Arc` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 4d73ccaa9419b393b9c94f977ec0e158897feeb3 Mon Sep 17 00:00:00 2001 From: Haraman Johal Date: Tue, 15 Sep 2020 00:20:31 +0100 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index d4a50dd9013..81e6214f143 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -411,16 +411,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if !is_comparing_arrays { diag.span_suggestion( expr.span, - "consider comparing them within some error", + "consider comparing them within some margin of error", format!( - "({}).abs() {} error", + "({}).abs() {} error_margin", lhs - rhs, if op == BinOpKind::Eq { '<' } else { '>' } ), Applicability::HasPlaceholders, // snippet ); } - diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`"); + diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index 2d454e8e70d..f7c380fc915 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -2,34 +2,34 @@ error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:65:5 | LL | ONE as f64 != 2.0; - | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error` + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` | = note: `-D clippy::float-cmp` implied by `-D warnings` - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:70:5 | LL | x == 1.0; - | ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:73:5 | LL | twice(x) != twice(ONE as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:93:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays --> $DIR/float_cmp.rs:98:5 @@ -37,15 +37,15 @@ error: strict comparison of `f32` or `f64` arrays LL | a1 == a2; | ^^^^^^^^ | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:99:5 | LL | a1[0] == a2[0]; - | ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error` + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 6 previous errors diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr index 19dc4a284b7..5d0455363e8 100644 --- a/tests/ui/float_cmp_const.stderr +++ b/tests/ui/float_cmp_const.stderr @@ -2,58 +2,58 @@ error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:20:5 | LL | 1f32 == ONE; - | ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error` + | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` | = note: `-D clippy::float-cmp-const` implied by `-D warnings` - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:21:5 | LL | TWO == ONE; - | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error` + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:22:5 | LL | TWO != ONE; - | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error` + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:23:5 | LL | ONE + ONE == TWO; - | ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error` + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:25:5 | LL | x as f32 == ONE; - | ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error` + | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:28:5 | LL | v == ONE; - | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:29:5 | LL | v != ONE; - | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant arrays --> $DIR/float_cmp_const.rs:61:5 @@ -61,7 +61,7 @@ error: strict comparison of `f32` or `f64` constant arrays LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From fd151f5135def6ffae7530f17ff0c458239f209d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 15 Sep 2020 08:51:52 +0900 Subject: Remove an extra blank line in `shadow_same` --- clippy_lints/src/shadow.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 087d50c90e6..ef4a54735a4 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -25,7 +25,6 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # let x = 1; - /// /// // Bad /// let x = &x; /// -- cgit 1.4.1-3-g733a5 From 8afa7ed6aec37c5423cffe12dbc854c954799fb3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:32:48 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 44afd7e82fe..b60a80e07d7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -867,6 +867,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, @@ -1112,6 +1113,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1240,6 +1242,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(&utils::internal_lints::PRODUCE_ICE), ]); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 8fa5d22210a..5beb4be5b29 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,6 +1,6 @@ use crate::utils::{ - is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, + is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, + snippet, span_lint, span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; @@ -206,6 +206,29 @@ declare_clippy_lint! { "found collapsible `span_lint_and_then` calls" } +declare_clippy_lint! { + /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item + /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// + /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Good: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type)) + /// ``` + pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + internal, + "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -652,3 +675,89 @@ fn suggest_note( Applicability::MachineApplicable, ); } + +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); + +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) { + return; + } + + if_chain! { + // Check if this is a call to utils::match_type() + if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; + if let ExprKind::Path(fn_qpath) = &fn_path.kind; + if match_qpath(&fn_qpath, &["utils", "match_type"]); + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, ty_path); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id()); + // Check if the matched type is a diagnostic item + let diag_items = cx.tcx.diagnostic_items(ty_did.krate); + if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); + then { + let cx_snippet = snippet(cx, context.span, "_"); + let ty_snippet = snippet(cx, ty.span, "_"); + + span_lint_and_sugg( + cx, + MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + expr.span, + "usage of `utils::match_type() on a type diagnostic item`", + "try", + format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), + Applicability::MaybeIncorrect, + ); + } + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + use rustc_hir::ItemKind; + + match &expr.kind { + ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), + ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { + if let Some(init) = local.init { + return path_to_matched_type(cx, init); + } + } + }, + Res::Def(DefKind::Const | DefKind::Static, def_id) => { + if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { + if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { + let body = cx.tcx.hir().body(body_id); + return path_to_matched_type(cx, &body.value); + } + } + }, + _ => {}, + }, + ExprKind::Array(exprs) => { + let segments: Vec = exprs + .iter() + .filter_map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some(sym.as_str()); + } + } + + None + }) + .collect(); + + if segments.len() == exprs.len() { + return Some(segments); + } + }, + _ => {}, + } + + None +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3ebbfed6456..8db7e693e62 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -130,6 +130,9 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { } /// Checks if type is struct, enum or union type with the given def path. +/// +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { match ty.kind() { ty::Adt(adt, _) => match_def_path(cx, adt.did, path), @@ -138,6 +141,8 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } /// Checks if the type is equal to a diagnostic item +/// +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), diff --git a/tests/ui/match_type_on_diag_item.rs b/tests/ui/match_type_on_diag_item.rs new file mode 100644 index 00000000000..fe950b0aa7c --- /dev/null +++ b/tests/ui/match_type_on_diag_item.rs @@ -0,0 +1,50 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; + +mod paths { + pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; +} + +mod utils { + use super::*; + + pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { + false + } +} + +use utils::match_type; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +static OPTION: [&str; 3] = ["core", "option", "Option"]; + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let ty = cx.typeck_results().expr_ty(expr); + + let _ = match_type(cx, ty, &paths::VEC); + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + let rc_path = &["alloc", "rc", "Rc"]; + let _ = utils::match_type(cx, ty, rc_path); + } +} + +fn main() {} diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr new file mode 100644 index 00000000000..c89137eb758 --- /dev/null +++ b/tests/ui/match_type_on_diag_item.stderr @@ -0,0 +1,33 @@ +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:41:17 + | +LL | let _ = match_type(cx, ty, &paths::VEC); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | +note: the lint level is defined here + --> $DIR/match_type_on_diag_item.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:42:17 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:43:17 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:46:17 + | +LL | let _ = utils::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 332c2dcb4dfe5151d7c8d44cdf96c4abd06db593 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:48:04 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6c54c07869a..3d2fd0eee85 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1114,7 +1114,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type)); if path.ident.name.as_str() == "push"; then { return Some((self_expr, pushed_item)) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 98e027b6d22..de966cccd11 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1808,7 +1808,7 @@ fn lint_or_fun_call<'tcx>( _ => (), } - if match_type(cx, ty, &paths::VEC) { + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { return; } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9494efe736c..60e5e7bfed3 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,6 +1,6 @@ use crate::utils; use crate::utils::sugg::Sugg; -use crate::utils::{match_type, paths, span_lint_and_sugg}; +use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -73,7 +73,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" - && match_type(cx, &cx.typeck_results().expr_ty(&receiver), &paths::RESULT) + && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type)) } else { false } -- cgit 1.4.1-3-g733a5 From c86a7e5f38e54f4404cc71874316eb0b4dba200b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:57:24 +0200 Subject: Misc doc updates --- clippy_lints/src/utils/diagnostics.rs | 9 +++++++++ doc/common_tools_writing_lints.md | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index e4e65b5f4d4..0a58231558e 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -51,6 +51,8 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// The `note` message is presented separately from the main lint message /// and is attached to a specific span: /// +/// If you change the signature, remember to update the internal lint `CollapsibleCalls` +/// /// # Example /// /// ```ignore @@ -126,6 +130,7 @@ pub fn span_lint_and_note<'a, T: LintContext>( /// Like `span_lint` but allows to add notes, help and suggestions using a closure. /// /// If you need to customize your lint output a lot, use this function. +/// If you change the signature, remember to update the internal lint `CollapsibleCalls` pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) where F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), @@ -168,6 +173,10 @@ pub fn span_lint_hir_and_then( /// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x > /// 2)"`. /// +/// If you change the signature, remember to update the internal lint `CollapsibleCalls` +/// +/// # Example +/// /// ```ignore /// error: This `.fold` can be more succinctly expressed as `.any` /// --> $DIR/methods.rs:390:13 diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 9dd4c8a5f7a..53c3d084dbc 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -60,7 +60,7 @@ impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, _args) = &expr.kind; + if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind; // Check the name of this method is `some_method` if path.ident.name == sym!(some_method); then { -- cgit 1.4.1-3-g733a5 From d0b5663d30457d1a9ec4f98eb9baff7123b77172 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 15 Sep 2020 10:31:50 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 5beb4be5b29..f201494a024 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -704,7 +704,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.span, - "usage of `utils::match_type() on a type diagnostic item`", + "usage of `utils::match_type()` on a type diagnostic item", "try", format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr index c89137eb758..5e5fe9e3a3e 100644 --- a/tests/ui/match_type_on_diag_item.stderr +++ b/tests/ui/match_type_on_diag_item.stderr @@ -1,4 +1,4 @@ -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); @@ -11,19 +11,19 @@ LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:43:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:46:17 | LL | let _ = utils::match_type(cx, ty, rc_path); -- cgit 1.4.1-3-g733a5 From 44eb66d947191be51f0a7b7d35c05936a4142a97 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 16 Sep 2020 00:25:00 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ef4a54735a4..225fe58906f 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -74,7 +74,9 @@ declare_clippy_lint! { /// names to bindings or introducing more scopes to contain the bindings. /// /// **Known problems:** This lint, as the other shadowing related lints, - /// currently only catches very simple patterns. + /// currently only catches very simple patterns. Note that + /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level + /// for this lint. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From 16b6cebaa67655a793d90d296c03e2c5496ec4ce Mon Sep 17 00:00:00 2001 From: Haraman Johal Date: Tue, 15 Sep 2020 17:29:41 +0100 Subject: update lint docs --- clippy_lints/src/misc.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 81e6214f143..67a3685fd0d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -99,11 +99,11 @@ declare_clippy_lint! { /// if y != x {} // where both are floats /// /// // Good - /// let error = f64::EPSILON; // Use an epsilon for comparison + /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. - /// // let error = std::f64::EPSILON; - /// if (y - 1.23f64).abs() < error { } - /// if (y - x).abs() > error { } + /// // let error_margin = std::f64::EPSILON; + /// if (y - 1.23f64).abs() < error_margin { } + /// if (y - x).abs() > error_margin { } /// ``` pub FLOAT_CMP, correctness, @@ -242,10 +242,10 @@ declare_clippy_lint! { /// if x == ONE { } // where both are floats /// /// // Good - /// let error = f64::EPSILON; // Use an epsilon for comparison + /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. - /// // let error = std::f64::EPSILON; - /// if (x - ONE).abs() < error { } + /// // let error_margin = std::f64::EPSILON; + /// if (x - ONE).abs() < error_margin { } /// ``` pub FLOAT_CMP_CONST, restriction, -- cgit 1.4.1-3-g733a5 From 79a0e5110a0168945d43e334e6dc0d12e0bc1487 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 15 Sep 2020 21:25:03 +0200 Subject: manual-strip: Fix formatting --- clippy_lints/src/manual_strip.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 127938aecd6..4afb0ab3bad 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -124,8 +124,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E if match_def_path(cx, method_def_id, &paths::STR_LEN); then { Some(arg) - } - else { + } else { None } } -- cgit 1.4.1-3-g733a5 From 6ba36bcfd3802d9520d0ac48dabfe6dc06d8dc82 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 22 Aug 2020 00:43:04 +0200 Subject: option_if_let_else - distinguish pure from impure else expressions --- clippy_lints/src/methods/bind_instead_of_map.rs | 21 ++-- clippy_lints/src/methods/mod.rs | 66 ++++------- clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 498f12518f8..ae37942e55a 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -12,6 +12,7 @@ use rustc_middle::hir::map::Map; use rustc_span::Span; pub(crate) struct OptionAndThenSome; + impl BindInsteadOfMap for OptionAndThenSome { const TYPE_NAME: &'static str = "Option"; const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; @@ -24,6 +25,7 @@ impl BindInsteadOfMap for OptionAndThenSome { } pub(crate) struct ResultAndThenOk; + impl BindInsteadOfMap for ResultAndThenOk { const TYPE_NAME: &'static str = "Result"; const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; @@ -36,6 +38,7 @@ impl BindInsteadOfMap for ResultAndThenOk { } pub(crate) struct ResultOrElseErrInfo; + impl BindInsteadOfMap for ResultOrElseErrInfo { const TYPE_NAME: &'static str = "Result"; const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; @@ -120,9 +123,9 @@ pub(crate) trait BindInsteadOfMap { } } - fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); - let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if_chain! { if !in_macro(ret_expr.span); if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; @@ -153,12 +156,13 @@ pub(crate) trait BindInsteadOfMap { ) }); } + can_sugg } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) { - return; + return false; } match args[1].kind { @@ -166,8 +170,10 @@ pub(crate) trait BindInsteadOfMap { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); - if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { - Self::lint_closure(cx, expr, closure_expr); + if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + true + } else { + Self::lint_closure(cx, expr, closure_expr) } }, // `_.and_then(Some)` case, which is no-op. @@ -181,8 +187,9 @@ pub(crate) trait BindInsteadOfMap { snippet(cx, args[0].span, "..").into(), Applicability::MachineApplicable, ); + true }, - _ => {}, + _ => false, } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 98e027b6d22..914f9f94e77 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -25,14 +25,15 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; +use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, - last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, - method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, + is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, + match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, + single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, + walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1454,18 +1455,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); - bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); - bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and"); + } }, ["or_else", ..] => { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); - bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or"); + } }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), @@ -1508,9 +1512,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), - ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), - ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), - ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), _ => {}, } @@ -1714,37 +1718,6 @@ fn lint_or_fun_call<'tcx>( name: &str, args: &'tcx [hir::Expr<'_>], ) { - // Searches an expression for method calls or function calls that aren't ctors - struct FunCallFinder<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: bool, - } - - impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - let call_found = match &expr.kind { - // ignore enum and struct constructors - hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), - hir::ExprKind::MethodCall(..) => true, - _ => false, - }; - - if call_found { - self.found |= true; - } - - if !self.found { - intravisit::walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - } - /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. fn check_unwrap_or_default( cx: &LateContext<'_>, @@ -1825,8 +1798,7 @@ fn lint_or_fun_call<'tcx>( if_chain! { if know_types.iter().any(|k| k.2.contains(&name)); - let mut finder = FunCallFinder { cx: &cx, found: false }; - if { finder.visit_expr(&arg); finder.found }; + if is_lazyness_candidate(cx, arg); if !contains_return(&arg); let self_ty = cx.typeck_results().expr_ty(self_expr); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 31517659c34..08b3eab9b7c 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,78 +1,17 @@ -use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; -use if_chain::if_chain; +use crate::utils::{eager_or_lazy, usage}; +use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use super::UNNECESSARY_LAZY_EVALUATIONS; -// Return true if the expression is an accessor of any of the arguments -fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { - params.iter().any(|arg| { - if_chain! { - if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; - if let [p, ..] = path.segments; - then { - ident.name == p.ident.name - } else { - false - } - } - }) -} - -fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { - paths.iter().any(|candidate| match_qpath(path, candidate)) -} - -fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { - match expr.kind { - // Closures returning literals can be unconditionally simplified - hir::ExprKind::Lit(_) => true, - - hir::ExprKind::Index(ref object, ref index) => { - // arguments are not being indexed into - if expr_uses_argument(object, params) { - false - } else { - // arguments are not used as index - !expr_uses_argument(index, params) - } - }, - - // Reading fields can be simplified if the object is not an argument of the closure - hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), - - // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), - - // Calls to Some, Ok, Err can be considered literals if they don't derive an argument - hir::ExprKind::Call(ref func, ref args) => if_chain! { - if variant_calls; // Disable lint when rules conflict with bind_instead_of_map - if let hir::ExprKind::Path(ref path) = func.kind; - if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); - then { - // Recursively check all arguments - args.iter().all(|arg| can_simplify(arg, params, variant_calls)) - } else { - false - } - }, - - // For anything more complex than the above, a closure is probably the right solution, - // or the case is handled by an other lint - _ => false, - } -} - /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` pub(super) fn lint<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], - allow_variant_calls: bool, simplify_using: &str, ) { let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); @@ -81,10 +20,13 @@ pub(super) fn lint<'tcx>( if is_option || is_result { if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; + let body_expr = &body.value; + + if usage::BindingUsageFinder::are_params_used(cx, body) { + return; + } - if can_simplify(ex, params, allow_variant_calls) { + if eager_or_lazy::is_eagerness_candidate(cx, body_expr) { let msg = if is_option { "unnecessary closure used to substitute value for `Option::None`" } else { @@ -101,7 +43,7 @@ pub(super) fn lint<'tcx>( "{0}.{1}({2})", snippet(cx, args[0].span, ".."), simplify_using, - snippet(cx, ex.span, ".."), + snippet(cx, body_expr.span, ".."), ), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9494efe736c..5e2652b48cb 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,4 +1,5 @@ use crate::utils; +use crate::utils::eager_or_lazy; use crate::utils::sugg::Sugg; use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; @@ -13,22 +14,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more - /// idiomatically done with `Option::map_or` (if the else bit is a simple - /// expression) or `Option::map_or_else` (if the else bit is a longer - /// block). + /// idiomatically done with `Option::map_or` (if the else bit is a pure + /// expression) or `Option::map_or_else` (if the else bit is an impure + /// expresion). /// /// **Why is this bad?** /// Using the dedicated functions of the Option type is clearer and /// more concise than an if let expression. /// /// **Known problems:** - /// This lint uses whether the block is just an expression or if it has - /// more statements to decide whether to use `Option::map_or` or - /// `Option::map_or_else`. If you have a single expression which calls - /// an expensive function, then it would be more efficient to use - /// `Option::map_or_else`, but this lint would suggest `Option::map_or`. - /// - /// Also, this lint uses a deliberately conservative metric for checking + /// This lint uses a deliberately conservative metric for checking /// if the inside of either body contains breaks or continues which will /// cause it to not suggest a fix if either block contains a loop with /// continues or breaks contained within the loop. @@ -92,6 +87,7 @@ struct OptionIfLetElseOccurence { struct ReturnBreakContinueMacroVisitor { seen_return_break_continue: bool, } + impl ReturnBreakContinueMacroVisitor { fn new() -> ReturnBreakContinueMacroVisitor { ReturnBreakContinueMacroVisitor { @@ -99,6 +95,7 @@ impl ReturnBreakContinueMacroVisitor { } } } + impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -157,7 +154,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { } /// If this is the else body of an if/else expression, then we need to wrap -/// it in curcly braces. Otherwise, we don't. +/// it in curly braces. Otherwise, we don't. fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if let Some(Expr { @@ -199,7 +196,10 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo /// If this expression is the option if let/else construct we're detecting, then /// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn detect_option_if_let_else<'tcx>( + cx: &'_ LateContext<'tcx>, + expr: &'_ Expr<'tcx>, +) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; @@ -214,10 +214,7 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option "map_or_else", - _ => "map_or", - }; + let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); let wrap_braces = should_wrap_in_braces(cx, expr); let (as_ref, as_mut) = match &cond_expr.kind { @@ -243,8 +240,8 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option LateLintPass<'a> for OptionIfLetElse { - fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(detection) = detect_option_if_let_else(cx, expr) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs new file mode 100644 index 00000000000..6938d9971d9 --- /dev/null +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -0,0 +1,128 @@ +//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa. +//! +//! Things to consider: +//! - has the expression side-effects? +//! - is the expression computationally expensive? +//! +//! See lints: +//! - unnecessary-lazy-evaluations +//! - or-fun-call +//! - option-if-let-else + +use crate::utils::is_ctor_or_promotable_const_function; +use rustc_hir::def::{DefKind, Res}; + +use rustc_hir::intravisit; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; + +use rustc_hir::{Block, Expr, ExprKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +/// Is the expr pure (is it free from side-effects)? +/// This function is named so to stress that it isn't exhaustive and returns FNs. +fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true, + ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr), + ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)), + ExprKind::Struct(_, fields, expr) => { + fields.iter().all(|f| identify_some_pure_patterns(f.expr)) + && expr.map_or(true, |e| identify_some_pure_patterns(e)) + }, + ExprKind::Call( + &Expr { + kind: + ExprKind::Path(QPath::Resolved( + _, + Path { + res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..), + .. + }, + )), + .. + }, + args, + ) => args.iter().all(|expr| identify_some_pure_patterns(expr)), + ExprKind::Block( + &Block { + stmts, + expr: Some(expr), + .. + }, + _, + ) => stmts.is_empty() && identify_some_pure_patterns(expr), + ExprKind::Box(..) + | ExprKind::Array(..) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Index(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) + | ExprKind::Err => false, + } +} + +/// Identify some potentially computationally expensive patterns. +/// This function is named so to stress that its implementation is non-exhaustive. +/// It returns FNs and FPs. +fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + // Searches an expression for method calls or function calls that aren't ctors + struct FunCallFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: bool, + } + + impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + let call_found = match &expr.kind { + // ignore enum and struct constructors + ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + ExprKind::MethodCall(..) => true, + _ => false, + }; + + if call_found { + self.found |= true; + } + + if !self.found { + intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + } + + let mut finder = FunCallFinder { cx, found: false }; + finder.visit_expr(expr); + finder.found +} + +pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr) +} + +pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + identify_some_potentially_expensive_patterns(cx, expr) +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3ebbfed6456..7efeef62690 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -10,6 +10,7 @@ pub mod comparisons; pub mod conf; pub mod constants; mod diagnostics; +pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod inspector; diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 811fde388d1..ec8b7e59b59 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -49,7 +49,7 @@ impl<'a> Sugg<'a> { /// Convenience function around `hir_opt` for suggestions with a default /// text. pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { - Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default))) + Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default))) } /// Same as `hir`, but it adapts the applicability level by following rules: diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 4a64b935ac9..ea1dc3be29b 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,6 +1,8 @@ use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; @@ -108,3 +110,67 @@ pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool { walk_expr(&mut visitor, body); !visitor.used } + +pub struct ParamBindingIdCollector { + binding_hir_ids: Vec, +} +impl<'tcx> ParamBindingIdCollector { + fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_body(body); + finder.binding_hir_ids + } +} +impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { + type Map = Map<'tcx>; + + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + self.binding_hir_ids.push(hir_id); + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +pub struct BindingUsageFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + binding_ids: Vec, + usage_found: bool, +} +impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { + pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool { + let mut finder = BindingUsageFinder { + cx, + binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body), + usage_found: false, + }; + finder.visit_body(body); + finder.usage_found + } +} +impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if !self.usage_found { + intravisit::walk_expr(self, expr); + } + } + + fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { + if let hir::def::Res::Local(id) = path.res { + if self.binding_ids.contains(&id) { + self.usage_found = true; + } + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 695a460cc4e..a7fb00a2705 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::option_if_let_else)] +#![allow(clippy::redundant_closure)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) @@ -36,6 +37,14 @@ fn longer_body(arg: Option) -> u32 { }) } +fn impure_else(arg: Option) { + let side_effect = || { + println!("return 1"); + 1 + }; + let _ = arg.map_or_else(|| side_effect(), |x| x); +} + fn test_map_or_else(arg: Option) { let _ = arg.map_or_else(|| { let mut y = 1; @@ -71,4 +80,5 @@ fn main() { let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); + let _ = impure_else(None); } diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index dee80d26bd9..895fd86321f 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::option_if_let_else)] +#![allow(clippy::redundant_closure)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { @@ -52,6 +53,19 @@ fn longer_body(arg: Option) -> u32 { } } +fn impure_else(arg: Option) { + let side_effect = || { + println!("return 1"); + 1 + }; + let _ = if let Some(x) = arg { + x + } else { + // map_or_else must be suggested + side_effect() + }; +} + fn test_map_or_else(arg: Option) { let _ = if let Some(x) = arg { x * x * x * x @@ -89,4 +103,5 @@ fn main() { let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); + let _ = impure_else(None); } diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 7005850efaf..b69fe767682 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:5:5 + --> $DIR/option_if_let_else.rs:6:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:15:12 + --> $DIR/option_if_let_else.rs:16:12 | LL | } else if let Some(x) = string { | ____________^ @@ -22,19 +22,19 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:23:13 + --> $DIR/option_if_let_else.rs:24:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:26:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -54,13 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:31:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:33:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -80,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:38:13 + --> $DIR/option_if_let_else.rs:39:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -100,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:47:5 + --> $DIR/option_if_let_else.rs:48:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -119,7 +119,19 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:56:13 + --> $DIR/option_if_let_else.rs:61:13 + | +LL | let _ = if let Some(x) = arg { + | _____________^ +LL | | x +LL | | } else { +LL | | // map_or_else must be suggested +LL | | side_effect() +LL | | }; + | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:70:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -142,10 +154,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:85:13 + --> $DIR/option_if_let_else.rs:99:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index fa66e68794e..4980c111499 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -2,6 +2,7 @@ #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] +#![allow(clippy::map_identity)] struct Deep(Option); @@ -34,13 +35,13 @@ fn main() { let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); - let _ = opt.unwrap_or(ext_arr[0]); + let _ = opt.unwrap_or_else(|| ext_arr[0]); let _ = opt.and(ext_opt); let _ = opt.or(ext_opt); let _ = opt.or(None); let _ = opt.get_or_insert(2); let _ = opt.ok_or(2); - let _ = opt.ok_or(ext_arr[0]); + let _ = nested_tuple_opt.unwrap_or(Some((1, 2))); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or(2); @@ -60,7 +61,6 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = nested_opt.unwrap_or_else(|| Some(some_call())); - let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); @@ -69,13 +69,16 @@ fn main() { let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); + let _ = opt.ok_or_else(|| ext_arr[0]); - // These are handled by bind_instead_of_map + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); - let _: Option = None.or_else(|| Some(3)); - let _ = deep.0.or_else(|| Some(3)); - let _ = opt.or_else(|| Some(3)); + + // should lint, bind_instead_of_map doesn't apply + let _: Option = None.or(Some(3)); + let _ = deep.0.or(Some(3)); + let _ = opt.or(Some(3)); // Should lint - Result let res: Result = Err(5); @@ -92,26 +95,27 @@ fn main() { let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + // should not lint, bind_instead_of_map takes priority let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); - - let _: Result = res.or_else(|err| Ok(err)); let _: Result = res.or_else(|err| Err(err)); - // These are handled by bind_instead_of_map let _: Result = res.and_then(|_| Ok(2)); let _: Result = res.and_then(|_| Ok(astronomers_pi)); let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); - - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); let _: Result = res.or_else(|_| Err(astronomers_pi)); let _: Result = res.or_else(|_| Err(ext_str.some_field)); + + // should lint, bind_instead_of_map doesn't apply + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); + + let _: Result = res.and(Err(2)); + let _: Result = res.and(Err(astronomers_pi)); + let _: Result = res.and(Err(ext_str.some_field)); + + let _: Result = res.or(Ok(2)); + let _: Result = res.or(Ok(astronomers_pi)); + let _: Result = res.or(Ok(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 04f47d1aa29..0b270939ec2 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -2,6 +2,7 @@ #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] +#![allow(clippy::map_identity)] struct Deep(Option); @@ -40,7 +41,7 @@ fn main() { let _ = opt.or_else(|| None); let _ = opt.get_or_insert_with(|| 2); let _ = opt.ok_or_else(|| 2); - let _ = opt.ok_or_else(|| ext_arr[0]); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or_else(|| 2); @@ -60,7 +61,6 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = nested_opt.unwrap_or_else(|| Some(some_call())); - let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); @@ -69,10 +69,13 @@ fn main() { let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); + let _ = opt.ok_or_else(|| ext_arr[0]); - // These are handled by bind_instead_of_map + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); + + // should lint, bind_instead_of_map doesn't apply let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); @@ -92,17 +95,22 @@ fn main() { let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + // should not lint, bind_instead_of_map takes priority let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); - - let _: Result = res.or_else(|err| Ok(err)); let _: Result = res.or_else(|err| Err(err)); - // These are handled by bind_instead_of_map let _: Result = res.and_then(|_| Ok(2)); let _: Result = res.and_then(|_| Ok(astronomers_pi)); let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); + + // should lint, bind_instead_of_map doesn't apply + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.and_then(|_| Err(2)); let _: Result = res.and_then(|_| Err(astronomers_pi)); let _: Result = res.and_then(|_| Err(ext_str.some_field)); @@ -110,8 +118,4 @@ fn main() { let _: Result = res.or_else(|_| Ok(2)); let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 5c1b2eb1f14..1cf7ac46346 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,142 +7,190 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 - | -LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` - -error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:39:13 + --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:13 + --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:44:13 | -LL | let _ = opt.ok_or_else(|| ext_arr[0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` +LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:47:13 + --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:28 + --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:35 + --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:28 + --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:56:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:59:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:79:28 + | +LL | let _: Option = None.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(Some(3))` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:80:13 + | +LL | let _ = deep.0.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(Some(3))` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:81:13 + | +LL | let _ = opt.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(Some(3))` + error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:84:13 + --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:85:13 + --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:86:13 + --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` -error: aborting due to 24 previous errors +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:114:35 + | +LL | let _: Result = res.and_then(|_| Err(2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:115:35 + | +LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:116:35 + | +LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:118:35 + | +LL | let _: Result = res.or_else(|_| Ok(2)); + | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:119:35 + | +LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:120:35 + | +LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` + +error: aborting due to 32 previous errors -- cgit 1.4.1-3-g733a5 From 0261e341fd435f0b42954510fb436cb0b289cdac Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 14 Sep 2020 14:58:39 -0400 Subject: {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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index e653240d049..fac63bcb993 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -322,11 +322,15 @@ impl EarlyLintPass for Write { } /// Given a format string that ends in a newline and its span, calculates the span of the -/// newline. +/// newline, or the format string itself if the format string consists solely of a newline. fn newline_span(fmtstr: &StrLit) -> Span { let sp = fmtstr.span; let contents = &fmtstr.symbol.as_str(); + if *contents == r"\n" { + return sp; + } + let newline_sp_hi = sp.hi() - match fmtstr.style { StrStyle::Cooked => BytePos(1), diff --git a/tests/ui/print_with_newline.rs b/tests/ui/print_with_newline.rs index 3f710540e90..a43a1fc4f52 100644 --- a/tests/ui/print_with_newline.rs +++ b/tests/ui/print_with_newline.rs @@ -9,6 +9,7 @@ fn main() { print!("Hello {}\n", "world"); print!("Hello {} {}\n", "world", "#2"); print!("{}\n", 1265); + print!("\n"); // these are all fine print!(""); diff --git a/tests/ui/print_with_newline.stderr b/tests/ui/print_with_newline.stderr index 05fe88915d6..54b3ad75b31 100644 --- a/tests/ui/print_with_newline.stderr +++ b/tests/ui/print_with_newline.stderr @@ -44,7 +44,18 @@ LL | println!("{}", 1265); | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:30:5 + --> $DIR/print_with_newline.rs:12:5 + | +LL | print!("/n"); + | ^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!(); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:31:5 | LL | print!("//n"); // should fail | ^^^^^^^^^^^^^^ @@ -55,7 +66,7 @@ LL | println!("/"); // should fail | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:37:5 + --> $DIR/print_with_newline.rs:38:5 | LL | / print!( LL | | " @@ -70,7 +81,7 @@ LL | "" | error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:41:5 + --> $DIR/print_with_newline.rs:42:5 | LL | / print!( LL | | r" @@ -85,7 +96,7 @@ LL | r"" | error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:49:5 + --> $DIR/print_with_newline.rs:50:5 | LL | print!("/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^ @@ -96,7 +107,7 @@ LL | println!("/r"); //~ ERROR | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:50:5 + --> $DIR/print_with_newline.rs:51:5 | LL | print!("foo/rbar/n") // ~ ERROR | ^^^^^^^^^^^^^^^^^^^^ @@ -106,5 +117,5 @@ help: use `println!` instead LL | println!("foo/rbar") // ~ ERROR | ^^^^^^^ -- -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index 93afd73d111..1c1b1b58402 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -14,6 +14,7 @@ fn main() { write!(&mut v, "Hello {}\n", "world"); write!(&mut v, "Hello {} {}\n", "world", "#2"); write!(&mut v, "{}\n", 1265); + write!(&mut v, "\n"); // These should be fine write!(&mut v, ""); diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 2473329ca72..a14e86122ee 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -44,7 +44,18 @@ LL | writeln!(&mut v, "{}", 1265); | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:35:5 + --> $DIR/write_with_newline.rs:17:5 + | +LL | write!(&mut v, "/n"); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, ); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:36:5 | LL | write!(&mut v, "//n"); // should fail | ^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +66,7 @@ LL | writeln!(&mut v, "/"); // should fail | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:42:5 + --> $DIR/write_with_newline.rs:43:5 | LL | / write!( LL | | &mut v, @@ -72,7 +83,7 @@ LL | "" | error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:47:5 + --> $DIR/write_with_newline.rs:48:5 | LL | / write!( LL | | &mut v, @@ -89,7 +100,7 @@ LL | r"" | error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:56:5 + --> $DIR/write_with_newline.rs:57:5 | LL | write!(&mut v, "/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +111,7 @@ LL | writeln!(&mut v, "/r"); //~ ERROR | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:57:5 + --> $DIR/write_with_newline.rs:58:5 | LL | write!(&mut v, "foo/rbar/n"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,5 +121,5 @@ help: use `writeln!()` instead LL | writeln!(&mut v, "foo/rbar"); | ^^^^^^^ -- -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index a28eda8be15..741195f3b10 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -88,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Index(ref array, ref index) = &expr.kind { - let ty = cx.typeck_results().expr_ty(array); + let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 000d5269930..ca8ca53c80c 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -15,7 +15,8 @@ fn main() { x[3]; // Ok, should not produce stderr. let y = &x; - y[0]; + y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 + y[4]; // Ok, rustc will handle references too. let v = vec![0; 5]; v[0]; diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 2b3f9be2dfb..2f6c9e2f4e5 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -8,15 +8,7 @@ LL | x[index]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:18:5 - | -LL | y[0]; - | ^^^^ - | - = help: Consider using `.get(n)` or `.get_mut(n)` instead - -error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:21:5 + --> $DIR/indexing_slicing_index.rs:22:5 | LL | v[0]; | ^^^^ @@ -24,7 +16,7 @@ LL | v[0]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:22:5 + --> $DIR/indexing_slicing_index.rs:23:5 | LL | v[10]; | ^^^^^ @@ -32,7 +24,7 @@ LL | v[10]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:23:5 + --> $DIR/indexing_slicing_index.rs:24:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -40,7 +32,7 @@ LL | v[1 << 3]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:29:5 + --> $DIR/indexing_slicing_index.rs:30:5 | LL | v[N]; | ^^^^ @@ -48,12 +40,12 @@ LL | v[N]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:30:5 + --> $DIR/indexing_slicing_index.rs:31:5 | LL | v[M]; | ^^^^ | = help: Consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index ec6c157ac1a..2231deee833 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -71,29 +71,17 @@ LL | &x[1..][..5]; | = help: Consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:24:6 - | -LL | &y[1..2]; - | ^^^^^^^ - | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead - -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:25:6 +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:25:12 | LL | &y[0..=4]; - | ^^^^^^^^ - | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^ -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:26:6 +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:26:11 | LL | &y[..=4]; - | ^^^^^^^ - | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + | ^ error: slicing may panic. --> $DIR/indexing_slicing_slice.rs:31:6 @@ -133,5 +121,5 @@ LL | &v[..100]; | = help: Consider using `.get(..n)`or `.get_mut(..n)` instead -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 189c07427ae..d7d02ebf985 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,6 +1,5 @@ use crate::utils::{ - contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, - span_lint_and_sugg, walk_ptrs_ty, + contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_ast::ast::UintTy; @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; if op.node == BinOpKind::Eq; if match_type(cx, - walk_ptrs_ty(cx.typeck_results().expr_ty(&filter_args[0])), + cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), &paths::SLICE_ITER); then { let needle = match get_path_name(l) { @@ -63,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { _ => { return; } } }; - if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() { + if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() { return; } let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 8ece44878fe..c0529a34cc4 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::Spanned; use crate::consts::{constant, Constant}; use crate::utils::paths; -use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind; if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind; - if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::DURATION); + if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION); if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right); then { let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index d616502a82a..35a5d00f4aa 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,6 +1,6 @@ use crate::utils::SpanlessEq; use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt}; -use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty}; +use crate::utils::{snippet_with_applicability, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -106,7 +106,7 @@ fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind; then { let map = ¶ms[0]; - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(map)); + let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); return if match_type(cx, obj_ty, &paths::BTREEMAP) { Some(("BTreeMap", map, key)) diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 000762334f6..a9e05fddbe7 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,5 @@ use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT}; -use crate::utils::{ - is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty, -}; +use crate::utils::{is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -96,7 +94,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { - let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 8bd85af8768..d6541010bca 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,7 +1,7 @@ use crate::utils::paths; use crate::utils::{ is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, - span_lint_and_then, walk_ptrs_ty, + span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -90,7 +90,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind; if pats.len() == 1; then { - let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0])); + let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { return None; } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index f330fa8fab8..0877b44d901 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -5,7 +5,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::{ get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, - trait_ref_of_method, walk_ptrs_ty, + trait_ref_of_method, }; declare_clippy_lint! { @@ -125,7 +125,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { // Get the real type of 'self' let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id); let self_type = cx.tcx.fn_sig(fn_def_id).input(0); - let self_type = walk_ptrs_ty(self_type.skip_binder()); + let self_type = self_type.skip_binder().peel_refs(); // Emit either a warning or an error if implements_trait(cx, self_type, display_trait_id, &[]) { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 42a98dc963d..c9c4891bb08 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -285,7 +285,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); + let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 57966452253..331b6c6c34a 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,3 @@ -use crate::utils::walk_ptrs_ty; use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -90,12 +89,12 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - let ty = walk_ptrs_ty(ty); + let ty = ty.peel_refs(); is_type_diagnostic_item(cx, ty, sym!(vec_type)) } fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - let ty = walk_ptrs_ty(ty); + let ty = ty.peel_refs(); is_type_lang_item(cx, ty, LangItem::RangeFull) } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7ba7397c29c..11380f83316 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -6,7 +6,7 @@ use crate::utils::{ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, walk_ptrs_ty, + span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -794,7 +794,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms } fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex)); + let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) { for arm in arms { if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 184aee95efa..dadd0f8ebb7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,8 +32,8 @@ use crate::utils::{ is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, + SpanlessEq, }; declare_clippy_lint! { @@ -1774,7 +1774,7 @@ fn lint_or_fun_call<'tcx>( ) { if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { if path.ident.as_str() == "len" { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); match ty.kind() { ty::Slice(_) | ty::Array(_, _) => return, @@ -1881,7 +1881,7 @@ fn lint_expect_fun_call( && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref)) && { let arg_type = cx.typeck_results().expr_ty(&call_args[0]); - let base_type = walk_ptrs_ty(arg_type); + let base_type = arg_type.peel_refs(); *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) } { @@ -2142,7 +2142,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp } fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg)); + let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs(); if let ty::Adt(_, subst) = obj_ty.kind() { let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { @@ -2173,7 +2173,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E let arg = &args[1]; if let Some(arglists) = method_chain_args(arg, &["chars"]) { let target = &arglists[0][0]; - let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target)); + let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { "" } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { @@ -2201,7 +2201,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E } fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) { lint_string_extend(cx, expr, args); } @@ -2384,7 +2384,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ } } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) || matches!( - &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(), + &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), ty::Array(_, _) ) { @@ -2587,7 +2587,7 @@ fn derefs_to_slice<'tcx>( /// lint use of `unwrap()` for `Option`s and `Result`s fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&unwrap_args[0])); + let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { Some((UNWRAP_USED, "an Option", "None")) @@ -2615,7 +2615,7 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E /// lint use of `expect()` for `Option`s and `Result`s fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&expect_args[0])); + let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { Some((EXPECT_USED, "an Option", "None")) @@ -3134,7 +3134,7 @@ fn lint_chars_cmp( if segment.ident.name == sym!(Some); then { let mut applicability = Applicability::MachineApplicable; - let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0])); + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); if *self_ty.kind() != ty::Str { return false; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 67a3685fd0d..909e79f661a 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -17,7 +17,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq, + span_lint_and_then, span_lint_hir_and_then, SpanlessEq, }; declare_clippy_lint! { @@ -561,7 +561,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(); + let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind(); if let ty::Array(arr_ty, _) = value { return matches!(arr_ty.kind(), ty::Float(_)); @@ -571,7 +571,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _)) + matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 0826ad0ab55..8a2dbdc50ea 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty}; +use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; @@ -98,7 +98,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir:: // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased // generics (because the compiler cannot ensure immutability for unknown types). fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { - let ty = walk_ptrs_ty(ty); + let ty = ty.peel_refs(); if let Adt(def, substs) = ty.kind() { if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET] .iter() diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index e99d0317ba2..73a99a3a2f8 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty}; +use crate::utils::{match_type, paths, span_lint}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -30,7 +30,7 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); impl<'tcx> LateLintPass<'tcx> for OpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0])); + let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { let mut options = Vec::new(); get_open_options(cx, &arguments[0], &mut options); @@ -58,7 +58,7 @@ enum OpenOption { fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind { - let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0])); + let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); // Only proceed if this is a call on some object of type std::fs::OpenOptions if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 { diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index b8583402928..6eeb031d383 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; if path.ident.name == sym!(push); if args.len() == 2; - if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::PATH_BUF); + if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::PATH_BUF); if let Some(get_index_arg) = args.get(1); if let ExprKind::Lit(ref lit) = get_index_arg.kind; if let LitKind::Str(ref path_lit, _) = lit.node; diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index c0890018d46..ae601353009 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); + let ty = cx.typeck_results().expr_ty(&receiver).peel_refs(); if ty.is_str() { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 7a659bf779c..15b66684eab 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned; use if_chain::if_chain; use crate::utils::SpanlessEq; -use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without @@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(e)), sym!(string_type)) + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type)) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 47a73ca9a24..54b38d9f4ce 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,6 @@ use crate::utils::sugg::Sugg; use crate::utils::{ differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, - walk_ptrs_ty, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -194,7 +193,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { if eq_expr_value(cx, lhs1, lhs2) { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); + let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 1c7e62ecd3d..0f8797243ec 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // check for `expect` if let Some(arglists) = method_chain_args(expr, &["expect"]) { - let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) { @@ -91,7 +91,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { - let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) { diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index f201494a024..bfe426a25eb 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,6 +1,6 @@ use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, - snippet, span_lint, span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, + snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -427,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); then { @@ -460,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { let args = arg_lists[1]; if args.len() == 1; let self_arg = &args[0]; - let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(self_arg)); + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); then { span_lint_and_sugg( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 95eedf88178..ea52741b7cc 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -754,14 +754,6 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { } } -/// Returns the base type for references and raw pointers. -pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { - match ty.kind() { - ty::Ref(_, ty, _) => walk_ptrs_ty(ty), - _ => ty, - } -} - /// Returns the base type for references and raw pointers, and count reference /// depth. pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { -- cgit 1.4.1-3-g733a5 From 27200855c34445bdf50185fbaed12b5c0d60eb9b Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Wed, 16 Sep 2020 17:19:35 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 239eeb10bb4..06f41be8a2a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1480,6 +1480,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::TYPE_COMPLEXITY), LintId::of(&types::UNIT_ARG), @@ -1780,6 +1781,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), + LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), ]); @@ -1805,7 +1807,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(&transmute::USELESS_TRANSMUTE), - LintId::of(&types::RC_BUFFER), LintId::of(&use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index da04d07885b..a29a199b8c3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -241,7 +241,7 @@ declare_clippy_lint! { /// fn foo(interned: Rc) { ... } /// ``` pub RC_BUFFER, - nursery, + perf, "shared ownership of a buffer type" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d0c6a1d63d9..e5563151b48 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1853,7 +1853,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "rc_buffer", - group: "nursery", + group: "perf", desc: "shared ownership of a buffer type", deprecation: None, module: "types", -- cgit 1.4.1-3-g733a5 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: 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 | 44 +++++++----- tests/ui/declare_interior_mutable_const.rs | 62 +++++++++-------- tests/ui/declare_interior_mutable_const.stderr | 62 ++++++----------- 5 files changed, 156 insertions(+), 125 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 73eabd4207e..28c68a2b68c 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -6,14 +6,17 @@ use std::ptr; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{Ty, TypeFlags}; +use rustc_middle::ty::fold::TypeFoldable as _; +use rustc_middle::ty::{AssocKind, Ty, TypeFlags}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then}; +use crate::utils::{in_constant, qpath_res, span_lint_and_then}; +use if_chain::if_chain; declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior @@ -83,11 +86,10 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[allow(dead_code)] #[derive(Copy, Clone)] enum Source { Item { item: Span }, - Assoc { item: Span, ty: Span }, + Assoc { item: Span }, Expr { expr: Span }, } @@ -110,10 +112,15 @@ impl Source { } fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { - if ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) || is_copy(cx, ty) { - // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which - // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze` - // as well. + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) { return; } @@ -127,11 +134,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { let const_kw_span = span.from_inner(InnerSpan::new(0, 5)); diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); }, - Source::Assoc { ty: ty_span, .. } => { - if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { - diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); - } - }, + Source::Assoc { .. } => (), Source::Expr { .. } => { diag.help("assign this const to a local or static variable, and use the variable here"); }, @@ -152,14 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound( - cx, - ty, - Source::Assoc { - ty: hir_ty.span, - item: trait_item.span, - }, - ); + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound. + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span }); } } @@ -167,17 +166,47 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); let item = cx.tcx.hir().expect_item(item_hir_id); - // Ensure the impl is an inherent impl. - if let ItemKind::Impl { of_trait: None, .. } = item.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound( - cx, - ty, - Source::Assoc { - ty: hir_ty.span, - item: impl_item.span, - }, - ); + + match &item.kind { + ItemKind::Impl { + of_trait: Some(of_trait_ref), + .. + } => { + if_chain! { + // Lint a trait impl item only when the definition is a generic type, + // assuming a assoc const is not meant to be a interior mutable type. + if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); + if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) + .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); + if cx.tcx + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound at the trait defs; + // and, in that case, the definition is *not* generic. + .normalize_erasing_regions( + cx.tcx.param_env(of_trait_def_id), + cx.tcx.type_of(of_assoc_item.def_id), + ) + .has_type_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_PARAM); + then { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound( + cx, + normalized, + Source::Assoc { + item: impl_item.span, + }, + ); + } + } + }, + ItemKind::Impl { of_trait: None, .. } => { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + // Normalize assoc types originated from generic params. + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span }); + }, + _ => (), } } } diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index 39f87510548..9fcc9ece49b 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -19,16 +19,30 @@ const NO_ANN: &dyn Display = &70; static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); const ONCE_INIT: Once = Once::new(); -trait Trait: Copy { - type NonCopyType; +trait Trait { + type AssocType; const ATOMIC: AtomicUsize; + const INPUT: T; + const ASSOC: Self::AssocType; + + fn function() { + let _ = &Self::INPUT; + let _ = &Self::ASSOC; + } } impl Trait for u64 { - type NonCopyType = u16; + type AssocType = AtomicUsize; const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INPUT: u32 = 10; + const ASSOC: Self::AssocType = AtomicUsize::new(11); + + fn function() { + let _ = &Self::INPUT; + let _ = &Self::ASSOC; //~ ERROR interior mutability + } } // This is just a pointer that can be safely dereferended, diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index 5800af7e960..ed726a6b46e 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,14 +1,22 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:66:5 + --> $DIR/borrow_interior_mutable_const.rs:44:18 + | +LL | 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:80:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ | - = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:16 + --> $DIR/borrow_interior_mutable_const.rs:81:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +24,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:70:22 + --> $DIR/borrow_interior_mutable_const.rs:84:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +32,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:71:25 + --> $DIR/borrow_interior_mutable_const.rs:85:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +40,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:72:27 + --> $DIR/borrow_interior_mutable_const.rs:86:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +48,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:73:26 + --> $DIR/borrow_interior_mutable_const.rs:87:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +56,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:84:14 + --> $DIR/borrow_interior_mutable_const.rs:98:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:85:14 + --> $DIR/borrow_interior_mutable_const.rs:99:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +72,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:86:19 + --> $DIR/borrow_interior_mutable_const.rs:100:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +80,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:87:14 + --> $DIR/borrow_interior_mutable_const.rs:101:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +88,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:88:13 + --> $DIR/borrow_interior_mutable_const.rs:102:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:94:13 + --> $DIR/borrow_interior_mutable_const.rs:108:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +104,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:99:5 + --> $DIR/borrow_interior_mutable_const.rs:113:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +112,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:100:16 + --> $DIR/borrow_interior_mutable_const.rs:114:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +120,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:113:5 + --> $DIR/borrow_interior_mutable_const.rs:127:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,12 +128,12 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:114:16 + --> $DIR/borrow_interior_mutable_const.rs:128:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index b4003ed8932..7471b360540 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -34,60 +34,64 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); #[allow(clippy::declare_interior_mutable_const)] const ONCE_INIT: Once = Once::new(); -trait Trait: Copy { - type NonCopyType; +struct Wrapper(T); + +trait Trait> { + type AssocType; + type AssocType2; + type AssocType3; const ATOMIC: AtomicUsize; //~ ERROR interior mutable const INTEGER: u64; const STRING: String; - const SELF: Self; // (no error) + const SELF: Self; const INPUT: T; - //~^ ERROR interior mutable - //~| HELP consider requiring `T` to be `Copy` - const ASSOC: Self::NonCopyType; - //~^ ERROR interior mutable - //~| HELP consider requiring `>::NonCopyType` to be `Copy` + const INPUT_ASSOC: T::AssocType4; + const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable + const ASSOC: Self::AssocType; + const ASSOC_2: Self::AssocType2; + const WRAPPED_ASSOC_2: Wrapper; + const WRAPPED_ASSOC_3: Wrapper; const AN_INPUT: T = Self::INPUT; - //~^ ERROR interior mutable - //~| ERROR consider requiring `T` to be `Copy` - declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable + declare_const!(ANOTHER_INPUT: T = Self::INPUT); + declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable } trait Trait2 { - type CopyType: Copy; + type AssocType4; + type AssocType5; const SELF_2: Self; - //~^ ERROR interior mutable - //~| HELP consider requiring `Self` to be `Copy` - const ASSOC_2: Self::CopyType; // (no error) + const ASSOC_4: Self::AssocType4; } -// we don't lint impl of traits, because an impl has no power to change the interface. -impl Trait for u64 { - type NonCopyType = u16; +impl> Trait for u64 { + type AssocType = u16; + type AssocType2 = AtomicUsize; + type AssocType3 = T; const ATOMIC: AtomicUsize = AtomicUsize::new(9); const INTEGER: u64 = 10; const STRING: String = String::new(); const SELF: Self = 11; - const INPUT: u32 = 12; - const ASSOC: Self::NonCopyType = 13; + const INPUT: T = T::SELF_2; + const INPUT_ASSOC: T::AssocType4 = T::ASSOC_4; + const INPUT_ASSOC_2: T::AssocType5 = AtomicUsize::new(16); + const ASSOC: Self::AssocType = 13; + const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable + const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable + const WRAPPED_ASSOC_3: Wrapper = Wrapper(T::SELF_2); } struct Local(T, U); -impl, U: Trait2> Local { - const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable +impl, U: Trait2> Local { + const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - const T_SELF: T = T::SELF_2; const U_SELF: U = U::SELF_2; - //~^ ERROR interior mutable - //~| HELP consider requiring `U` to be `Copy` - const T_ASSOC: T::NonCopyType = T::ASSOC; - //~^ ERROR interior mutable - //~| HELP consider requiring `>::NonCopyType` to be `Copy` - const U_ASSOC: U::CopyType = U::ASSOC_2; + const T_ASSOC: T::AssocType = T::ASSOC; + const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 6a9a57361f9..0fcb726db46 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -36,34 +36,16 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:40:5 + --> $DIR/declare_interior_mutable_const.rs:44:5 | LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:44:5 - | -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 + --> $DIR/declare_interior_mutable_const.rs:50: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:84:5 + --> $DIR/declare_interior_mutable_const.rs:90:5 | -LL | const U_SELF: U = U::SELF_2; - | ^^^^^^^^^^^^^^-^^^^^^^^^^^^^ - | | - | consider requiring `U` to be `Copy` +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:87:5 + --> $DIR/declare_interior_mutable_const.rs:94:5 | -LL | const T_ASSOC: T::NonCopyType = T::ASSOC; - | ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^ - | | - | consider requiring `>::NonCopyType` to be `Copy` +LL | const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 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: 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 | 159 ++++++++++++++++++------- tests/ui/declare_interior_mutable_const.stderr | 58 +++++---- 3 files changed, 166 insertions(+), 75 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 28c68a2b68c..bb44eeb6adc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -9,8 +9,7 @@ use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, Tr use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::fold::TypeFoldable as _; -use rustc_middle::ty::{AssocKind, Ty, TypeFlags}; +use rustc_middle::ty::{AssocKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -178,15 +177,18 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); - if cx.tcx - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - .normalize_erasing_regions( - cx.tcx.param_env(of_trait_def_id), - cx.tcx.type_of(of_assoc_item.def_id), - ) - .has_type_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_PARAM); + if cx + .tcx + .layout_of(cx.tcx.param_env(of_trait_def_id).and( + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound at the trait defs; + // and, in that case, the definition is *not* generic. + cx.tcx.normalize_erasing_regions( + cx.tcx.param_env(of_trait_def_id), + cx.tcx.type_of(of_assoc_item.def_id), + ), + )) + .is_err(); then { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index 7471b360540..646d3ec8b47 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -34,64 +34,135 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); #[allow(clippy::declare_interior_mutable_const)] const ONCE_INIT: Once = Once::new(); -struct Wrapper(T); - -trait Trait> { - type AssocType; - type AssocType2; - type AssocType3; - +// a constant whose type is a concrete type should be linted at the definition site. +trait ConcreteTypes { const ATOMIC: AtomicUsize; //~ ERROR interior mutable const INTEGER: u64; const STRING: String; - const SELF: Self; - const INPUT: T; - const INPUT_ASSOC: T::AssocType4; - const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable - const ASSOC: Self::AssocType; - const ASSOC_2: Self::AssocType2; - const WRAPPED_ASSOC_2: Wrapper; - const WRAPPED_ASSOC_3: Wrapper; - - const AN_INPUT: T = Self::INPUT; - declare_const!(ANOTHER_INPUT: T = Self::INPUT); declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable } -trait Trait2 { - type AssocType4; - type AssocType5; +impl ConcreteTypes for u64 { + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INTEGER: u64 = 10; + const STRING: String = String::new(); +} - const SELF_2: Self; - const ASSOC_4: Self::AssocType4; +// a helper trait used below +trait ConstDefault { + const DEFAULT: Self; } -impl> Trait for u64 { - type AssocType = u16; - type AssocType2 = AtomicUsize; - type AssocType3 = T; +// 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 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); + 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); } -struct Local(T, U); +// a helper trait used below +trait AssocTypesHelper { + type NotToBeBounded; + type ToBeBounded; -impl, U: Trait2> Local { - const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; +} + +// a constant whose type is an assoc type originated from a generic param bounded at the definition +// site should be linted at there. +trait AssocTypesFromGenericParam +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded; + const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable +} + +impl AssocTypesFromGenericParam for u64 +where + T: AssocTypesHelper, +{ + // an associated type could remain unknown in a trait impl. + const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); +} + +trait SelfType { + const SELF: Self; +} + +impl SelfType for u64 { + const SELF: Self = 16; +} + +impl SelfType for AtomicUsize { + // this (interior mutable `Self` const) exists in `parking_lot`. + // `const_trait_impl` will replace it in the future, hopefully. + const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable +} + +// Even though a constant contains a generic type, if it also have a interior mutable type, +// it should be linted at the definition site. +trait BothOfCellAndGeneric { + // this is a false negative in the current implementation. + const DIRECT: Cell; + const INDIRECT: Cell<*const T>; //~ ERROR interior mutable +} + +impl BothOfCellAndGeneric for u64 { + const DIRECT: Cell = Cell::new(T::DEFAULT); + const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); +} + +struct Local(T); + +// a constant in an inherent impl are essentially the same as a normal const item +// except there can be a generic or associated type. +impl Local +where + T: ConstDefault + AssocTypesHelper, +{ + const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - const U_SELF: U = U::SELF_2; - const T_ASSOC: T::AssocType = T::ASSOC; - const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable + + const GENERIC_TYPE: T = T::DEFAULT; + + const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 0fcb726db46..0a0b818b8b7 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -36,17 +36,11 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:44:5 + --> $DIR/declare_interior_mutable_const.rs:39:5 | LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:50:5 - | -LL | const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: a `const` item should never be interior mutable --> $DIR/declare_interior_mutable_const.rs:16:9 | @@ -59,28 +53,52 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR i = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:82:5 + --> $DIR/declare_interior_mutable_const.rs:67:5 + | +LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:92:5 + | +LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:93:5 + | +LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:112:5 + | +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 | -LL | const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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:83:5 + --> $DIR/declare_interior_mutable_const.rs:143:5 | -LL | const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const INDIRECT: Cell<*const T>; //~ 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:159:5 | -LL | const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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:94:5 + --> $DIR/declare_interior_mutable_const.rs:165:5 | -LL | const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From d4f158fa5cd5f4ae1cc690b37b0a8850cb013b69 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sun, 20 Sep 2020 12:38:23 +0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 6f47687c410..b1a4e06d4c3 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -502,7 +502,7 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { return; } diff --git a/tests/ui/crashes/ice-2636.rs b/tests/ui/crashes/ice-2636.rs deleted file mode 100644 index e0b58157590..00000000000 --- a/tests/ui/crashes/ice-2636.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![allow(dead_code)] - -enum Foo { - A, - B, - C, -} - -macro_rules! test_hash { - ($foo:expr, $($t:ident => $ord:expr),+ ) => { - use self::Foo::*; - match $foo { - $ ( & $t => $ord, - )* - }; - }; -} - -fn main() { - let a = Foo::A; - test_hash!(&a, A => 0, B => 1, C => 2); -} diff --git a/tests/ui/crashes/ice-2636.stderr b/tests/ui/crashes/ice-2636.stderr deleted file mode 100644 index 53799b4fbf1..00000000000 --- a/tests/ui/crashes/ice-2636.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/ice-2636.rs:12:9 - | -LL | / match $foo { -LL | | $ ( & $t => $ord, -LL | | )* -LL | | }; - | |_________^ -... -LL | test_hash!(&a, A => 0, B => 1, C => 2); - | --------------------------------------- in this macro invocation - | - = note: `-D clippy::match-ref-pats` implied by `-D warnings` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 17d908336d5..fe8f62503b7 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -42,6 +42,7 @@ fn main() { issue5504(); issue6067(); + issue6065(); let _ = if gen_res().is_ok() { 1 @@ -79,6 +80,17 @@ fn issue5504() { while m!().is_some() {} } +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + // Methods that are unstable const should not be suggested within a const context, see issue #5697. // However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, // so the following should be linted. diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index d57fbb14ae4..09426a6e590 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -54,6 +54,7 @@ fn main() { issue5504(); issue6067(); + issue6065(); let _ = if let Ok(_) = gen_res() { 1 @@ -91,6 +92,17 @@ fn issue5504() { while let Some(_) = m!() {} } +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + // Methods that are unstable const should not be suggested within a const context, see issue #5697. // However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, // so the following should be linted. diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 955900f3e6c..3473ceea00e 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:58:20 + --> $DIR/redundant_pattern_matching.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:60:19 + --> $DIR/redundant_pattern_matching.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:83:19 + --> $DIR/redundant_pattern_matching.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:84:16 + --> $DIR/redundant_pattern_matching.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:90:12 + --> $DIR/redundant_pattern_matching.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:91:15 + --> $DIR/redundant_pattern_matching.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:98:12 + --> $DIR/redundant_pattern_matching.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:100:12 + --> $DIR/redundant_pattern_matching.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:102:15 + --> $DIR/redundant_pattern_matching.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:104:15 + --> $DIR/redundant_pattern_matching.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:106:5 + --> $DIR/redundant_pattern_matching.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:111:5 + --> $DIR/redundant_pattern_matching.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3410341a1e3..7f998c63f49 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2134,7 +2134,7 @@ enum VarState { DontWarn, } -/// Scan a for loop for variables that are incremented exactly once. +/// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: FxHashMap, // incremented variables @@ -2154,6 +2154,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(VarState::Initial); + if *state == VarState::IncrOnce { + *state = VarState::DontWarn; + return; + } match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa6ef162fe4..81d8221bd13 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -38,54 +38,54 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { continue; } count += 1; - println!("{}", count); } // should not trigger the lint because the count is conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { count += 1; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; if ch == 'a' { continue; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { let _ = 123; } - println!("{}", count); } // should not trigger the lint because the count is incremented multiple times let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { count += 1; } - println!("{}", count); } } } @@ -96,30 +96,30 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { + println!("{}", skips); while erasures.contains(&(i + skips)) { skips += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); let mut j = 0; while j < 5 { skips += 1; j += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); for j in 0..5 { skips += 1; } - println!("{}", skips); } } } @@ -145,3 +145,16 @@ mod issue_4732 { let _closure = || println!("index: {}", index); } } + +mod issue_4677 { + pub fn test() { + let slice = &[1, 2, 3]; + + // should not trigger the lint because the count is used after incremented + let mut count = 0; + for _i in slice { + count += 1; + println!("{}", count); + } + } +} -- cgit 1.4.1-3-g733a5 From 3e294b22a43be349262405715cf4885296c284ba Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 00:34:56 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index ea52741b7cc..3a005e2513e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -893,7 +893,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5fb568672d3..67faa8bd4a0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -116,17 +122,6 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 737b0f7e55b..9867e2eedcf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -116,17 +122,6 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index b8a436993f3..bc5978b538f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,23 +60,35 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:62:21 + --> $DIR/or_fun_call.rs:68:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:87:35 + --> $DIR/or_fun_call.rs:93:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:91:10 + --> $DIR/or_fun_call.rs:97:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From ce83d8d4d1b28e73888a616d3ffbf19c6a620588 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 00:39:00 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a005e2513e..92cb31fcf85 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -46,7 +46,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; -use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -883,19 +882,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() - } - if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { - const_eval::is_const_fn(cx.tcx, def_id) - }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..2045ffdb5f0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -116,12 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..522f31b72d0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -116,12 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); -} - fn main() {} -- cgit 1.4.1-3-g733a5 From 6c056d346562eebf8535e2a4415c353a911b6280 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Tue, 22 Sep 2020 20:34:38 -0600 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 3ee022e4e68..0000d39263e 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -21,7 +21,7 @@ pub enum Constant { /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). - Binary(Lrc>), + Binary(Lrc<[u8]>), /// A single `char` (e.g., `'a'`). Char(char), /// An integer's bit representation. @@ -155,7 +155,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s) => Constant::Binary(Lrc::from(s.as_slice())), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { -- cgit 1.4.1-3-g733a5 From 9365660a2fc57210996df733efe468019c671b2f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 23:33:50 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 9b6a9075a29..1307237dbc7 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -170,22 +170,12 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, - // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested - // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more - // than one level of references would add some extra complexity as we would have to compensate - // in the closure body. - if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - let vec_ty = cx.typeck_results().expr_ty(vec); - if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec - if !matches!(&ty.kind(), ty::Ref(..)); - if utils::is_copy(cx, ty); + if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type)); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, @@ -210,40 +200,32 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - if_chain! { - if let ExprKind::Path(QPath::Resolved(_, Path { - segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind; - if left_name == left_ident; - then { - return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } else { - if !key_returns_borrow(cx, left_expr) { - return Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) - } + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind { + if left_name == left_ident { + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); } } + + if !expr_borrows(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })); + } } } None } -fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(def_id) = utils::fn_def_id(cx, expr) { - let output = cx.tcx.fn_sig(def_id).output(); - let ty = output.skip_binder(); - return matches!(ty.kind(), ty::Ref(..)) - || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - false +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } impl LateLintPass<'_> for UnnecessarySortBy { @@ -256,7 +238,7 @@ impl LateLintPass<'_> for UnnecessarySortBy { "use Vec::sort_by_key here instead", "try", format!( - "{}.sort{}_by_key(|&{}| {})", + "{}.sort{}_by_key(|{}| {})", trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index ad0d0387db0..b45b27d8f23 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -13,12 +13,12 @@ fn unnecessary_sort_by() { // Forward examples vec.sort(); vec.sort_unstable(); - vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_unstable_by_key(|&a| id(-a)); + vec.sort_by_key(|a| (a + 5).abs()); + vec.sort_unstable_by_key(|a| id(-a)); // Reverse examples - vec.sort_by_key(|&b| Reverse(b)); - vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_unstable_by_key(|&b| Reverse(id(-b))); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow + vec.sort_by_key(|b| Reverse((b + 5).abs())); + vec.sort_unstable_by_key(|b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; - vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); - vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by_key(|a| (***a).abs()); + vec.sort_unstable_by_key(|a| (***a).abs()); + // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { @@ -85,11 +85,11 @@ mod issue_6001 { let mut args: Vec = vec![]; // Forward - args.sort_by(|a, b| a.name().cmp(&b.name())); - args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + args.sort_by_key(|a| a.name()); + args.sort_unstable_by_key(|a| a.name()); // Reverse - args.sort_by(|a, b| b.name().cmp(&a.name())); - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + args.sort_by_key(|b| Reverse(b.name())); + args.sort_unstable_by_key(|b| Reverse(b.name())); } } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 9746f6e6849..be2abe7f701 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -16,7 +16,7 @@ fn unnecessary_sort_by() { vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples - vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 70c6cf0a3b6..50607933e18 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -16,31 +16,61 @@ error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` - -error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:19:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:20:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))` + +error: 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 7 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 2892a2b0f578edd290b3be6f5e5c3280bc160f24 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 24 Sep 2020 23:22:54 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index fac63bcb993..780d474ee96 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::ops::Range; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; +use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; @@ -11,7 +12,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, FileName, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -236,7 +237,15 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + let filename = cx.sess.source_map().span_to_filename(mac.span()); + if_chain! { + if let FileName::Real(filename) = filename; + if let Some(filename) = filename.local_path().file_name(); + if filename != "build.rs"; + then { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + } + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( @@ -251,7 +260,15 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + if_chain! { + let filename = cx.sess.source_map().span_to_filename(mac.span()); + if let FileName::Real(filename) = filename; + if let Some(filename) = filename.local_path().file_name(); + if filename != "build.rs"; + then { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + } + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( diff --git a/tests/ui/build.rs b/tests/ui/build.rs new file mode 100644 index 00000000000..2d43d452a4f --- /dev/null +++ b/tests/ui/build.rs @@ -0,0 +1,10 @@ +#![warn(clippy::print_stdout)] + +fn main() { + // Fix #6041 + // + // The `print_stdout` shouldn't be linted in `build.rs` + // as these methods are used for the build script. + println!("Hello"); + print!("Hello"); +} diff --git a/tests/ui/build.stderr b/tests/ui/build.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 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: Use if_chain in Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7f998c63f49..9f3be26e672 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2162,15 +2162,16 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { if lhs.hir_id == expr.hir_id { - if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) { - *state = match *state { - VarState::Initial if self.depth == 0 => VarState::IncrOnce, - _ => VarState::DontWarn, - }; + *state = if op.node == BinOpKind::Add + && is_integer_const(self.cx, rhs, 1) + && *state == VarState::Initial + && self.depth == 0 + { + VarState::IncrOnce } else { - // Assigned some other value - *state = VarState::DontWarn; - } + // Assigned some other value or assigned multiple times + VarState::DontWarn + }; } }, ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, @@ -2212,18 +2213,20 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { // Look for declarations of the variable - if let StmtKind::Local(ref local) = stmt.kind { - if local.pat.hir_id == self.var_id { - if let PatKind::Binding(.., ident, _) = local.pat.kind { - self.name = Some(ident.name); - - self.state = local.init.as_ref().map_or(VarState::Declared, |init| { - if is_integer_const(&self.cx, init, 0) { - VarState::Warn - } else { - VarState::Declared - } - }) + if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if local.pat.hir_id == self.var_id; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + then { + self.name = Some(ident.name); + self.state = if_chain! { + if let Some(ref init) = local.init; + if is_integer_const(&self.cx, init, 0); + then { + VarState::Warn + } else { + VarState::Declared + } } } } -- cgit 1.4.1-3-g733a5 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: Use else blocks instead of return statements in Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9f3be26e672..a59d3ef8708 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2181,16 +2181,17 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { _ => (), } } + + walk_expr(self, expr); } else if is_loop(expr) || is_conditional(expr) { self.depth += 1; walk_expr(self, expr); self.depth -= 1; - return; } else if let ExprKind::Continue(_) = expr.kind { self.done = true; - return; + } else { + walk_expr(self, expr); } - walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -2272,16 +2273,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { self.state = VarState::DontWarn; return; } + walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { self.state = VarState::DontWarn; - return; } else if is_conditional(expr) { self.depth += 1; walk_expr(self, expr); self.depth -= 1; - return; + } else { + walk_expr(self, expr); } - walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { -- cgit 1.4.1-3-g733a5 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: Check if it's after the loop earlier --- clippy_lints/src/loops.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index a59d3ef8708..20e75546d71 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2250,6 +2250,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { // If node is the desired variable, see how it's used if var_def_id(self.cx, expr) == Some(self.var_id) { + if self.past_loop { + self.state = VarState::DontWarn; + return; + } + if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { @@ -2269,10 +2274,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } } - if self.past_loop { - self.state = VarState::DontWarn; - return; - } walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { self.state = VarState::DontWarn; -- cgit 1.4.1-3-g733a5 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: add concinient methods to Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 63 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 20e75546d71..61f0059cca4 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1536,31 +1536,17 @@ fn check_for_loop_explicit_counter<'tcx>( expr: &'tcx Expr<'_>, ) { // Look for variables that are incremented once per loop iteration. - let mut visitor = IncrementVisitor { - cx, - states: FxHashMap::default(), - depth: 0, - done: false, - }; + let mut visitor = IncrementVisitor::new(cx); walk_expr(&mut visitor, body); // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) { - let mut visitor2 = InitializeVisitor { - cx, - end_expr: expr, - var_id: *id, - state: VarState::IncrOnce, - name: None, - depth: 0, - past_loop: false, - }; + for id in visitor.into_results() { + let mut visitor2 = InitializeVisitor::new(cx, expr, id); walk_block(&mut visitor2, block); - if visitor2.state == VarState::Warn { - if let Some(name) = visitor2.name { + if let Some(name) = visitor2.get_result() { let mut applicability = Applicability::MachineApplicable; // for some reason this is the only way to get the `Span` @@ -1585,7 +1571,6 @@ fn check_for_loop_explicit_counter<'tcx>( ), applicability, ); - } } } } @@ -2142,6 +2127,24 @@ struct IncrementVisitor<'a, 'tcx> { done: bool, } +impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + Self { + cx, + states: FxHashMap::default(), + depth: 0, + done: false, + } + } + + fn into_results(self) -> impl Iterator { + self.states + .into_iter() + .filter(|(_, state)| *state == VarState::IncrOnce) + .map(|(id, _)| id) + } +} + impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { type Map = Map<'tcx>; @@ -2209,6 +2212,28 @@ struct InitializeVisitor<'a, 'tcx> { past_loop: bool, } +impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + Self { + cx, + end_expr, + var_id, + state: VarState::IncrOnce, + name: None, + depth: 0, + past_loop: false, + } + } + + fn get_result(&self) -> Option { + if self.state == VarState::Warn { + self.name + } else { + None + } + } +} + impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { type Map = Map<'tcx>; -- cgit 1.4.1-3-g733a5 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: Split VarState --- clippy_lints/src/loops.rs | 87 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 39 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 61f0059cca4..2b7830e7cad 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2107,23 +2107,18 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool { } } -// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be -// incremented exactly once in the loop body, and initialized to zero -// at the start of the loop. #[derive(Debug, PartialEq)] -enum VarState { +enum IncrementVisitorVarState { Initial, // Not examined yet IncrOnce, // Incremented exactly once, may be a loop counter - Declared, // Declared but not (yet) initialized to zero - Warn, DontWarn, } /// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, // context reference - states: FxHashMap, // incremented variables - depth: u32, // depth of conditional expressions + cx: &'a LateContext<'tcx>, // context reference + states: FxHashMap, // incremented variables + depth: u32, // depth of conditional expressions done: bool, } @@ -2140,7 +2135,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { fn into_results(self) -> impl Iterator { self.states .into_iter() - .filter(|(_, state)| *state == VarState::IncrOnce) + .filter(|(_, state)| *state == IncrementVisitorVarState::IncrOnce) .map(|(id, _)| id) } } @@ -2156,9 +2151,9 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { // If node is a variable if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { - let state = self.states.entry(def_id).or_insert(VarState::Initial); - if *state == VarState::IncrOnce { - *state = VarState::DontWarn; + let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); + if *state == IncrementVisitorVarState::IncrOnce { + *state = IncrementVisitorVarState::DontWarn; return; } @@ -2167,19 +2162,21 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if lhs.hir_id == expr.hir_id { *state = if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) - && *state == VarState::Initial + && *state == IncrementVisitorVarState::Initial && self.depth == 0 { - VarState::IncrOnce + IncrementVisitorVarState::IncrOnce } else { // Assigned some other value or assigned multiple times - VarState::DontWarn + IncrementVisitorVarState::DontWarn }; } }, - ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, + ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => { + *state = IncrementVisitorVarState::DontWarn + }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - *state = VarState::DontWarn + *state = IncrementVisitorVarState::DontWarn }, _ => (), } @@ -2201,13 +2198,20 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } } -/// Checks whether a variable is initialized to zero at the start of a loop. +enum InitializeVisitorState { + Initial, // Not examined yet + Declared(Symbol), // Declared but not (yet) initialized + Initialized { name: Symbol }, + DontWarn, +} + +/// Checks whether a variable is initialized to zero at the start of a loop and not modified +/// and used after the loop. struct InitializeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, - state: VarState, - name: Option, + state: InitializeVisitorState, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2218,16 +2222,15 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { cx, end_expr, var_id, - state: VarState::IncrOnce, - name: None, + state: InitializeVisitorState::Initial, depth: 0, past_loop: false, } } fn get_result(&self) -> Option { - if self.state == VarState::Warn { - self.name + if let InitializeVisitorState::Initialized { name } = self.state { + Some(name) } else { None } @@ -2244,23 +2247,24 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.name = Some(ident.name); self.state = if_chain! { if let Some(ref init) = local.init; if is_integer_const(&self.cx, init, 0); then { - VarState::Warn - } else { - VarState::Declared + InitializeVisitorState::Initialized { + name: ident.name } + } else { + InitializeVisitorState::Declared(ident.name) } } } + } walk_stmt(self, stmt); } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.state == VarState::DontWarn { + if matches!(self.state, InitializeVisitorState::DontWarn) { return; } if expr.hir_id == self.end_expr.hir_id { @@ -2269,31 +2273,36 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } // No need to visit expressions before the variable is // declared - if self.state == VarState::IncrOnce { + if matches!(self.state, InitializeVisitorState::Initial) { return; } // If node is the desired variable, see how it's used if var_def_id(self.cx, expr) == Some(self.var_id) { if self.past_loop { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; return; } if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; }, ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { - self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 { - VarState::Warn - } else { - VarState::DontWarn + self.state = if_chain! { + if is_integer_const(&self.cx, rhs, 0) && self.depth == 0; + if let InitializeVisitorState::Declared(name) + | InitializeVisitorState::Initialized { name, ..} = self.state; + then { + InitializeVisitorState::Initialized { name } + } else { + InitializeVisitorState::DontWarn + } } }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - self.state = VarState::DontWarn + self.state = InitializeVisitorState::DontWarn }, _ => (), } @@ -2301,7 +2310,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; } else if is_conditional(expr) { self.depth += 1; walk_expr(self, expr); -- cgit 1.4.1-3-g733a5 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: Generalise `InitializeVisitor` --- clippy_lints/src/loops.rs | 36 ++++++++++++++++++++---------------- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2b7830e7cad..bf067c70a7e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1528,6 +1528,9 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { } } +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be +// incremented exactly once in the loop body, and initialized to zero +// at the start of the loop. fn check_for_loop_explicit_counter<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, @@ -1546,7 +1549,10 @@ fn check_for_loop_explicit_counter<'tcx>( let mut visitor2 = InitializeVisitor::new(cx, expr, id); walk_block(&mut visitor2, block); - if let Some(name) = visitor2.get_result() { + if_chain! { + if let Some((name, initializer)) = visitor2.get_result(); + if is_integer_const(cx, initializer, 0); + then { let mut applicability = Applicability::MachineApplicable; // for some reason this is the only way to get the `Span` @@ -1571,6 +1577,7 @@ fn check_for_loop_explicit_counter<'tcx>( ), applicability, ); + } } } } @@ -2198,20 +2205,20 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } } -enum InitializeVisitorState { +enum InitializeVisitorState<'hir> { Initial, // Not examined yet Declared(Symbol), // Declared but not (yet) initialized - Initialized { name: Symbol }, + Initialized { name: Symbol, initializer: &'hir Expr<'hir> }, DontWarn, } -/// Checks whether a variable is initialized to zero at the start of a loop and not modified +/// Checks whether a variable is initialized at the start of a loop and not modified /// and used after the loop. struct InitializeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, - state: InitializeVisitorState, + state: InitializeVisitorState<'tcx>, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2228,9 +2235,9 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } - fn get_result(&self) -> Option { - if let InitializeVisitorState::Initialized { name } = self.state { - Some(name) + fn get_result(&self) -> Option<(Name, &'tcx Expr<'tcx>)> { + if let InitializeVisitorState::Initialized { name, initializer } = self.state { + Some((name, initializer)) } else { None } @@ -2247,19 +2254,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.state = if_chain! { - if let Some(ref init) = local.init; - if is_integer_const(&self.cx, init, 0); - then { + self.state = if let Some(ref init) = local.init { InitializeVisitorState::Initialized { - name: ident.name + initializer: init, + name: ident.name, } } else { InitializeVisitorState::Declared(ident.name) } } } - } walk_stmt(self, stmt); } @@ -2291,11 +2295,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { }, ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { self.state = if_chain! { - if is_integer_const(&self.cx, rhs, 0) && self.depth == 0; + if self.depth == 0; if let InitializeVisitorState::Declared(name) | InitializeVisitorState::Initialized { name, ..} = self.state; then { - InitializeVisitorState::Initialized { name } + InitializeVisitorState::Initialized { initializer: rhs, name } } else { InitializeVisitorState::DontWarn } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 92cb31fcf85..a0ddcce111e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -707,7 +707,7 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, } /// Gets the parent expression, if any –- this is useful to constrain a lint. -pub fn get_parent_expr<'c>(cx: &'c LateContext<'_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> { +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { let map = &cx.tcx.hir(); let hir_id = e.hir_id; let parent_id = map.get_parent_node(hir_id); -- cgit 1.4.1-3-g733a5 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: Rename variables --- clippy_lints/src/loops.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index bf067c70a7e..1c316c00f43 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1539,18 +1539,18 @@ fn check_for_loop_explicit_counter<'tcx>( expr: &'tcx Expr<'_>, ) { // Look for variables that are incremented once per loop iteration. - let mut visitor = IncrementVisitor::new(cx); - walk_expr(&mut visitor, body); + let mut increment_visitor = IncrementVisitor::new(cx); + walk_expr(&mut increment_visitor, body); // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for id in visitor.into_results() { - let mut visitor2 = InitializeVisitor::new(cx, expr, id); - walk_block(&mut visitor2, block); + for id in increment_visitor.into_results() { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); + walk_block(&mut initialize_visitor, block); if_chain! { - if let Some((name, initializer)) = visitor2.get_result(); + if let Some((name, initializer)) = initialize_visitor.get_result(); if is_integer_const(cx, initializer, 0); then { let mut applicability = Applicability::MachineApplicable; -- cgit 1.4.1-3-g733a5 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: Rename a struct and variables --- clippy_lints/src/loops.rs | 65 ++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 32 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c316c00f43..d9896f7fd6b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -820,9 +820,9 @@ impl Offset { } } -struct FixedOffsetVar<'hir> { - var: &'hir Expr<'hir>, - offset: Offset, +struct IndexExpr<'hir> { + base: &'hir Expr<'hir>, + idx_offset: Offset, } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { @@ -845,14 +845,14 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Option { - fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, var: HirId) -> Option { +fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Option { + fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, start: HirId) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, - ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + ExprKind::Path(..) if !same_var(cx, e, start) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), _ => None, } } @@ -860,20 +860,20 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Optio match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if same_var(cx, lhs, var) { - extract_offset(cx, rhs, var) - } else if same_var(cx, rhs, var) { - extract_offset(cx, lhs, var) + let offset_opt = if same_var(cx, lhs, start) { + extract_offset(cx, rhs, start) + } else if same_var(cx, rhs, start) { + extract_offset(cx, lhs, start) } else { None }; offset_opt.map(Offset::positive) }, - BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), + BinOpKind::Sub if same_var(cx, lhs, start) => extract_offset(cx, rhs, start).map(Offset::negative), _ => None, }, - ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), + ExprKind::Path(..) if same_var(cx, idx, start) => Some(Offset::positive("0".into())), _ => None, } } @@ -916,8 +916,8 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, - dst_var: FixedOffsetVar<'_>, - src_var: FixedOffsetVar<'_>, + dst: IndexExpr<'_>, + src: IndexExpr<'_>, ) -> String { fn print_sum(arg1: &str, arg2: &Offset) -> String { match (arg1, &arg2.value[..], arg2.sign) { @@ -944,13 +944,13 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { + let print_limit = |end: &Expr<'_>, offset: Offset, base: &Expr<'_>| { if_chain! { if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; if method.ident.name == sym!(len); if len_args.len() == 1; if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, var); + if var_def_id(cx, arg) == var_def_id(cx, base); then { match offset.sign { OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), @@ -971,25 +971,26 @@ fn build_manual_memcpy_suggestion<'tcx>( }; let start_str = snippet(cx, start.span, "").to_string(); - let dst_offset = print_offset(&start_str, &dst_var.offset); - let dst_limit = print_limit(end, dst_var.offset, dst_var.var); - let src_offset = print_offset(&start_str, &src_var.offset); - let src_limit = print_limit(end, src_var.offset, src_var.var); + let dst_offset = print_offset(&start_str, &dst.idx_offset); + let dst_limit = print_limit(end, dst.idx_offset, dst.base); + let src_offset = print_offset(&start_str, &src.idx_offset); + let src_limit = print_limit(end, src.idx_offset, src.base); - let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into()); - let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into()); + let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); + let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); let dst = if dst_offset == "" && dst_limit == "" { - dst_var_name + dst_base_str } else { - format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) + format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var_name, src_offset, src_limit + dst, src_base_str, src_offset, src_limit ) } + /// Checks for for loops that sequentially copy items from one slice-like /// object to another. fn detect_manual_memcpy<'tcx>( @@ -1014,18 +1015,18 @@ fn detect_manual_memcpy<'tcx>( o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); if_chain! { - if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind; - if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind; - if is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_left)) - && is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_right)); + if let ExprKind::Index(base_left, idx_left) = lhs.kind; + if let ExprKind::Index(base_right, idx_right) = rhs.kind; + if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) + && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); // Source and destination must be different - if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right); + if var_def_id(cx, base_left) != var_def_id(cx, base_right); then { - Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left }, - FixedOffsetVar { var: seqexpr_right, offset: offset_right })) + Some((IndexExpr { base: base_left, idx_offset: offset_left }, + IndexExpr { base: base_right, idx_offset: offset_right })) } else { None } -- cgit 1.4.1-3-g733a5 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: Introduce Start and StartKind --- clippy_lints/src/loops.rs | 67 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d9896f7fd6b..157dff59d44 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -818,13 +818,29 @@ impl Offset { sign: OffsetSign::Positive, } } + + fn empty() -> Self { + Self::positive("0".into()) + } +} + +#[derive(Debug, Clone, Copy)] +enum StartKind<'hir> { + Range, + Counter { initializer: &'hir Expr<'hir> }, } struct IndexExpr<'hir> { base: &'hir Expr<'hir>, + idx: StartKind<'hir>, idx_offset: Offset, } +struct Start<'hir> { + id: HirId, + kind: StartKind<'hir>, +} + fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { let is_slice = match ty.kind() { ty::Ref(_, subty, _) => is_slice_like(cx, subty), @@ -845,14 +861,32 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Option { - fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, start: HirId) -> Option { +fn get_offset<'tcx>( + cx: &LateContext<'tcx>, + idx: &Expr<'_>, + starts: &[Start<'tcx>], +) -> Option<(StartKind<'tcx>, Offset)> { + fn extract_start<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, + starts: &[Start<'tcx>], + ) -> Option> { + starts.iter().find(|var| same_var(cx, expr, var.id)).map(|v| v.kind) + } + + fn extract_offset<'tcx>( + cx: &LateContext<'tcx>, + e: &Expr<'_>, + starts: &[Start<'tcx>], + ) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, - ExprKind::Path(..) if !same_var(cx, e, start) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { + Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())) + }, _ => None, } } @@ -860,20 +894,21 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Opt match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if same_var(cx, lhs, start) { - extract_offset(cx, rhs, start) - } else if same_var(cx, rhs, start) { - extract_offset(cx, lhs, start) + let offset_opt = if let Some(s) = extract_start(cx, lhs, starts) { + extract_offset(cx, rhs, starts).map(|o| (s, o)) + } else if let Some(s) = extract_start(cx, rhs, starts) { + extract_offset(cx, lhs, starts).map(|o| (s, o)) } else { None }; - offset_opt.map(Offset::positive) + offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, - BinOpKind::Sub if same_var(cx, lhs, start) => extract_offset(cx, rhs, start).map(Offset::negative), + BinOpKind::Sub => extract_start(cx, lhs, starts) + .and_then(|s| extract_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))), _ => None, }, - ExprKind::Path(..) if same_var(cx, idx, start) => Some(Offset::positive("0".into())), + ExprKind::Path(..) => extract_start(cx, idx, starts).map(|s| (s, Offset::empty())), _ => None, } } @@ -1008,6 +1043,10 @@ fn detect_manual_memcpy<'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; // The only statements in the for loops can be indexed assignments from // indexed retrievals. let big_sugg = get_assignments(body) @@ -1019,14 +1058,14 @@ fn detect_manual_memcpy<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); - if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); + if let Some((start_left, offset_left)) = get_offset(cx, &idx_left, &starts); + if let Some((start_right, offset_right)) = get_offset(cx, &idx_right, &starts); // Source and destination must be different if var_def_id(cx, base_left) != var_def_id(cx, base_right); then { - Some((IndexExpr { base: base_left, idx_offset: offset_left }, - IndexExpr { base: base_right, idx_offset: offset_right })) + Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None } -- cgit 1.4.1-3-g733a5 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: Implement detecting `manual_memcpy` with loop counters --- clippy_lints/src/loops.rs | 90 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 24 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 157dff59d44..c036d63c335 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -913,37 +913,62 @@ fn get_offset<'tcx>( } } -fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator, &'tcx Expr<'tcx>)>> { - fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if let ExprKind::Assign(lhs, rhs, _) = e.kind { - Some((lhs, rhs)) - } else { - None - } +fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if let ExprKind::Assign(lhs, rhs, _) = e.kind { + Some((lhs, rhs)) + } else { + None } +} - // This is one of few ways to return different iterators - // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 - let mut iter_a = None; - let mut iter_b = None; +fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( + cx: &'a LateContext<'a, 'tcx>, + stmts: &'tcx [Stmt<'tcx>], + expr: Option<&'tcx Expr<'tcx>>, + loop_counters: &'c [Start<'tcx>], +) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { + stmts + .iter() + .filter_map(move |stmt| match stmt.kind { + StmtKind::Local(..) | StmtKind::Item(..) => None, + StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { + if let ExprKind::AssignOp(_, var, _) = e.kind; + // skip StartKind::Range + if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); + then { None } else { Some(e) } + }, + }) + .chain(expr.into_iter()) + .map(get_assignment) +} - if let ExprKind::Block(b, _) = body.kind { - let Block { stmts, expr, .. } = *b; +fn get_loop_counters<'a, 'tcx>( + cx: &'a LateContext<'a, 'tcx>, + body: &'tcx Block<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option> + 'a> { + // Look for variables that are incremented once per loop iteration. + let mut increment_visitor = IncrementVisitor::new(cx); + walk_block(&mut increment_visitor, body); - iter_a = stmts - .iter() - .filter_map(|stmt| match stmt.kind { - StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), + // For each candidate, check the parent block to see if + // it's initialized to zero at the start of the loop. + if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + increment_visitor + .into_results() + .filter_map(move |var_id| { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id); + walk_block(&mut initialize_visitor, block); + + initialize_visitor.get_result().map(|(_, initializer)| Start { + id: var_id, + kind: StartKind::Counter { initializer }, + }) }) - .chain(expr.into_iter()) - .map(get_assignment) .into() } else { - iter_b = Some(get_assignment(body)) + None } - - iter_a.into_iter().flatten().chain(iter_b.into_iter()) } fn build_manual_memcpy_suggestion<'tcx>( @@ -1047,9 +1072,26 @@ fn detect_manual_memcpy<'tcx>( id: canonical_id, kind: StartKind::Range, }]; + + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); + } + iter_a = Some(get_assignments(cx, block.stmts, block.expr, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } + // The only statements in the for loops can be indexed assignments from // indexed retrievals. - let big_sugg = get_assignments(body) + let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); + + let big_sugg = assignments .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c036d63c335..bd2ae47b723 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -800,19 +800,19 @@ enum OffsetSign { } struct Offset { - value: String, + value: MinifyingSugg<'static>, sign: OffsetSign, } impl Offset { - fn negative(value: String) -> Self { + fn negative(value: MinifyingSugg<'static>) -> Self { Self { value, sign: OffsetSign::Negative, } } - fn positive(value: String) -> Self { + fn positive(value: MinifyingSugg<'static>) -> Self { Self { value, sign: OffsetSign::Positive, @@ -820,7 +820,88 @@ impl Offset { } fn empty() -> Self { - Self::positive("0".into()) + Self::positive(MinifyingSugg::non_paren("0")) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + +#[derive(Clone)] +struct MinifyingSugg<'a>(sugg::Sugg<'a>); + +impl std::fmt::Display for MinifyingSugg<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Display::fmt(&self.0, f) + } +} + +impl<'a> MinifyingSugg<'a> { + fn as_str(&self) -> &str { + let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0; + s.as_ref() + } + + fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self { + Self(sugg::Sugg::hir(cx, expr, default)) + } + + fn maybe_par(self) -> Self { + Self(self.0.maybe_par()) + } + + fn non_paren(str: impl Into>) -> Self { + Self(sugg::Sugg::NonParen(str.into())) + } +} + +impl std::ops::Add for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self.clone(), + (_, _) => MinifyingSugg(&self.0 + &rhs.0), + } + } +} + +impl std::ops::Sub for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self.clone(), + ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + (x, y) if x == y => MinifyingSugg::non_paren("0"), + (_, _) => MinifyingSugg(&self.0 - &rhs.0), + } + } +} + +impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self, + (_, _) => MinifyingSugg(self.0 + &rhs.0), + } + } +} + +impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self, + ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + (x, y) if x == y => MinifyingSugg::non_paren("0"), + (_, _) => MinifyingSugg(self.0 - &rhs.0), + } } } @@ -878,14 +959,15 @@ fn get_offset<'tcx>( cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>], - ) -> Option { + ) -> Option> { match &e.kind { ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(x.to_string()), + ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), _ => None, }, ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { - Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())) + // `e` is always non paren as it's a `Path` + Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) }, _ => None, } @@ -979,32 +1061,15 @@ fn build_manual_memcpy_suggestion<'tcx>( dst: IndexExpr<'_>, src: IndexExpr<'_>, ) -> String { - fn print_sum(arg1: &str, arg2: &Offset) -> String { - match (arg1, &arg2.value[..], arg2.sign) { - ("0", "0", _) => "0".into(), - ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), - ("0", x, OffsetSign::Negative) => format!("-{}", x), - (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), - (x, y, OffsetSign::Negative) => { - if x == y { - "0".into() - } else { - format!("({} - {})", x, y) - } - }, - } - } - - fn print_offset(start_str: &str, inline_offset: &Offset) -> String { - let offset = print_sum(start_str, inline_offset); + fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { - "".into() + MinifyingSugg::non_paren("") } else { offset } } - let print_limit = |end: &Expr<'_>, offset: Offset, base: &Expr<'_>| { + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { if_chain! { if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; if method.ident.name == sym!(len); @@ -1012,42 +1077,72 @@ fn build_manual_memcpy_suggestion<'tcx>( if let Some(arg) = len_args.get(0); if var_def_id(cx, arg) == var_def_id(cx, base); then { - match offset.sign { - OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), - OffsetSign::Positive => "".into(), + if sugg.as_str() == end_str { + MinifyingSugg::non_paren("") + } else { + sugg } } else { - let end_str = match limits { + match limits { ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) + sugg + &MinifyingSugg::non_paren("1") }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&end_str, &offset) + ast::RangeLimits::HalfOpen => sugg, + } } } }; - let start_str = snippet(cx, start.span, "").to_string(); - let dst_offset = print_offset(&start_str, &dst.idx_offset); - let dst_limit = print_limit(end, dst.idx_offset, dst.base); - let src_offset = print_offset(&start_str, &src.idx_offset); - let src_limit = print_limit(end, src.idx_offset, src.base); + let start_str = MinifyingSugg::hir(cx, start, ""); + let end_str = MinifyingSugg::hir(cx, end, ""); + + let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { + StartKind::Range => ( + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset), + ), + ), + StartKind::Counter { initializer } => { + let counter_start = MinifyingSugg::hir(cx, initializer, ""); + ( + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, + ), + ) + }, + }; + + let (dst_offset, dst_limit) = print_offset_and_limit(&dst); + let (src_offset, src_limit) = print_offset_and_limit(&src); let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); - let dst = if dst_offset == "" && dst_limit == "" { + let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { dst_base_str } else { - format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit) + format!( + "{}[{}..{}]", + dst_base_str, + dst_offset.maybe_par(), + dst_limit.maybe_par() + ) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_base_str, src_offset, src_limit + dst, + src_base_str, + src_offset.maybe_par(), + src_limit.maybe_par() ) } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index ec8b7e59b59..50d48650a09 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -36,6 +36,46 @@ impl Display for Sugg<'_> { } } +// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp +impl Clone for Sugg<'_> { + fn clone(&self) -> Self { + /// manually cloning AssocOp + fn clone_assoc_op(this: &AssocOp) -> AssocOp { + match this { + AssocOp::Add => AssocOp::Add, + AssocOp::Subtract => AssocOp::Subtract, + AssocOp::Multiply => AssocOp::Multiply, + AssocOp::Divide => AssocOp::Divide, + AssocOp::Modulus => AssocOp::Modulus, + AssocOp::LAnd => AssocOp::LAnd, + AssocOp::LOr => AssocOp::LOr, + AssocOp::BitXor => AssocOp::BitXor, + AssocOp::BitAnd => AssocOp::BitAnd, + AssocOp::BitOr => AssocOp::BitOr, + AssocOp::ShiftLeft => AssocOp::ShiftLeft, + AssocOp::ShiftRight => AssocOp::ShiftRight, + AssocOp::Equal => AssocOp::Equal, + AssocOp::Less => AssocOp::Less, + AssocOp::LessEqual => AssocOp::LessEqual, + AssocOp::NotEqual => AssocOp::NotEqual, + AssocOp::Greater => AssocOp::Greater, + AssocOp::GreaterEqual => AssocOp::GreaterEqual, + AssocOp::Assign => AssocOp::Assign, + AssocOp::AssignOp(t) => AssocOp::AssignOp(*t), + AssocOp::As => AssocOp::As, + AssocOp::DotDot => AssocOp::DotDot, + AssocOp::DotDotEq => AssocOp::DotDotEq, + AssocOp::Colon => AssocOp::Colon, + } + } + match self { + Sugg::NonParen(x) => Sugg::NonParen(x.clone()), + Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()), + Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()), + } + } +} + #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. @@ -267,21 +307,49 @@ impl<'a> Sugg<'a> { } } -impl<'a, 'b> std::ops::Add> for Sugg<'a> { +impl std::ops::Add for Sugg<'_> { type Output = Sugg<'static>; - fn add(self, rhs: Sugg<'b>) -> Sugg<'static> { + fn add(self, rhs: Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Add, &self, &rhs) } } -impl<'a, 'b> std::ops::Sub> for Sugg<'a> { +impl std::ops::Sub for Sugg<'_> { type Output = Sugg<'static>; - fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> { + fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Sub, &self, &rhs) } } -impl<'a> std::ops::Not for Sugg<'a> { +impl std::ops::Add<&Sugg<'_>> for Sugg<'_> { + type Output = Sugg<'static>; + fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, &self, rhs) + } +} + +impl std::ops::Sub<&Sugg<'_>> for Sugg<'_> { + type Output = Sugg<'static>; + fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, &self, rhs) + } +} + +impl std::ops::Add for &Sugg<'_> { + type Output = Sugg<'static>; + fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, self, rhs) + } +} + +impl std::ops::Sub for &Sugg<'_> { + type Output = Sugg<'static>; + fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, self, rhs) + } +} + +impl std::ops::Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { make_unop("!", self) -- cgit 1.4.1-3-g733a5 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: fmt --- clippy_lints/src/loops.rs | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index bd2ae47b723..2aee3b93e82 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1069,29 +1069,30 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { - if_chain! { - if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, base); - then { - if sugg.as_str() == end_str { - MinifyingSugg::non_paren("") + let print_limit = + |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if var_def_id(cx, arg) == var_def_id(cx, base); + then { + if sugg.as_str() == end_str { + MinifyingSugg::non_paren("") + } else { + sugg + } } else { - sugg - } - } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &MinifyingSugg::non_paren("1") - }, - ast::RangeLimits::HalfOpen => sugg, + match limits { + ast::RangeLimits::Closed => { + sugg + &MinifyingSugg::non_paren("1") + }, + ast::RangeLimits::HalfOpen => sugg, + } } } - } - }; + }; let start_str = MinifyingSugg::hir(cx, start, ""); let end_str = MinifyingSugg::hir(cx, end, ""); -- cgit 1.4.1-3-g733a5 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: Rename `get_offset` and its private items --- clippy_lints/src/loops.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2aee3b93e82..98c411f5ae6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -942,20 +942,20 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>( +fn get_details_from_idx<'tcx>( cx: &LateContext<'tcx>, idx: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { - fn extract_start<'tcx>( + fn get_start<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + e: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option> { - starts.iter().find(|var| same_var(cx, expr, var.id)).map(|v| v.kind) + starts.iter().find(|var| same_var(cx, e, var.id)).map(|v| v.kind) } - fn extract_offset<'tcx>( + fn get_offset<'tcx>( cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>], @@ -965,7 +965,7 @@ fn get_offset<'tcx>( ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), _ => None, }, - ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { + ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { // `e` is always non paren as it's a `Path` Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) }, @@ -976,21 +976,22 @@ fn get_offset<'tcx>( match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if let Some(s) = extract_start(cx, lhs, starts) { - extract_offset(cx, rhs, starts).map(|o| (s, o)) - } else if let Some(s) = extract_start(cx, rhs, starts) { - extract_offset(cx, lhs, starts).map(|o| (s, o)) + let offset_opt = if let Some(s) = get_start(cx, lhs, starts) { + get_offset(cx, rhs, starts).map(|o| (s, o)) + } else if let Some(s) = get_start(cx, rhs, starts) { + get_offset(cx, lhs, starts).map(|o| (s, o)) } else { None }; offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, - BinOpKind::Sub => extract_start(cx, lhs, starts) - .and_then(|s| extract_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))), + BinOpKind::Sub => { + get_start(cx, lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))) + }, _ => None, }, - ExprKind::Path(..) => extract_start(cx, idx, starts).map(|s| (s, Offset::empty())), + ExprKind::Path(..) => get_start(cx, idx, starts).map(|s| (s, Offset::empty())), _ => None, } } @@ -1196,8 +1197,8 @@ fn detect_manual_memcpy<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - if let Some((start_left, offset_left)) = get_offset(cx, &idx_left, &starts); - if let Some((start_right, offset_right)) = get_offset(cx, &idx_right, &starts); + if let Some((start_left, offset_left)) = get_details_from_idx(cx, &idx_left, &starts); + if let Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts); // Source and destination must be different if var_def_id(cx, base_left) != var_def_id(cx, base_right); -- cgit 1.4.1-3-g733a5 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: make use of macros in operator overloading --- clippy_lints/src/utils/sugg.rs | 55 +++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 50d48650a09..aada4122e78 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,6 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; +use std::ops::{Add, Sub, Not}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -307,49 +308,53 @@ impl<'a> Sugg<'a> { } } -impl std::ops::Add for Sugg<'_> { - type Output = Sugg<'static>; - fn add(self, rhs: Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Add, &self, &rhs) - } -} +// Copied from the rust standart library, and then edited +macro_rules! forward_binop_impls_to_ref { + (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { + impl $imp<$t> for &$t { + type Output = $o; -impl std::ops::Sub for Sugg<'_> { - type Output = Sugg<'static>; - fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Sub, &self, &rhs) - } -} + fn $method(self, other: $t) -> $o { + $imp::$method(self, &other) + } + } -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 $imp<&$t> for $t { + type Output = $o; -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) + 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::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) -- cgit 1.4.1-3-g733a5 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: fmt --- clippy_lints/src/utils/sugg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index aada4122e78..8f2aa724d20 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,7 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use std::ops::{Add, Sub, Not}; +use std::ops::{Add, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -334,7 +334,7 @@ macro_rules! forward_binop_impls_to_ref { $imp::$method(&self, &other) } } - } + }; } impl Add for &Sugg<'_> { -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 98c411f5ae6..5928d12f30b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -875,7 +875,7 @@ impl std::ops::Sub for &MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self.clone(), - ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + ("0", _) => MinifyingSugg(-(rhs.0.clone())), (x, y) if x == y => MinifyingSugg::non_paren("0"), (_, _) => MinifyingSugg(&self.0 - &rhs.0), } @@ -898,7 +898,7 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self, - ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + ("0", _) => MinifyingSugg(-(rhs.0.clone())), (x, y) if x == y => MinifyingSugg::non_paren("0"), (_, _) => MinifyingSugg(self.0 - &rhs.0), } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 8f2aa724d20..1f448f86d72 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,7 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use std::ops::{Add, Not, Sub}; +use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -354,6 +354,13 @@ impl Sub for &Sugg<'_> { forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); +impl Neg for Sugg<'_> { + type Output = Sugg<'static>; + fn neg(self) -> Sugg<'static> { + make_unop("-", self) + } +} + impl Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5928d12f30b..f684e7de8f8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -952,7 +952,13 @@ fn get_details_from_idx<'tcx>( e: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option> { - starts.iter().find(|var| same_var(cx, e, var.id)).map(|v| v.kind) + starts.iter().find_map(|start| { + if same_var(cx, e, start.id) { + Some(start.kind) + } else { + None + } + }) } fn get_offset<'tcx>( @@ -1059,8 +1065,8 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, - dst: IndexExpr<'_>, - src: IndexExpr<'_>, + dst: &IndexExpr<'_>, + src: &IndexExpr<'_>, ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { @@ -1211,7 +1217,7 @@ fn detect_manual_memcpy<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src))) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) .collect::>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -2319,10 +2325,13 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { } fn into_results(self) -> impl Iterator { - self.states - .into_iter() - .filter(|(_, state)| *state == IncrementVisitorVarState::IncrOnce) - .map(|(id, _)| id) + self.states.into_iter().filter_map(|(id, state)| { + if state == IncrementVisitorVarState::IncrOnce { + Some(id) + } else { + None + } + }) } } -- cgit 1.4.1-3-g733a5 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: use `#[derive]` instead of the manual implementation --- clippy_lints/src/utils/sugg.rs | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 1f448f86d72..062b273c0f4 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -16,6 +16,7 @@ use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. +#[derive(Clone)] pub enum Sugg<'a> { /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -37,46 +38,6 @@ impl Display for Sugg<'_> { } } -// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp -impl Clone for Sugg<'_> { - fn clone(&self) -> Self { - /// manually cloning AssocOp - fn clone_assoc_op(this: &AssocOp) -> AssocOp { - match this { - AssocOp::Add => AssocOp::Add, - AssocOp::Subtract => AssocOp::Subtract, - AssocOp::Multiply => AssocOp::Multiply, - AssocOp::Divide => AssocOp::Divide, - AssocOp::Modulus => AssocOp::Modulus, - AssocOp::LAnd => AssocOp::LAnd, - AssocOp::LOr => AssocOp::LOr, - AssocOp::BitXor => AssocOp::BitXor, - AssocOp::BitAnd => AssocOp::BitAnd, - AssocOp::BitOr => AssocOp::BitOr, - AssocOp::ShiftLeft => AssocOp::ShiftLeft, - AssocOp::ShiftRight => AssocOp::ShiftRight, - AssocOp::Equal => AssocOp::Equal, - AssocOp::Less => AssocOp::Less, - AssocOp::LessEqual => AssocOp::LessEqual, - AssocOp::NotEqual => AssocOp::NotEqual, - AssocOp::Greater => AssocOp::Greater, - AssocOp::GreaterEqual => AssocOp::GreaterEqual, - AssocOp::Assign => AssocOp::Assign, - AssocOp::AssignOp(t) => AssocOp::AssignOp(*t), - AssocOp::As => AssocOp::As, - AssocOp::DotDot => AssocOp::DotDot, - AssocOp::DotDotEq => AssocOp::DotDotEq, - AssocOp::Colon => AssocOp::Colon, - } - } - match self { - Sugg::NonParen(x) => Sugg::NonParen(x.clone()), - Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()), - Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()), - } - } -} - #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. -- cgit 1.4.1-3-g733a5 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: Reflect the changes that has been made and fmt --- clippy_lints/src/loops.rs | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f684e7de8f8..4ff567ffb0e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -846,7 +846,7 @@ impl<'a> MinifyingSugg<'a> { s.as_ref() } - fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self { + fn hir(cx: &LateContext<'_>, expr: &Expr<'_>, default: &'a str) -> Self { Self(sugg::Sugg::hir(cx, expr, default)) } @@ -947,11 +947,7 @@ fn get_details_from_idx<'tcx>( idx: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { - fn get_start<'tcx>( - cx: &LateContext<'tcx>, - e: &Expr<'_>, - starts: &[Start<'tcx>], - ) -> Option> { + fn get_start<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { starts.iter().find_map(|start| { if same_var(cx, e, start.id) { Some(start.kind) @@ -982,13 +978,9 @@ fn get_details_from_idx<'tcx>( match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if let Some(s) = get_start(cx, lhs, starts) { - get_offset(cx, rhs, starts).map(|o| (s, o)) - } else if let Some(s) = get_start(cx, rhs, starts) { - get_offset(cx, lhs, starts).map(|o| (s, o)) - } else { - None - }; + let offset_opt = get_start(cx, lhs, starts) + .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o))) + .or_else(|| get_start(cx, rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o)))); offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, @@ -1011,7 +1003,7 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx } fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( - cx: &'a LateContext<'a, 'tcx>, + cx: &'a LateContext<'tcx>, stmts: &'tcx [Stmt<'tcx>], expr: Option<&'tcx Expr<'tcx>>, loop_counters: &'c [Start<'tcx>], @@ -1032,7 +1024,7 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( } fn get_loop_counters<'a, 'tcx>( - cx: &'a LateContext<'a, 'tcx>, + cx: &'a LateContext<'tcx>, body: &'tcx Block<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> + 'a> { @@ -1042,7 +1034,7 @@ fn get_loop_counters<'a, 'tcx>( // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. - if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + get_enclosing_block(&cx, expr.hir_id).and_then(|block| { increment_visitor .into_results() .filter_map(move |var_id| { @@ -1055,9 +1047,7 @@ fn get_loop_counters<'a, 'tcx>( }) }) .into() - } else { - None - } + }) } fn build_manual_memcpy_suggestion<'tcx>( @@ -2315,7 +2305,7 @@ struct IncrementVisitor<'a, 'tcx> { } impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, states: FxHashMap::default(), @@ -2396,7 +2386,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { enum InitializeVisitorState<'hir> { Initial, // Not examined yet Declared(Symbol), // Declared but not (yet) initialized - Initialized { name: Symbol, initializer: &'hir Expr<'hir> }, + Initialized { + name: Symbol, + initializer: &'hir Expr<'hir>, + }, DontWarn, } @@ -2412,7 +2405,7 @@ struct InitializeVisitor<'a, 'tcx> { } impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'a, 'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { Self { cx, end_expr, @@ -2423,7 +2416,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } - fn get_result(&self) -> Option<(Name, &'tcx Expr<'tcx>)> { + fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> { if let InitializeVisitorState::Initialized { name, initializer } = self.state { Some((name, initializer)) } else { @@ -2442,14 +2435,12 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.state = if let Some(ref init) = local.init { + self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| { InitializeVisitorState::Initialized { initializer: init, name: ident.name, } - } else { - InitializeVisitorState::Declared(ident.name) - } + }) } } walk_stmt(self, stmt); -- cgit 1.4.1-3-g733a5 From 5b484b405748fc8d7476f9a8d68d2e7227767271 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 25 Sep 2020 23:32:18 +0900 Subject: Fix the detection of build scripts --- clippy_lints/src/write.rs | 33 ++++++++++++++----------------- tests/ui/build.rs | 10 ---------- tests/ui/build.stderr | 0 tests/ui/print_stdout_build_script.rs | 12 +++++++++++ tests/ui/print_stdout_build_script.stderr | 0 5 files changed, 27 insertions(+), 28 deletions(-) delete mode 100644 tests/ui/build.rs delete mode 100644 tests/ui/build.stderr create mode 100644 tests/ui/print_stdout_build_script.rs create mode 100644 tests/ui/print_stdout_build_script.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 780d474ee96..0e9c7098af8 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::ops::Range; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; @@ -12,7 +11,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, FileName, Span}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -236,15 +235,19 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + fn is_build_scripts(cx: &EarlyContext<'_>) -> bool { + // We could leverage the fact that Cargo sets the crate name + // for build scripts to `build_script_build`. + cx.sess + .opts + .crate_name + .as_ref() + .map_or(false, |crate_name| crate_name == "build_script_build") + } + if mac.path == sym!(println) { - let filename = cx.sess.source_map().span_to_filename(mac.span()); - if_chain! { - if let FileName::Real(filename) = filename; - if let Some(filename) = filename.local_path().file_name(); - if filename != "build.rs"; - then { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - } + if !is_build_scripts(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { @@ -260,14 +263,8 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - if_chain! { - let filename = cx.sess.source_map().span_to_filename(mac.span()); - if let FileName::Real(filename) = filename; - if let Some(filename) = filename.local_path().file_name(); - if filename != "build.rs"; - then { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - } + if !is_build_scripts(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { diff --git a/tests/ui/build.rs b/tests/ui/build.rs deleted file mode 100644 index 2d43d452a4f..00000000000 --- a/tests/ui/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![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 deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs new file mode 100644 index 00000000000..b84bf9124fc --- /dev/null +++ b/tests/ui/print_stdout_build_script.rs @@ -0,0 +1,12 @@ +// compile-flags: --crate-name=build_script_build + +#![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/print_stdout_build_script.stderr b/tests/ui/print_stdout_build_script.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 1479c18396d764482aa0e56b372c5f57a97c102b Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 15:32:03 -0500 Subject: 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 ++ tests/ui-toml/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d1dfe36ffd8..575cbd60792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1559,6 +1559,7 @@ Released 2018-09-13 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs new file mode 100644 index 00000000000..7088b2718f2 --- /dev/null +++ b/clippy_lints/src/disallowed_method.rs @@ -0,0 +1,75 @@ +use crate::utils::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{impl_lint_pass, declare_tool_lint}; +use rustc_hir::*; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Lints for specific trait methods defined in clippy.toml + /// + /// **Why is this bad?** Some methods are undesirable in certain contexts, + /// and it would be beneficial to lint for them as needed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// foo.bad_method(); // Foo is disallowed + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// GoodStruct.bad_method(); // not disallowed + /// ``` + pub DISALLOWED_METHOD, + nursery, + "used disallowed method call" +} + +#[derive(Clone, Debug)] +pub struct DisallowedMethod { + disallowed: FxHashSet>, +} + +impl DisallowedMethod { + pub fn new(disallowed: FxHashSet) -> Self { + Self { + disallowed: disallowed.iter() + .map(|s| { + s.split("::").map(|seg| Symbol::intern(seg)).collect::>() + }) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); + +impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(path, _, _args, _) = &expr.kind { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + + let method_call = cx.get_def_path(def_id); + if self.disallowed.contains(&method_call) { + span_lint( + cx, + DISALLOWED_METHOD, + expr.span, + &format!( + "Use of a disallowed method `{}`", + method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"), + ) + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58112ac8da5..7c886ab87d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -175,6 +175,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod disallowed_method; mod doc; mod double_comparison; mod double_parens; @@ -525,6 +526,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1118,6 +1120,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(disallowed_methods.clone())); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1807,6 +1812,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c5a12ea9c8..07591ce2229 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -164,6 +164,8 @@ define_Conf! { (max_fn_params_bools, "max_fn_params_bools": u64, 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), + /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses + (disallowed_methods, "disallowed_methods": Vec, ["disallowed_method::Foo::bad_method", "disallowed_method::Baz::bad_method", "disallowed_method::Quux::bad_method"].iter().map(ToString::to_string).collect()), } impl Default for Conf { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 9603023ed06..a77bbcb0abe 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "disallowed_method", + group: "nursery", + desc: "default lint description", + deprecation: None, + module: "disallowed_method", + }, Lint { name: "diverging_sub_expression", group: "complexity", diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml new file mode 100644 index 00000000000..a1f515e443d --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -0,0 +1 @@ +disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"] diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs new file mode 100644 index 00000000000..3d3f0729abd --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs @@ -0,0 +1,13 @@ +#![warn(clippy::disallowed_method)] + +extern crate regex; +use regex::Regex; + +fn main() { + let a = vec![1, 2, 3, 4]; + let re = Regex::new(r"ab.*c").unwrap(); + + re.is_match("abc"); + + a.iter().sum::(); +} diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr new file mode 100644 index 00000000000..5da551cb430 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -0,0 +1,16 @@ +error: Use of a disallowed method `regex::re_unicode::Regex::is_match` + --> $DIR/conf_disallowed_method.rs:10:5 + | +LL | re.is_match("abc"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: Use of a disallowed method `core::iter::traits::iterator::Iterator::sum` + --> $DIR/conf_disallowed_method.rs:12:5 + | +LL | a.iter().sum::(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs new file mode 100644 index 00000000000..a54a04b4d2c --- /dev/null +++ b/tests/ui/disallowed_method.rs @@ -0,0 +1,56 @@ +#![warn(clippy::disallowed_method)] +#![allow(clippy::no_effect, clippy::many_single_char_names)] + +struct ImplStruct; + +trait Baz { + fn bad_method(self); +} + +impl Baz for ImplStruct { + fn bad_method(self) {} +} + +struct Foo; + +impl Foo { + fn bad_method(self) {} +} + +struct StaticStruct; + +trait Quux { + fn bad_method(); +} + +impl Quux for StaticStruct { + fn bad_method() {} +} + +struct NormalStruct; + +impl NormalStruct { + fn bad_method(self) {} +} + +struct AttrStruct { + bad_method: i32, +} + +fn main() { + let b = ImplStruct; + let f = Foo; + let c = ImplStruct; + let n = NormalStruct; + let a = AttrStruct{ bad_method: 5 }; + + // lint these + b.bad_method(); + c.bad_method(); + f.bad_method(); + // these are good + // good because not a method call (ExprKind => Call) + StaticStruct::bad_method(); + n.bad_method(); + a.bad_method; +} diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr new file mode 100644 index 00000000000..93dabf38cfc --- /dev/null +++ b/tests/ui/disallowed_method.stderr @@ -0,0 +1,22 @@ +error: Use of a disallowed method `disallowed_method::Baz::bad_method` + --> $DIR/disallowed_method.rs:48:5 + | +LL | b.bad_method(); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: Use of a disallowed method `disallowed_method::Baz::bad_method` + --> $DIR/disallowed_method.rs:49:5 + | +LL | c.bad_method(); + | ^^^^^^^^^^^^^^ + +error: Use of a disallowed method `disallowed_method::Foo::bad_method` + --> $DIR/disallowed_method.rs:50:5 + | +LL | f.bad_method(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 12e5637f757f4fd4cc2331619ebeca59934a910d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 15:36:38 -0500 Subject: update unused variable --- clippy_lints/src/disallowed_method.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 7088b2718f2..5ecdcc0e08a 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -51,7 +51,7 @@ impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(path, _, _args, _) = &expr.kind { + if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); let method_call = cx.get_def_path(def_id); -- cgit 1.4.1-3-g733a5 From e1b3f85e984c9d4fba3ef5360892c88990b8391d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:00:46 -0500 Subject: run cargo dev fmt --- clippy_lints/src/disallowed_method.rs | 15 +++++++-------- tests/ui/disallowed_method.rs | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 5ecdcc0e08a..42fbff8ff87 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,9 +1,9 @@ use crate::utils::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{impl_lint_pass, declare_tool_lint}; use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; declare_clippy_lint! { @@ -38,10 +38,9 @@ pub struct DisallowedMethod { impl DisallowedMethod { pub fn new(disallowed: FxHashSet) -> Self { Self { - disallowed: disallowed.iter() - .map(|s| { - s.split("::").map(|seg| Symbol::intern(seg)).collect::>() - }) + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), } } @@ -49,7 +48,7 @@ impl DisallowedMethod { impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); -impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { +impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); @@ -67,7 +66,7 @@ impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { .map(|s| s.to_ident_string()) .collect::>() .join("::"), - ) + ), ); } } diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs index a54a04b4d2c..46c9185268c 100644 --- a/tests/ui/disallowed_method.rs +++ b/tests/ui/disallowed_method.rs @@ -42,7 +42,7 @@ fn main() { let f = Foo; let c = ImplStruct; let n = NormalStruct; - let a = AttrStruct{ bad_method: 5 }; + let a = AttrStruct { bad_method: 5 }; // lint these b.bad_method(); -- cgit 1.4.1-3-g733a5 From 725a0ef8b1948e459d266cd0ab33dda0c8bc3708 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:26:29 -0500 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 42fbff8ff87..7638019340f 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,7 +1,7 @@ use crate::utils::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; @@ -36,7 +36,7 @@ pub struct DisallowedMethod { } impl DisallowedMethod { - pub fn new(disallowed: FxHashSet) -> Self { + pub fn new(disallowed: &FxHashSet) -> Self { Self { disallowed: disallowed .iter() diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7c886ab87d0..7b6efc660af 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1121,7 +1121,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); - store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(disallowed_methods.clone())); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ -- cgit 1.4.1-3-g733a5 From 3886edb05a2b570664f7249620766582e55a74aa Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:43:29 -0500 Subject: fix error message --- clippy_lints/src/disallowed_method.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 7638019340f..eea369924a6 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -16,14 +16,14 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust + /// ```rust,ignore /// // example code where clippy issues a warning /// foo.bad_method(); // Foo is disallowed /// ``` /// Use instead: - /// ```rust + /// ```rust,ignore /// // example code which does not raise clippy warning - /// GoodStruct.bad_method(); // not disallowed + /// goodStruct.bad_method(); // not disallowed /// ``` pub DISALLOWED_METHOD, nursery, -- cgit 1.4.1-3-g733a5 From f9da2946d81a973b3c25aa5f4739ac7e05c27278 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 09:38:19 -0500 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index eea369924a6..603f776e688 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// ``` pub DISALLOWED_METHOD, nursery, - "used disallowed method call" + "use of a disallowed method call" } #[derive(Clone, Debug)] @@ -55,18 +55,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { let method_call = cx.get_def_path(def_id); if self.disallowed.contains(&method_call) { + let method = method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"); + span_lint( cx, DISALLOWED_METHOD, expr.span, - &format!( - "Use of a disallowed method `{}`", - method_call - .iter() - .map(|s| s.to_ident_string()) - .collect::>() - .join("::"), - ), + &format!("use of a disallowed method `{}`", method), ); } } diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr index 5da551cb430..ed91b5a6796 100644 --- a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -1,4 +1,4 @@ -error: Use of a disallowed method `regex::re_unicode::Regex::is_match` +error: use of a disallowed method `regex::re_unicode::Regex::is_match` --> $DIR/conf_disallowed_method.rs:10:5 | LL | re.is_match("abc"); @@ -6,7 +6,7 @@ LL | re.is_match("abc"); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: Use of a disallowed method `core::iter::traits::iterator::Iterator::sum` +error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum` --> $DIR/conf_disallowed_method.rs:12:5 | LL | a.iter().sum::(); diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr index 93dabf38cfc..40db1b946d8 100644 --- a/tests/ui/disallowed_method.stderr +++ b/tests/ui/disallowed_method.stderr @@ -1,4 +1,4 @@ -error: Use of a disallowed method `disallowed_method::Baz::bad_method` +error: use of a disallowed method `disallowed_method::Baz::bad_method` --> $DIR/disallowed_method.rs:48:5 | LL | b.bad_method(); @@ -6,13 +6,13 @@ LL | b.bad_method(); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: Use of a disallowed method `disallowed_method::Baz::bad_method` +error: use of a disallowed method `disallowed_method::Baz::bad_method` --> $DIR/disallowed_method.rs:49:5 | LL | c.bad_method(); | ^^^^^^^^^^^^^^ -error: Use of a disallowed method `disallowed_method::Foo::bad_method` +error: use of a disallowed method `disallowed_method::Foo::bad_method` --> $DIR/disallowed_method.rs:50:5 | LL | f.bad_method(); -- cgit 1.4.1-3-g733a5 From d18653158ddde023f8165ef7ba3c607661398abf Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 11:03:45 -0500 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 603f776e688..581c3242e37 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -18,12 +18,12 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // example code where clippy issues a warning - /// foo.bad_method(); // Foo is disallowed + /// foo.bad_method(); // Foo::bad_method is disallowed in the configuration /// ``` /// Use instead: /// ```rust,ignore /// // example code which does not raise clippy warning - /// goodStruct.bad_method(); // not disallowed + /// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed /// ``` pub DISALLOWED_METHOD, nursery, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 07591ce2229..03f8c5a2c07 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -165,7 +165,7 @@ define_Conf! { /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses - (disallowed_methods, "disallowed_methods": Vec, ["disallowed_method::Foo::bad_method", "disallowed_method::Baz::bad_method", "disallowed_method::Quux::bad_method"].iter().map(ToString::to_string).collect()), + (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), } impl Default for Conf { diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs deleted file mode 100644 index 46c9185268c..00000000000 --- a/tests/ui/disallowed_method.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![warn(clippy::disallowed_method)] -#![allow(clippy::no_effect, clippy::many_single_char_names)] - -struct ImplStruct; - -trait Baz { - fn bad_method(self); -} - -impl Baz for ImplStruct { - fn bad_method(self) {} -} - -struct Foo; - -impl Foo { - fn bad_method(self) {} -} - -struct StaticStruct; - -trait Quux { - fn bad_method(); -} - -impl Quux for StaticStruct { - fn bad_method() {} -} - -struct NormalStruct; - -impl NormalStruct { - fn bad_method(self) {} -} - -struct AttrStruct { - bad_method: i32, -} - -fn main() { - let b = ImplStruct; - let f = Foo; - let c = ImplStruct; - let n = NormalStruct; - let a = AttrStruct { bad_method: 5 }; - - // lint these - b.bad_method(); - c.bad_method(); - f.bad_method(); - // these are good - // good because not a method call (ExprKind => Call) - StaticStruct::bad_method(); - n.bad_method(); - a.bad_method; -} diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr deleted file mode 100644 index 40db1b946d8..00000000000 --- a/tests/ui/disallowed_method.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: use of a disallowed method `disallowed_method::Baz::bad_method` - --> $DIR/disallowed_method.rs:48:5 - | -LL | b.bad_method(); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-method` implied by `-D warnings` - -error: use of a disallowed method `disallowed_method::Baz::bad_method` - --> $DIR/disallowed_method.rs:49:5 - | -LL | c.bad_method(); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `disallowed_method::Foo::bad_method` - --> $DIR/disallowed_method.rs:50:5 - | -LL | f.bad_method(); - | ^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 83294f894d477def457d54f2391a287bcb949f06 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 26 Sep 2020 23:10:25 +0900 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0e9c7098af8..d9d60fffcd7 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -235,9 +235,8 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { - fn is_build_scripts(cx: &EarlyContext<'_>) -> bool { - // We could leverage the fact that Cargo sets the crate name - // for build scripts to `build_script_build`. + fn is_build_script(cx: &EarlyContext<'_>) -> bool { + // Cargo sets the crate name for build scripts to `build_script_build` cx.sess .opts .crate_name @@ -246,7 +245,7 @@ impl EarlyLintPass for Write { } if mac.path == sym!(println) { - if !is_build_scripts(cx) { + if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { @@ -263,7 +262,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - if !is_build_scripts(cx) { + if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs index b84bf9124fc..997ebef8a69 100644 --- a/tests/ui/print_stdout_build_script.rs +++ b/tests/ui/print_stdout_build_script.rs @@ -5,7 +5,7 @@ fn main() { // Fix #6041 // - // The `print_stdout` shouldn't be linted in `build.rs` + // The `print_stdout` lint shouldn't emit in `build.rs` // as these methods are used for the build script. println!("Hello"); print!("Hello"); diff --git a/tests/ui/print_stdout_build_script.stderr b/tests/ui/print_stdout_build_script.stderr deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit 1.4.1-3-g733a5 From 00e641b91417a0b4489544406f60a1e2babe7c88 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Sat, 26 Sep 2020 17:19:12 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a29a199b8c3..17d950169fd 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -216,18 +216,19 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. /// - /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since - /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since + /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. /// - /// While mutating a buffer type would still be possible with Rc::get_mut(), it only - /// works if there are no additional references yet, which defeats the purpose of + /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only + /// works if there are no additional references yet, which usually defeats the purpose of /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner - /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally /// be used. /// - /// **Known problems:** None. + /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for + /// cases where mutation only happens before there are any additional references. /// /// **Example:** /// ```rust,ignore -- cgit 1.4.1-3-g733a5 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: Replace `snippet_opt` + `unwrap_or_else` with `snippet` --- clippy_lints/src/loops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4ff567ffb0e..647537933f7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,9 +5,8 @@ use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - SpanlessEq, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -1121,8 +1120,8 @@ fn build_manual_memcpy_suggestion<'tcx>( let (dst_offset, dst_limit) = print_offset_and_limit(&dst); let (src_offset, src_limit) = print_offset_and_limit(&src); - let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); - let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); + let dst_base_str = snippet(cx, dst.base.span, "???"); + let src_base_str = snippet(cx, src.base.span, "???"); let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { dst_base_str @@ -1133,6 +1132,7 @@ fn build_manual_memcpy_suggestion<'tcx>( dst_offset.maybe_par(), dst_limit.maybe_par() ) + .into() }; format!( -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 647537933f7..f2df53aee4f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -768,12 +768,14 @@ fn check_for_loop<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - check_for_loop_range(cx, pat, arg, body, expr); + let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); + if !is_manual_memcpy_triggered { + check_for_loop_range(cx, pat, arg, body, expr); + check_for_loop_explicit_counter(cx, pat, arg, body, expr); + } check_for_loop_arg(cx, pat, arg, expr); - check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); - detect_manual_memcpy(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1152,7 +1154,7 @@ fn detect_manual_memcpy<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, -) { +) -> bool { if let Some(higher::Range { start: Some(start), end: Some(end), @@ -1222,9 +1224,11 @@ fn detect_manual_memcpy<'tcx>( big_sugg, Applicability::Unspecified, ); + return true; } } } + false } // Scans the body of the for loop and determines whether lint should be given diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 8318fd89811..84758275dd7 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,7 +115,6 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } -#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { let mut count = 0; for i in 3..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index d189bde0508..464b18984fb 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -79,49 +79,49 @@ LL | for i in 0..0 { | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:121:14 + --> $DIR/manual_memcpy.rs:120:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:127:14 + --> $DIR/manual_memcpy.rs:126:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:133:14 + --> $DIR/manual_memcpy.rs:132:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:139:14 + --> $DIR/manual_memcpy.rs:138:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:145:14 + --> $DIR/manual_memcpy.rs:144:14 | LL | for i in 3..(3 + src.len()) { | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:151:14 + --> $DIR/manual_memcpy.rs:150:14 | LL | for i in 5..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:157:14 + --> $DIR/manual_memcpy.rs:156:14 | LL | for i in 3..10 { | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:164:14 + --> $DIR/manual_memcpy.rs:163:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:174:14 + --> $DIR/manual_memcpy.rs:173:14 | LL | for i in 0..1 << 1 { | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:182:14 + --> $DIR/manual_memcpy.rs:181:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f2df53aee4f..7c8b6f483bc 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -779,6 +779,17 @@ fn check_for_loop<'tcx>( detect_same_item_push(cx, pat, arg, body, expr); } +// this function assumes the given expression is a `for` loop. +fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { + // for some reason this is the only way to get the `Span` + // of the entire `for` loop + if let ExprKind::Match(_, arms, _) = &expr.kind { + arms[0].body.span + } else { + unreachable!() + } +} + fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if_chain! { if let ExprKind::Path(qpath) = &expr.kind; @@ -1138,7 +1149,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.clone_from_slice(&{}[{}..{}])", + "{}.clone_from_slice(&{}[{}..{}]);", dst, src_base_str, src_offset.maybe_par(), @@ -1218,7 +1229,7 @@ fn detect_manual_memcpy<'tcx>( span_lint_and_sugg( cx, MANUAL_MEMCPY, - expr.span, + get_span_of_entire_for_loop(expr), "it looks like you're manually copying between slices", "try replacing the loop by", big_sugg, @@ -1734,13 +1745,7 @@ fn check_for_loop_explicit_counter<'tcx>( then { let mut applicability = Applicability::MachineApplicable; - // for some reason this is the only way to get the `Span` - // of the entire `for` loop - let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind { - arms[0].body.span - } else { - unreachable!() - }; + let for_span = get_span_of_entire_for_loop(expr); span_lint_and_sugg( cx, diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 464b18984fb..db62ed90d97 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -1,148 +1,204 @@ error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:14 + --> $DIR/manual_memcpy.rs:7:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:14 + --> $DIR/manual_memcpy.rs:12:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i + 10] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:14 + --> $DIR/manual_memcpy.rs:17:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i + 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:14 + --> $DIR/manual_memcpy.rs:22:5 | -LL | for i in 11..src.len() { - | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])` +LL | / for i in 11..src.len() { +LL | | dst[i] = src[i - 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:14 + --> $DIR/manual_memcpy.rs:27:5 | -LL | for i in 0..dst.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])` +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:14 + --> $DIR/manual_memcpy.rs:40:5 | -LL | for i in 10..256 { - | ^^^^^^^ +LL | / for i in 10..256 { +LL | | dst[i] = src[i - 5]; +LL | | dst2[i + 500] = src[i] +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]) -LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) { +LL | dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:14 + --> $DIR/manual_memcpy.rs:52:5 | -LL | for i in 10..LOOP_OFFSET { - | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])` +LL | / for i in 10..LOOP_OFFSET { +LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:14 + --> $DIR/manual_memcpy.rs:65:5 | -LL | for i in 0..src_vec.len() { - | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])` +LL | / for i in 0..src_vec.len() { +LL | | dst_vec[i] = src_vec[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:14 + --> $DIR/manual_memcpy.rs:94:5 | -LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)])` +LL | / for i in from..from + src.len() { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:98:14 + --> $DIR/manual_memcpy.rs:98:5 | -LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)])` +LL | / for i in from..from + 3 { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:103:14 + --> $DIR/manual_memcpy.rs:103:5 | -LL | for i in 0..5 { - | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])` +LL | / for i in 0..5 { +LL | | dst[i - 0] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:108:14 + --> $DIR/manual_memcpy.rs:108:5 | -LL | for i in 0..0 { - | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` +LL | / for i in 0..0 { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:14 + --> $DIR/manual_memcpy.rs:120:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:14 + --> $DIR/manual_memcpy.rs:126:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:14 + --> $DIR/manual_memcpy.rs:132:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:14 + --> $DIR/manual_memcpy.rs:138:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:14 + --> $DIR/manual_memcpy.rs:144:5 | -LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:14 + --> $DIR/manual_memcpy.rs:150:5 | -LL | for i in 5..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:14 + --> $DIR/manual_memcpy.rs:156:5 | -LL | for i in 3..10 { - | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:14 + --> $DIR/manual_memcpy.rs:163:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:14 + --> $DIR/manual_memcpy.rs:173:5 | -LL | for i in 0..1 << 1 { - | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:14 + --> $DIR/manual_memcpy.rs:181:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i].clone(); +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From cd4706413fb891ffe33ef06e0c229d97258fbfaf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 27 Sep 2020 15:17:13 +0200 Subject: Run cargo dev fmt --- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/utils/mod.rs | 2 +- clippy_lints/src/utils/qualify_min_const_fn.rs | 183 ++++++++++--------------- 3 files changed, 71 insertions(+), 116 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index e5f7cc51111..3e786da28df 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,10 +1,10 @@ +use crate::utils::qualify_min_const_fn::is_min_const_fn; use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use crate::utils::qualify_min_const_fn::is_min_const_fn; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index f59ed6df1a9..dfe2aadffc0 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -18,9 +18,9 @@ pub mod internal_lints; pub mod numeric_literal; pub mod paths; pub mod ptr; +pub mod qualify_min_const_fn; pub mod sugg; pub mod usage; -pub mod qualify_min_const_fn; pub use self::attrs::*; pub use self::diagnostics::*; diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index 6809b1fa88d..c1684575930 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::*; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; -use rustc_span::symbol::{sym}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; use std::borrow::Cow; @@ -23,15 +23,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, - ty::PredicateAtom::ObjectSafe(_) => { - panic!("object safe predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::ClosureKind(..) => { - panic!("closure kind predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::Subtype(_) => { - panic!("subtype predicate on function: {:#?}", predicate) - } + ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), + ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), + ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), ty::PredicateAtom::Trait(pred, _) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; @@ -47,12 +41,12 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - on const fn parameters are unstable" .into(), )); - } + }, // other kinds of bounds are either tautologies // or cause errors in other passes _ => continue, } - } + }, } } match predicates.parent { @@ -92,24 +86,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { - return Err((span, "mutable references in const fn are unstable".into())); - } + return Err((span, "mutable references in const fn are unstable".into())); + }, ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { - return Err((span, "function pointers in const fn are unstable".into())); - } + return Err((span, "function pointers in const fn are unstable".into())); + }, ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { - ty::ExistentialPredicate::AutoTrait(_) - | ty::ExistentialPredicate::Projection(_) => { + ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { return Err(( span, "trait bounds other than `Sized` \ on const fn parameters are unstable" .into(), )); - } + }, ty::ExistentialPredicate::Trait(trait_ref) => { if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { return Err(( @@ -119,34 +112,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { .into(), )); } - } + }, } } - } - _ => {} + }, + _ => {}, } } Ok(()) } -fn check_rvalue( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - rvalue: &Rvalue<'tcx>, - span: Span, -) -> McfResult { +fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult { match rvalue { - Rvalue::ThreadLocalRef(_) => { - Err((span, "cannot access thread local storage in const fn".into())) - } - Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { - check_operand(tcx, operand, span, body) - } - Rvalue::Len(place) - | Rvalue::Discriminant(place) - | Rvalue::Ref(_, _, place) - | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body), + Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), + Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + check_place(tcx, *place, span, body) + }, Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { use rustc_middle::ty::cast::CastTy; let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); @@ -154,20 +136,16 @@ fn check_rvalue( match (cast_in, cast_out) { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { Err((span, "casting pointers to ints is unstable in const fn".into())) - } + }, _ => check_operand(tcx, operand, span, body), } - } - Rvalue::Cast( - CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), - operand, - _, - ) => check_operand(tcx, operand, span, body), + }, + Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { + check_operand(tcx, operand, span, body) + }, Rvalue::Cast( CastKind::Pointer( - PointerCast::UnsafeFnPointer - | PointerCast::ClosureFnPointer(_) - | PointerCast::ReifyFnPointer, + PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, ), _, _, @@ -177,10 +155,7 @@ fn check_rvalue( deref_ty.ty } else { // We cannot allow this for now. - return Err(( - span, - "unsizing casts are only allowed for references right now".into(), - )); + return Err((span, "unsizing casts are only allowed for references right now".into())); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { @@ -191,7 +166,7 @@ fn check_rvalue( // We just can't allow trait objects until we have figured out trait method calls. Err((span, "unsizing casts are not allowed in const fn".into())) } - } + }, // binops are fine on integers Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { check_operand(tcx, lhs, span, body)?; @@ -200,13 +175,14 @@ fn check_rvalue( if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) } else { - Err((span, "only int, `bool` and `char` operations are stable in const fn".into())) + Err(( + span, + "only int, `bool` and `char` operations are stable in const fn".into(), + )) } - } + }, Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()), - Rvalue::NullaryOp(NullOp::Box, _) => { - Err((span, "heap allocations are not allowed in const fn".into())) - } + Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { @@ -214,39 +190,30 @@ fn check_rvalue( } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } - } + }, Rvalue::Aggregate(_, operands) => { for operand in operands { check_operand(tcx, operand, span, body)?; } Ok(()) - } + }, } } -fn check_statement( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - statement: &Statement<'tcx>, -) -> McfResult { +fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_rvalue(tcx, body, def_id, rval, span) - } + }, StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } => { - check_place(tcx, **place, span, body) - } + StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), - StatementKind::LlvmInlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), // These are all NOPs StatementKind::StorageLive(_) @@ -258,12 +225,7 @@ fn check_statement( } } -fn check_operand( - tcx: TyCtxt<'tcx>, - operand: &Operand<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { match operand { Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { @@ -273,12 +235,7 @@ fn check_operand( } } -fn check_place( - tcx: TyCtxt<'tcx>, - place: Place<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); while let &[ref proj_base @ .., elem] = cursor { cursor = proj_base; @@ -288,26 +245,22 @@ fn check_place( if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); + return Err((span, "accessing union fields is unstable".into())); } } - } + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref - | ProjectionElem::Index(_) => {} + | ProjectionElem::Index(_) => {}, } } Ok(()) } -fn check_terminator( - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - terminator: &Terminator<'tcx>, -) -> McfResult { +fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -317,20 +270,23 @@ fn check_terminator( | TerminatorKind::Resume | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), TerminatorKind::DropAndReplace { place, value, .. } => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_operand(tcx, value, span, body) - } + }, - TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => { - check_operand(tcx, discr, span, body) - } + TerminatorKind::SwitchInt { + discr, + switch_ty: _, + values: _, + targets: _, + } => check_operand(tcx, discr, span, body), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn generators are unstable".into())) - } + }, TerminatorKind::Call { func, @@ -342,8 +298,7 @@ fn check_terminator( } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) - { + if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { return Err(( span, format!( @@ -359,9 +314,7 @@ fn check_terminator( // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic - && tcx.item_name(fn_def_id) == sym::transmute - { + if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute { return Err(( span, "can only call `transmute` from const items, not `const fn`".into(), @@ -377,14 +330,16 @@ fn check_terminator( } else { Err((span, "can only call other const fns within const fn".into())) } - } + }, - TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { - check_operand(tcx, cond, span, body) - } + TerminatorKind::Assert { + cond, + expected: _, + msg: _, + target: _, + cleanup: _, + } => check_operand(tcx, cond, span, body), - TerminatorKind::InlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } -- cgit 1.4.1-3-g733a5 From 8bf27c5e92af39215a3d1da992a7207dafc883e1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 27 Sep 2020 15:20:19 +0200 Subject: Fix dogfood --- clippy_lints/src/utils/qualify_min_const_fn.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index c1684575930..3773b9d9a2e 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -1,6 +1,9 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::mir::*; +use rustc_middle::mir::{ + Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -208,8 +211,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen check_rvalue(tcx, body, def_id, rval, span) }, - StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), - + StatementKind::FakeRead(_, place) | // just an assignment StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), @@ -237,7 +239,7 @@ fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: & fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { + while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; match elem { ProjectionElem::Field(..) => { -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7c8b6f483bc..215700fed8c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -817,22 +817,22 @@ struct Offset { } impl Offset { - fn negative(value: MinifyingSugg<'static>) -> Self { + fn negative(value: Sugg<'static>) -> Self { Self { - value, + value: value.into(), sign: OffsetSign::Negative, } } - fn positive(value: MinifyingSugg<'static>) -> Self { + fn positive(value: Sugg<'static>) -> Self { Self { - value, + value: value.into(), sign: OffsetSign::Positive, } } fn empty() -> Self { - Self::positive(MinifyingSugg::non_paren("0")) + Self::positive(sugg::ZERO) } } @@ -844,30 +844,22 @@ fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'st } #[derive(Clone)] -struct MinifyingSugg<'a>(sugg::Sugg<'a>); - -impl std::fmt::Display for MinifyingSugg<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - std::fmt::Display::fmt(&self.0, f) - } -} +struct MinifyingSugg<'a>(Sugg<'a>); impl<'a> MinifyingSugg<'a> { fn as_str(&self) -> &str { - let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0; + let Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) = &self.0; s.as_ref() } - fn hir(cx: &LateContext<'_>, expr: &Expr<'_>, default: &'a str) -> Self { - Self(sugg::Sugg::hir(cx, expr, default)) - } - - fn maybe_par(self) -> Self { - Self(self.0.maybe_par()) + fn into_sugg(self) -> Sugg<'a> { + self.0 } +} - fn non_paren(str: impl Into>) -> Self { - Self(sugg::Sugg::NonParen(str.into())) +impl<'a> From> for MinifyingSugg<'a> { + fn from(sugg: Sugg<'a>) -> Self { + Self(sugg) } } @@ -877,7 +869,7 @@ impl std::ops::Add for &MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { ("0", _) => rhs.clone(), (_, "0") => self.clone(), - (_, _) => MinifyingSugg(&self.0 + &rhs.0), + (_, _) => (&self.0 + &rhs.0).into(), } } } @@ -887,9 +879,9 @@ impl std::ops::Sub for &MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self.clone(), - ("0", _) => MinifyingSugg(-(rhs.0.clone())), - (x, y) if x == y => MinifyingSugg::non_paren("0"), - (_, _) => MinifyingSugg(&self.0 - &rhs.0), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (&self.0 - &rhs.0).into(), } } } @@ -900,7 +892,7 @@ impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { ("0", _) => rhs.clone(), (_, "0") => self, - (_, _) => MinifyingSugg(self.0 + &rhs.0), + (_, _) => (self.0 + &rhs.0).into(), } } } @@ -910,9 +902,9 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self, - ("0", _) => MinifyingSugg(-(rhs.0.clone())), - (x, y) if x == y => MinifyingSugg::non_paren("0"), - (_, _) => MinifyingSugg(self.0 - &rhs.0), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (self.0 - &rhs.0).into(), } } } @@ -969,19 +961,15 @@ fn get_details_from_idx<'tcx>( }) } - fn get_offset<'tcx>( - cx: &LateContext<'tcx>, - e: &Expr<'_>, - starts: &[Start<'tcx>], - ) -> Option> { + fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { match &e.kind { ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), + ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), _ => None, }, ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { // `e` is always non paren as it's a `Path` - Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) + Some(Sugg::NonParen(snippet(cx, e.span, "???"))) }, _ => None, } @@ -1072,7 +1060,7 @@ fn build_manual_memcpy_suggestion<'tcx>( ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { - MinifyingSugg::non_paren("") + sugg::EMPTY.into() } else { offset } @@ -1088,14 +1076,14 @@ fn build_manual_memcpy_suggestion<'tcx>( if var_def_id(cx, arg) == var_def_id(cx, base); then { if sugg.as_str() == end_str { - MinifyingSugg::non_paren("") + sugg::EMPTY.into() } else { sugg } } else { match limits { ast::RangeLimits::Closed => { - sugg + &MinifyingSugg::non_paren("1") + sugg + &sugg::ONE.into() }, ast::RangeLimits::HalfOpen => sugg, } @@ -1103,29 +1091,31 @@ fn build_manual_memcpy_suggestion<'tcx>( } }; - let start_str = MinifyingSugg::hir(cx, start, ""); - let end_str = MinifyingSugg::hir(cx, end, ""); + let start_str = Sugg::hir(cx, start, "").into(); + let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { StartKind::Range => ( - print_offset(apply_offset(&start_str, &idx_expr.idx_offset)), + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(), print_limit( end, end_str.as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset), - ), + ) + .into_sugg(), ), StartKind::Counter { initializer } => { - let counter_start = MinifyingSugg::hir(cx, initializer, ""); + let counter_start = Sugg::hir(cx, initializer, "").into(); ( - print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)), + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(), print_limit( end, end_str.as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, - ), + ) + .into_sugg(), ) }, }; @@ -1136,7 +1126,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst_base_str = snippet(cx, dst.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???"); - let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { + let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { dst_base_str } else { format!( diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 062b273c0f4..0b2cb667bf4 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -16,7 +16,7 @@ use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Sugg<'a> { /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -27,8 +27,12 @@ pub enum Sugg<'a> { BinOp(AssocOp, Cow<'a, str>), } +/// Literal constant `0`, for convenience. +pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0")); /// Literal constant `1`, for convenience. pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1")); +/// a constant represents an empty string, for convenience. +pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { -- cgit 1.4.1-3-g733a5 From 101e76f117eeefcd2e901bab707de199b030f201 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 28 Sep 2020 19:14:39 +0200 Subject: needless arbitrary self: handle macros --- clippy_lints/src/needless_arbitrary_self_type.rs | 30 +++++++++-- tests/ui/auxiliary/proc_macro_attr.rs | 61 +++++++++++++++++++++- tests/ui/needless_arbitrary_self_type_unfixable.rs | 45 ++++++++++++++++ .../needless_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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 38bdd0f7ed2..7687962bdd9 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{in_macro, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; @@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod if let [segment] = &path.segments[..]; if segment.ident.name == kw::SelfUpper; then { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; let self_param = match (binding_mode, mutbl) { (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), (Mode::Value, Mutability::Not) => "self".to_string(), }; @@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, - Applicability::MachineApplicable, + applicability, ) } } @@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if !p.is_self() { + // Bail out if the parameter it's not a receiver or was not written by the user + if !p.is_self() || in_macro(p.span) { return; } diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index 01796d45f13..de670cdfc31 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -2,7 +2,7 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(clippy::useless_conversion)] extern crate proc_macro; @@ -12,7 +12,11 @@ extern crate syn; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::parse_macro_input; -use syn::{parse_quote, ItemTrait, TraitItem}; +use syn::spanned::Spanned; +use syn::token::Star; +use syn::{ + parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, +}; #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { @@ -36,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { } TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream { + fn make_name(count: usize) -> String { + format!("'life{}", count) + } + + fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { + let arg = sig.inputs.first_mut()?; + if let FnArg::Typed(PatType { pat, .. }) = arg { + if let Pat::Ident(PatIdent { ident, .. }) = &**pat { + if ident == "self" { + return Some(arg); + } + } + } + None + } + + let mut elided = 0; + let mut item = parse_macro_input!(input as ItemImpl); + + // Look for methods having arbitrary self type taken by &mut ref + for inner in &mut item.items { + if let ImplItem::Method(method) = inner { + if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { + if let box Type::Reference(reference) = &mut pat_type.ty { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a non-existent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); + } + } + } + } + + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs new file mode 100644 index 00000000000..a39d96109f1 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -0,0 +1,45 @@ +// aux-build:proc_macro_attr.rs + +#![warn(clippy::needless_arbitrary_self_type)] + +#[macro_use] +extern crate proc_macro_attr; + +mod issue_6089 { + // Check that we don't lint if the `self` parameter comes from expansion + + macro_rules! test_from_expansion { + () => { + trait T1 { + fn test(self: &Self); + } + + struct S1 {} + + impl T1 for S1 { + fn test(self: &Self) {} + } + }; + } + + test_from_expansion!(); + + // If only the lifetime name comes from expansion we will lint, but the suggestion will have + // placeholders and will not be applied automatically, as we can't reliably know the original name. + // This specific case happened with async_trait. + + trait T2 { + fn call_with_mut_self(&mut self); + } + + struct S2 {} + + // The method's signature will be expanded to: + // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} + #[rename_my_lifetimes] + impl T2 for S2 { + fn call_with_mut_self(self: &mut Self) {} + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/tests/ui/needless_arbitrary_self_type_unfixable.stderr new file mode 100644 index 00000000000..44a0e6ddeac --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.stderr @@ -0,0 +1,10 @@ +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31 + | +LL | fn call_with_mut_self(self: &mut Self) {} + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From aa2ac38fa7224bff68eeff8781321c9c355e5980 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 30 Sep 2020 00:08:19 +0200 Subject: needless-lifetime / add known problem item --- clippy_lints/src/lifetimes.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 1d17fbd3725..530968c191f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -26,8 +26,11 @@ declare_clippy_lint! { /// complicated, while there is nothing out of the ordinary going on. Removing /// them leads to more readable code. /// - /// **Known problems:** Potential false negatives: we bail out if the function - /// has a `where` clause where lifetimes are mentioned. + /// **Known problems:** + /// - We bail out if the function has a `where` clause where lifetimes + /// are mentioned due to potenial false positives. + /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the + /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From cb2be6f9dba0224c63180eada2d089100450391a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 30 Sep 2020 00:33:46 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 530968c191f..d7043e7bd8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -383,6 +383,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + return; }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 548c5929c61..d482d466e44 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -336,9 +336,25 @@ mod nested_elision_sites { |i| i } // lint - fn pointer_fn_elidable<'a>(f: fn(&i32) -> &i32, i: &'a i32) -> &'a i32 { + fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { f(i) } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + |f| () + } } fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index ac38ab8effd..c8a2e8b81c0 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -132,5 +132,23 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:339:5 + | +LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:352:5 + | +LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:355:5 + | +LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From 0690f9c5d526baa593cc62b175ef5e7f2c8f4f9c Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Mon, 28 Sep 2020 15:30:47 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 575cbd60792..0de6f4b4235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1635,6 +1635,8 @@ Released 2018-09-13 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs new file mode 100644 index 00000000000..ef1f1a14afc --- /dev/null +++ b/clippy_lints/src/asm_syntax.rs @@ -0,0 +1,125 @@ +use std::fmt; + +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum AsmStyle { + Intel, + Att, +} + +impl fmt::Display for AsmStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AsmStyle::Intel => f.write_str("Intel"), + AsmStyle::Att => f.write_str("AT&T"), + } + } +} + +impl std::ops::Not for AsmStyle { + type Output = AsmStyle; + + fn not(self) -> AsmStyle { + match self { + AsmStyle::Intel => AsmStyle::Att, + AsmStyle::Att => AsmStyle::Intel, + } + } +} + +fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) { + if let ExprKind::InlineAsm(ref inline_asm) = expr.kind { + let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + AsmStyle::Att + } else { + AsmStyle::Intel + }; + + if style == check_for { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("{} x86 assembly syntax used", style), + None, + &format!("use {} x86 assembly syntax", !style), + ); + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of Intel x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for AT&T x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + pub INLINE_ASM_X86_INTEL_SYNTAX, + restriction, + "prefer AT&T x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel); + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of AT&T x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for Intel x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + pub INLINE_ASM_X86_ATT_SYNTAX, + restriction, + "prefer Intel x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86AttSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att); + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 529c2450541..10da59c7a7a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -153,6 +153,7 @@ mod utils; mod approx_const; mod arithmetic; mod as_conversions; +mod asm_syntax; mod assertions_on_constants; mod assign_ops; mod async_yields_async; @@ -487,6 +488,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, &as_conversions::AS_CONVERSIONS, + &asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, + &asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, @@ -1123,12 +1126,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); + store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); + store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), + LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 76e655ad603..16ceb617965 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -899,6 +899,20 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "inline_asm_x86_att_syntax", + group: "restriction", + desc: "prefer Intel x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, + Lint { + name: "inline_asm_x86_intel_syntax", + group: "restriction", + desc: "prefer AT&T x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, Lint { name: "inline_fn_without_body", group: "correctness", diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs new file mode 100644 index 00000000000..049bfa604d5 --- /dev/null +++ b/tests/ui/asm_syntax.rs @@ -0,0 +1,31 @@ +#![feature(asm)] +// only-x86 only-x86_64 + +#[warn(clippy::inline_asm_x86_intel_syntax)] +mod warn_intel { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +#[warn(clippy::inline_asm_x86_att_syntax)] +mod warn_att { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +fn main() { + unsafe { + warn_att::use_asm(); + warn_intel::use_asm(); + } +} diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr new file mode 100644 index 00000000000..27b51166eac --- /dev/null +++ b/tests/ui/asm_syntax.stderr @@ -0,0 +1,44 @@ +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:7:9 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:8:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:9:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:21:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:22:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 0a91fe7016cdb48d3824c218e02eca8cfdec3854 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 1 Oct 2020 23:53:05 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7f998c63f49..61b63597b16 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, @@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>( let skip = if starts_at_zero { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + return; } else { format!(".skip({})", snippet(cx, start.span, "..")) }; @@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + return; } else { match limits { ast::RangeLimits::Closed => { diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index a82b1159161..7633316e0f8 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -82,6 +82,20 @@ fn main() { for i in 1..3 { println!("{}", arr[i]); } + + // Fix #5945 + let mut vec = vec![1, 2, 3, 4]; + for i in 0..vec.len() - 1 { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() - 1 { + vec[i] += 1; + } } mod issue2277 { -- cgit 1.4.1-3-g733a5 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: document `MinifyingSugg` and `Offset` ...and also swap their position --- clippy_lints/src/loops.rs | 82 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 215700fed8c..9a1ba4907cd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -805,44 +805,10 @@ fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { } } -#[derive(Clone, Copy)] -enum OffsetSign { - Positive, - Negative, -} - -struct Offset { - value: MinifyingSugg<'static>, - sign: OffsetSign, -} - -impl Offset { - fn negative(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Negative, - } - } - - fn positive(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Positive, - } - } - - fn empty() -> Self { - Self::positive(sugg::ZERO) - } -} - -fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { - match rhs.sign { - OffsetSign::Positive => lhs + &rhs.value, - OffsetSign::Negative => lhs - &rhs.value, - } -} - +/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; +/// and also, it avoids subtracting a variable from the same one by replacing it with `0`. +/// it exists for the convenience of the overloaded operators while normal functions can do the +/// same. #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); @@ -909,6 +875,46 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { } } +/// a wrapper around `MinifyingSugg`, which carries a operator like currying +/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`). +struct Offset { + value: MinifyingSugg<'static>, + sign: OffsetSign, +} + +#[derive(Clone, Copy)] +enum OffsetSign { + Positive, + Negative, +} + +impl Offset { + fn negative(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Negative, + } + } + + fn positive(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Positive, + } + } + + fn empty() -> Self { + Self::positive(sugg::ZERO) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + #[derive(Debug, Clone, Copy)] enum StartKind<'hir> { Range, -- cgit 1.4.1-3-g733a5 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: simplify the code --- clippy_lints/src/loops.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9a1ba4907cd..2ae5a9acb75 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -973,10 +973,7 @@ fn get_details_from_idx<'tcx>( ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), _ => None, }, - ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { - // `e` is always non paren as it's a `Path` - Some(Sugg::NonParen(snippet(cx, e.span, "???"))) - }, + ExprKind::Path(..) if get_start(cx, e, starts).is_none() => Some(Sugg::hir(cx, e, "???")), _ => None, } } @@ -1010,8 +1007,7 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( cx: &'a LateContext<'tcx>, - stmts: &'tcx [Stmt<'tcx>], - expr: Option<&'tcx Expr<'tcx>>, + Block { stmts, expr, .. }: &'tcx Block<'tcx>, loop_counters: &'c [Start<'tcx>], ) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { stmts @@ -1025,7 +1021,7 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( then { None } else { Some(e) } }, }) - .chain(expr.into_iter()) + .chain((*expr).into_iter()) .map(get_assignment) } @@ -1184,7 +1180,7 @@ fn detect_manual_memcpy<'tcx>( if let Some(loop_counters) = get_loop_counters(cx, block, expr) { starts.extend(loop_counters); } - iter_a = Some(get_assignments(cx, block.stmts, block.expr, &starts)); + iter_a = Some(get_assignments(cx, block, &starts)); } else { iter_b = Some(get_assignment(body)); } -- cgit 1.4.1-3-g733a5 From e91202cf683b7265eec484ad311b5a9d3b56fc3e Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 2 Oct 2020 05:43:43 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 5e8800d38eb..52d3c2c1daf 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent separator (b'e' or b'E') and the exponent part. - pub exponent: Option<(char, &'a str)>, + /// The exponent separator (b'e' or b'E') including preceding underscore if present + /// and the exponent part. + pub exponent: Option<(&'a str, &'a str)>, /// The type suffix, including preceding underscore if present. pub suffix: Option<&'a str>, @@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> { self.radix == Radix::Decimal } - pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { + pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { let mut integer = digits; let mut fraction = None; let mut exponent = None; @@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> { fraction = Some(&digits[i + 1..]); }, 'e' | 'E' => { - if integer.len() > i { - integer = &digits[..i]; + let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i }; + + if integer.len() > exp_start { + integer = &digits[..exp_start]; } else { - fraction = Some(&digits[integer.len() + 1..i]); + fraction = Some(&digits[integer.len() + 1..exp_start]); }; - exponent = Some((c, &digits[i + 1..])); + exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, _ => {}, @@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> { } if let Some((separator, exponent)) = self.exponent { - output.push(separator); + output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); } diff --git a/tests/ui/inconsistent_digit_grouping.fixed b/tests/ui/inconsistent_digit_grouping.fixed index b75f10917df..dd683e7f746 100644 --- a/tests/ui/inconsistent_digit_grouping.fixed +++ b/tests/ui/inconsistent_digit_grouping.fixed @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/inconsistent_digit_grouping.rs b/tests/ui/inconsistent_digit_grouping.rs index 79ce38be19b..d5d27c853c2 100644 --- a/tests/ui/inconsistent_digit_grouping.rs +++ b/tests/ui/inconsistent_digit_grouping.rs @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } -- cgit 1.4.1-3-g733a5 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: 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 +++++++ tests/ui/manual_memcpy/with_loop_counters.stderr | 11 ++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2ae5a9acb75..ea40c2af4f7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,14 +1014,20 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( .iter() .filter_map(move |stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { - if let ExprKind::AssignOp(_, var, _) = e.kind; - // skip StartKind::Range - if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); - then { None } else { Some(e) } - }, + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain((*expr).into_iter()) + .filter(move |e| { + if let ExprKind::AssignOp(_, place, _) = e.kind { + !loop_counters + .iter() + // skip StartKind::Range + .skip(1) + .any(|counter| same_var(cx, place, counter.id)) + } else { + true + } + }) .map(get_assignment) } diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index a49ba9eb10a..70873c9e994 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -59,6 +59,13 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[count] = src[i + 2]; count += 1; } + + // make sure incrementing expressions without semicolons at the end of loops are handled correctly. + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1 + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 24393ad9b4d..598c881b4d6 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -89,5 +89,14 @@ LL | | count += 1; LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` -error: aborting due to 9 previous errors +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:65:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1 +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 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: add comments around `loop_counters` --- clippy_lints/src/loops.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ea40c2af4f7..4f279cc5ef7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1005,6 +1005,10 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx } } +/// Get assignments from the given block. +/// The returned iterator yields `None` if no assignment expressions are there, +/// filtering out the increments of the given whitelisted loop counters; +/// because its job is to make sure there's nothing other than assignments and the increments. fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( cx: &'a LateContext<'tcx>, Block { stmts, expr, .. }: &'tcx Block<'tcx>, @@ -1021,7 +1025,8 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( if let ExprKind::AssignOp(_, place, _) = e.kind { !loop_counters .iter() - // skip StartKind::Range + // skip the first item which should be `StartKind::Range` + // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop. .skip(1) .any(|counter| same_var(cx, place, counter.id)) } else { @@ -1191,11 +1196,11 @@ fn detect_manual_memcpy<'tcx>( iter_b = Some(get_assignment(body)); } - // The only statements in the for loops can be indexed assignments from - // indexed retrievals. let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); -- cgit 1.4.1-3-g733a5 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: supress `clippy::filter_map` --- clippy_lints/src/loops.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4f279cc5ef7..d3a1683cc0c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,6 +1014,9 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( Block { stmts, expr, .. }: &'tcx Block<'tcx>, loop_counters: &'c [Start<'tcx>], ) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { + // As the `filter` and `map` below do different things, I think putting together + // just increases complexity. (cc #3188 and #4193) + #[allow(clippy::filter_map)] stmts .iter() .filter_map(move |stmt| match stmt.kind { -- cgit 1.4.1-3-g733a5 From 515ca9312393bd00af0e867fceee9aff9b6e565d Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Fri, 2 Oct 2020 11:54:31 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d8c57f0e7ae..d3fe60042a8 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -8,18 +8,18 @@ use rustc_span::source_map::Span; use unicode_normalization::UnicodeNormalization; declare_clippy_lint! { - /// **What it does:** Checks for the Unicode zero-width space in the code. + /// **What it does:** Checks for invisible Unicode characters in the code. /// /// **Why is this bad?** Having an invisible character in the code makes for all /// sorts of April fools, but otherwise is very much frowned upon. /// /// **Known problems:** None. /// - /// **Example:** You don't see it, but there may be a zero-width space - /// somewhere in this text. + /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen + /// some­where in this text. pub ZERO_WIDTH_SPACE, correctness, - "using a zero-width space in a string literal, which is confusing" + "using an invisible character in a string literal, which is confusing" } declare_clippy_lint! { @@ -91,14 +91,14 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.contains('\u{200B}') { + if let Some(invisible) = string.chars().find(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { span_lint_and_sugg( cx, ZERO_WIDTH_SPACE, span, - "zero-width space detected", + &format!("invisible character detected: {:?}", invisible), "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}"), + string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index 27db9594f3b..f3fd1c57da6 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -2,6 +2,8 @@ fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); + print!("Here >­< is a SHY, and ­another"); + print!("This\u{ad}is\u{ad}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 4575a132e5b..b0445b070fd 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,4 +1,4 @@ -error: zero-width space detected +error: invisible character detected: '/u{200b}' --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); @@ -6,8 +6,14 @@ LL | print!("Here >​< is a ZWS, and ​another"); | = note: `-D clippy::zero-width-space` implied by `-D warnings` +error: invisible character detected: '/u{ad}' + --> $DIR/unicode.rs:5:12 + | +LL | print!("Here >­< is a SHY, and ­another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` + error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:9:12 + --> $DIR/unicode.rs:11:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -15,12 +21,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:15:12 + --> $DIR/unicode.rs:17:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 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: remove the explicit return value of `print_limit` --- clippy_lints/src/loops.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d3a1683cc0c..3e9265c1215 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1082,30 +1082,29 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = - |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { - if_chain! { - if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, base); - then { - if sugg.as_str() == end_str { - sugg::EMPTY.into() - } else { - sugg - } + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if var_def_id(cx, arg) == var_def_id(cx, base); + then { + if sugg.as_str() == end_str { + sugg::EMPTY.into() } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &sugg::ONE.into() - }, - ast::RangeLimits::HalfOpen => sugg, - } + sugg + } + } else { + match limits { + ast::RangeLimits::Closed => { + sugg + &sugg::ONE.into() + }, + ast::RangeLimits::HalfOpen => sugg, } } - }; + } + }; let start_str = Sugg::hir(cx, start, "").into(); let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); -- cgit 1.4.1-3-g733a5 From f302af33bc1901ddd0226c8d3a6433682a85f69b Mon Sep 17 00:00:00 2001 From: nahuakang Date: Fri, 2 Oct 2020 20:13:01 +0200 Subject: Add doc comment issue of #5834 to known problems of lint doc_markdown --- clippy_lints/src/doc.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 62bb70af06e..07f604cf714 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -32,6 +32,11 @@ declare_clippy_lint! { /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks /// for is limited, and there are still false positives. /// + /// In addition, when writing documentation comments, including `[]` brackets + /// inside a link text would trip the parser. Therfore, documenting link with + /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec + /// would fail. + /// /// **Examples:** /// ```rust /// /// Do something with the foo_bar parameter. See also @@ -39,6 +44,14 @@ declare_clippy_lint! { /// // ^ `foo_bar` and `that::other::module::foo` should be ticked. /// fn doit(foo_bar: usize) {} /// ``` + /// + /// ```rust + /// // Link text with `[]` brackets should be written as following: + /// /// Consume the array and return the inner + /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec]. + /// /// [SmallVec]: SmallVec + /// fn main() {} + /// ``` pub DOC_MARKDOWN, pedantic, "presence of `_`, `::` or camel-case outside backticks in documentation" -- cgit 1.4.1-3-g733a5 From 8b8c63f568bc838aa7997a6933a40c3ab7b91a5d Mon Sep 17 00:00:00 2001 From: Long Louis Bui Date: Wed, 30 Sep 2020 12:13:12 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 10da59c7a7a..71bd7c4f82a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1604,6 +1604,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&panic_unimplemented::PANIC_PARAMS), @@ -1760,8 +1762,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index bb44eeb6adc..7b662eae775 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,6 +1,6 @@ //! Checks for uses of const which the type is not `Freeze` (`Cell`-free). //! -//! This lint is **deny** by default. +//! This lint is **warn** by default. use std::ptr; @@ -17,6 +17,8 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::{in_constant, qpath_res, span_lint_and_then}; use if_chain::if_chain; +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). @@ -34,6 +36,15 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// + /// When an enum has variants with interior mutability, use of its non interior mutable + /// variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) + /// /// **Example:** /// ```rust /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; @@ -49,10 +60,12 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub DECLARE_INTERIOR_MUTABLE_CONST, - correctness, + style, "declaring `const` with interior mutability" } +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks if `const` items which is interior mutable (e.g., /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. @@ -64,7 +77,14 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// **Known problems:** None + /// **Known problems:** When an enum has variants with interior mutability, use of its non + /// interior mutable variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -81,7 +101,7 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub BORROW_INTERIOR_MUTABLE_CONST, - correctness, + style, "referencing `const` with interior mutability" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 16ceb617965..ccdfac3a34b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -110,7 +110,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "borrow_interior_mutable_const", - group: "correctness", + group: "style", desc: "referencing `const` with interior mutability", deprecation: None, module: "non_copy_const", @@ -334,7 +334,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "declare_interior_mutable_const", - group: "correctness", + group: "style", desc: "declaring `const` with interior mutability", deprecation: None, module: "non_copy_const", -- cgit 1.4.1-3-g733a5 From 998bd3b6b4d168099346e460ae42897dc3667882 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sat, 3 Oct 2020 00:03:33 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de6f4b4235..617bf32f463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1647,6 +1647,7 @@ Released 2018-09-13 [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop @@ -1922,6 +1923,5 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr -[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 10da59c7a7a..91244ec2724 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -854,9 +854,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, - &unicode::ZERO_WIDTH_SPACE, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, @@ -1511,7 +1511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1779,7 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1910,6 +1910,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); + ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d3fe60042a8..d6c8d317dc2 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen /// some­where in this text. - pub ZERO_WIDTH_SPACE, + pub INVISIBLE_CHARACTERS, correctness, "using an invisible character in a string literal, which is confusing" } @@ -63,7 +63,7 @@ declare_clippy_lint! { "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" } -declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); +declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -91,12 +91,12 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if let Some(invisible) = string.chars().find(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { + if string.chars().any(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { span_lint_and_sugg( cx, - ZERO_WIDTH_SPACE, + INVISIBLE_CHARACTERS, span, - &format!("invisible character detected: {:?}", invisible), + "invisible character detected", "consider replacing the string with", string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), Applicability::MachineApplicable, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3654dbc6124..e7df733d3a2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -969,6 +969,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "invisible_characters", + group: "correctness", + desc: "using an invisible character in a string literal, which is confusing", + deprecation: None, + module: "unicode", + }, Lint { name: "items_after_statements", group: "pedantic", @@ -2810,13 +2817,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, - Lint { - name: "zero_width_space", - group: "correctness", - desc: "using an invisible character in a string literal, which is confusing", - deprecation: None, - module: "unicode", - }, Lint { name: "zst_offset", group: "correctness", diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index f3fd1c57da6..b6944e04859 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,4 +1,4 @@ -#[warn(clippy::zero_width_space)] +#[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index b0445b070fd..595d80ea279 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,12 +1,12 @@ -error: invisible character detected: '/u{200b}' +error: invisible character detected --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` | - = note: `-D clippy::zero-width-space` implied by `-D warnings` + = note: `-D clippy::invisible-characters` implied by `-D warnings` -error: invisible character detected: '/u{ad}' +error: invisible character detected --> $DIR/unicode.rs:5:12 | LL | print!("Here >­< is a SHY, and ­another"); -- cgit 1.4.1-3-g733a5 From 572e4c4837e5f955cdc3751b9ad63f0bfb86beac Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sat, 3 Oct 2020 00:07:56 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d6c8d317dc2..93d59cc7fcd 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -91,14 +91,17 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.chars().any(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { + if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { span_lint_and_sugg( cx, INVISIBLE_CHARACTERS, span, "invisible character detected", "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), + string + .replace("\u{200B}", "\\u{200B}") + .replace("\u{ad}", "\\u{AD}") + .replace("\u{2060}", "\\u{2060}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index b6944e04859..1f596c312fe 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -4,6 +4,8 @@ fn zero() { print!("This\u{200B}is\u{200B}fine"); print!("Here >­< is a SHY, and ­another"); print!("This\u{ad}is\u{ad}fine"); + print!("Here >⁠< is a WJ, and ⁠another"); + print!("This\u{2060}is\u{2060}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 595d80ea279..3fca463c620 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -12,8 +12,14 @@ error: invisible character detected LL | print!("Here >­< is a SHY, and ­another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` +error: invisible character detected + --> $DIR/unicode.rs:7:12 + | +LL | print!("Here >⁠< is a WJ, and ⁠another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` + error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:11:12 + --> $DIR/unicode.rs:13:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -21,12 +27,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:17:12 + --> $DIR/unicode.rs:19:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From f58a1695a67e2db741b7237992e696ff9a14d0ab Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 1 Oct 2020 10:51:49 +1300 Subject: fix a FP in `interior_mutable_const` fix a false positive in two `interior_mutable_const` lints where a constant with enums gets linted even if it uses a clearly unfrozen variant. Note that the code uses the MIR interpreter, which the author of #3962 thought unlikely to be a solution. This might be over-engineering; but, I think it's important to be able to work with the 'http' crate (#3825). --- clippy_lints/src/non_copy_const.rs | 170 ++++++++++++++++----- .../auxiliary/helper.rs | 16 ++ tests/ui/borrow_interior_mutable_const/enums.rs | 101 ++++++++++++ .../ui/borrow_interior_mutable_const/enums.stderr | 75 +++++++++ tests/ui/declare_interior_mutable_const/enums.rs | 123 +++++++++++++++ .../ui/declare_interior_mutable_const/enums.stderr | 89 +++++++++++ 6 files changed, 539 insertions(+), 35 deletions(-) create mode 100644 tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs create mode 100644 tests/ui/borrow_interior_mutable_const/enums.rs create mode 100644 tests/ui/borrow_interior_mutable_const/enums.stderr create mode 100644 tests/ui/declare_interior_mutable_const/enums.rs create mode 100644 tests/ui/declare_interior_mutable_const/enums.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 7b662eae775..6b0d198edcf 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -5,11 +5,15 @@ use std::ptr; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + BodyId, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, +}; use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{AssocKind, Ty}; +use rustc_middle::ty::{self, AssocKind, Const, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -36,14 +40,17 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// - /// When an enum has variants with interior mutability, use of its non interior mutable - /// variants can generate false positives. See issue - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// Even though the lint avoids triggering on a constant whose type has enums that have variants + /// with interior mutability, and its value uses non interior mutable variants (see + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); + /// it complains about associated constants without default values only based on its types; + /// which might not be preferable. + /// There're other enums plus associated constants cases that the lint cannot handle. /// /// Types that have underlying or potential interior mutability trigger the lint whether /// the interior mutable field is used or not. See issues /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -105,6 +112,79 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } +fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) +} + +fn is_value_unfrozen_raw<'tcx>( + cx: &LateContext<'tcx>, + result: Result, ErrorHandled>, + ty: Ty<'tcx>, +) -> bool { + fn inner<'tcx>(cx: &LateContext<'tcx>, val: &'tcx Const<'tcx>) -> bool { + match val.ty.kind() { + // the fact that we have to dig into every structs to search enums + // leads us to the point checking `UnsafeCell` directly is the only option. + ty::Adt(ty_def, ..) if Some(ty_def.did) == cx.tcx.lang_items().unsafe_cell_type() => true, + ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { + let val = cx.tcx.destructure_const(cx.param_env.and(val)); + val.fields.iter().any(|field| inner(cx, field)) + }, + _ => false, + } + } + + result.map_or_else( + |err| { + // Consider `TooGeneric` cases as being unfrozen. + // This causes a false positive where an assoc const whose type is unfrozen + // have a value that is a frozen variant with a generic param (an example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). + // However, it prevents a number of false negatives that is, I think, important: + // 1. assoc consts in trait defs referring to consts of themselves + // (an example is `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). + // 2. a path expr referring to assoc consts whose type is doesn't have + // any frozen variants in trait defs (i.e. without substitute for `Self`). + // (e.g. borrowing `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) + // 3. similar to the false positive above; + // but the value is an unfrozen variant, or the type has no enums. (An example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` + // and `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). + // One might be able to prevent these FNs correctly, and replace this with `false`; + // e.g. implementing `has_frozen_variant` described above, and not running this function + // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd + // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, + // similar to 2., but with the a frozen variant) (e.g. borrowing + // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). + // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). + err == ErrorHandled::TooGeneric + }, + |val| inner(cx, Const::from_value(cx.tcx, val, ty)), + ) +} + +fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { + let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id()); + is_value_unfrozen_raw(cx, result, ty) +} + +fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { + let substs = cx.typeck_results().node_substs(hir_id); + + let result = cx + .tcx + .const_eval_resolve(cx.param_env, ty::WithOptConstParam::unknown(def_id), substs, None, None); + is_value_unfrozen_raw(cx, result, ty) +} + #[derive(Copy, Clone)] enum Source { Item { item: Span }, @@ -130,19 +210,7 @@ impl Source { } } -fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { - // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, - // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is - // 'unfrozen'. However, this code causes a false negative in which - // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. - // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` - // since it works when a pointer indirection involves (`Cell<*const T>`). - // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; - // but I'm not sure whether it's a decent way, if possible. - if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) { - return; - } - +fn lint(cx: &LateContext<'_>, source: Source) { let (lint, msg, span) = source.lint(); span_lint_and_then(cx, lint, span, msg, |diag| { if span.from_expansion() { @@ -165,24 +233,44 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(hir_ty, ..) = &it.kind { + if let ItemKind::Const(hir_ty, body_id) = it.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound(cx, ty, Source::Item { item: it.span }); + + if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { + lint(cx, Source::Item { item: it.span }); + } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { + if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); + // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span }); + if is_unfrozen(cx, normalized) + // When there's no default value, lint it only according to its type; + // in other words, lint consts whose value *could* be unfrozen, not definitely is. + // This feels inconsistent with how the lint treats generic types, + // which avoids linting types which potentially become unfrozen. + // One could check whether a unfrozen type have a *frozen variant* + // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), + // and do the same as the case of generic types at impl items. + // Note that it isn't sufficient to check if it has an enum + // since all of that enum's variants can be unfrozen: + // i.e. having an enum doesn't necessary mean a type has a frozen variant. + // And, implementing it isn't a trivial task; it'll probably end up + // re-implementing the trait predicate evaluation specific to `Freeze`. + && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized)) + { + lint(cx, Source::Assoc { item: trait_item.span }); + } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { + if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind { let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); let item = cx.tcx.hir().expect_item(item_hir_id); @@ -209,16 +297,23 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { ), )) .is_err(); + // If there were a function like `has_frozen_variant` described above, + // we should use here as a frozen variant is a potential to be frozen + // similar to unknown layouts. + // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` then { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound( - cx, - normalized, - Source::Assoc { - item: impl_item.span, - }, - ); + if is_unfrozen(cx, normalized) + && is_value_unfrozen_poly(cx, *body_id, normalized) + { + lint( + cx, + Source::Assoc { + item: impl_item.span, + }, + ); + } } } }, @@ -226,7 +321,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let ty = hir_ty_to_ty(cx.tcx, hir_ty); // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span }); + + if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) { + lint(cx, Source::Assoc { item: impl_item.span }); + } }, _ => (), } @@ -241,8 +339,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - match qpath_res(cx, qpath, expr.hir_id) { - Res::Def(DefKind::Const | DefKind::AssocConst, _) => {}, + let item_def_id = match qpath_res(cx, qpath, expr.hir_id) { + Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, _ => return, }; @@ -319,7 +417,9 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.typeck_results().expr_ty(dereferenced_expr) }; - verify_ty_bound(cx, ty, Source::Expr { expr: expr.span }); + if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) { + lint(cx, Source::Expr { expr: expr.span }); + } } } } diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs new file mode 100644 index 00000000000..2289f7875f0 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs @@ -0,0 +1,16 @@ +// this file solely exists to test constants defined in foreign crates. +// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure. + +#![allow(clippy::declare_interior_mutable_const)] + +use std::sync::atomic::AtomicUsize; + +enum Private { + ToBeUnfrozen(T), + Frozen(usize), +} + +pub struct Wrapper(Private); + +pub const WRAPPED_PRIVATE_UNFROZEN_VARIANT: Wrapper = Wrapper(Private::ToBeUnfrozen(AtomicUsize::new(6))); +pub const WRAPPED_PRIVATE_FROZEN_VARIANT: Wrapper = Wrapper(Private::Frozen(7)); diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs new file mode 100644 index 00000000000..5027db44561 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/enums.rs @@ -0,0 +1,101 @@ +// aux-build:helper.rs + +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] + +// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. + +extern crate helper; + +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +enum OptionalCell { + Unfrozen(Cell), + Frozen, +} + +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +fn borrow_optional_cell() { + let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &FROZEN_VARIANT; +} + +trait AssocConsts { + const TO_BE_UNFROZEN_VARIANT: OptionalCell; + const TO_BE_FROZEN_VARIANT: OptionalCell; + + const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + fn function() { + // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` + // caused by a similar reason to unfrozen types without any default values + // get linted even if it has frozen variants'. + let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable + + // The lint ignores default values because an impl of this trait can set + // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable + } +} + +impl AssocConsts for u64 { + const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + fn function() { + let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &::TO_BE_FROZEN_VARIANT; + let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; + } +} + +trait AssocTypes { + type ToBeUnfrozen; + + const TO_BE_UNFROZEN_VARIANT: Option; + const TO_BE_FROZEN_VARIANT: Option; + + // there's no need to test here because it's the exactly same as `trait::AssocTypes` + fn function(); +} + +impl AssocTypes for u64 { + type ToBeUnfrozen = AtomicUsize; + + const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: Option = None; + + fn function() { + let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &::TO_BE_FROZEN_VARIANT; + } +} + +enum BothOfCellAndGeneric { + Unfrozen(Cell<*const T>), + Generic(*const T), + Frozen(usize), +} + +impl BothOfCellAndGeneric { + const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); + + fn function() { + let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability + let _ = &Self::FROZEN_VARIANT; + } +} + +fn main() { + // constants defined in foreign crates + let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; +} diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr new file mode 100644 index 00000000000..654a1ee7df6 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/enums.stderr @@ -0,0 +1,75 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:22:14 + | +LL | let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:37:18 + | +LL | let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:41:18 + | +LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:50:18 + | +LL | let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:52:18 + | +LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:74:18 + | +LL | let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:91:18 + | +LL | let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:92:18 + | +LL | let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:99:14 + | +LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 9 previous errors + diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs new file mode 100644 index 00000000000..f44518694b8 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/enums.rs @@ -0,0 +1,123 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +enum OptionalCell { + Unfrozen(Cell), + Frozen, +} + +// a constant with enums should be linted only when the used variant is unfrozen (#3962). +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +const fn unfrozen_variant() -> OptionalCell { + OptionalCell::Unfrozen(Cell::new(false)) +} + +const fn frozen_variant() -> OptionalCell { + OptionalCell::Frozen +} + +const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable +const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); + +enum NestedInnermost { + Unfrozen(AtomicUsize), + Frozen, +} + +struct NestedInner { + inner: NestedInnermost, +} + +enum NestedOuter { + NestedInner(NestedInner), + NotNested(usize), +} + +struct NestedOutermost { + outer: NestedOuter, +} + +// a constant with enums should be linted according to its value, no matter how structs involve. +const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + outer: NestedOuter::NestedInner(NestedInner { + inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), + }), +}; //~ ERROR interior mutable +const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { + outer: NestedOuter::NestedInner(NestedInner { + inner: NestedInnermost::Frozen, + }), +}; + +trait AssocConsts { + // When there's no default value, lint it only according to its type. + // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). + const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + + // Lint default values accordingly. + const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; +} + +// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it +// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. +impl AssocConsts for u64 { + const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + // even if this sets an unfrozen variant, the lint ignores it. + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); +} + +// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters +// here are values; and I think substituted generics at definitions won't appear in MIR. +trait AssocTypes { + type ToBeUnfrozen; + + const TO_BE_UNFROZEN_VARIANT: Option; + const TO_BE_FROZEN_VARIANT: Option; +} + +impl AssocTypes for u64 { + type ToBeUnfrozen = AtomicUsize; + + const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: Option = None; +} + +// Use raw pointers since direct generics have a false negative at the type level. +enum BothOfCellAndGeneric { + Unfrozen(Cell<*const T>), + Generic(*const T), + Frozen(usize), +} + +impl BothOfCellAndGeneric { + const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + + // This is a false positive. The argument about this is on `is_value_unfrozen_raw` + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); + + // This is what is likely to be a false negative when one tries to fix + // the `GENERIC_VARIANT` false positive. + const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable +} + +// associated types here is basically the same as the one above. +trait BothOfCellAndGenericWithAssocType { + type AssocType; + + const UNFROZEN_VARIANT: BothOfCellAndGeneric = + BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); +} + +fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr new file mode 100644 index 00000000000..84198d54615 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/enums.stderr @@ -0,0 +1,89 @@ +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:12:1 + | +LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:23:1 + | +LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:45:1 + | +LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + | ^---- + | | + | _make this a static item (maybe with lazy_static) + | | +LL | | outer: NestedOuter::NestedInner(NestedInner { +LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), +LL | | }), +LL | | }; //~ ERROR interior mutable + | |__^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:59:5 + | +LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:60:5 + | +LL | const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:63:5 + | +LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:89:5 + | +LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:101:5 + | +LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mut... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:104:5 + | +LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:110:5 + | +LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:117:5 + | +LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = +LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + | |____________________________________________________________________^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:119:5 + | +LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mu... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + -- cgit 1.4.1-3-g733a5 From 78695bd4968d45ef6afd3433af4bf6f310128770 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 5 Oct 2020 12:08:57 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index a36fdca5d5d..c54103b25c2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,13 +264,10 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') + } else if num_lit.fraction.is_some() { + (&mut num_lit.integer, &["32", "64"][..], 'f') } else { - num_lit - .fraction - .as_mut() - .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| { - (fraction, &["32", "64"][..], 'f') - }) + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') }; let mut split = part.rsplit('_'); diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index baee7735730..8275bfa5db7 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] fn main() { let fail14 = 2_i32; @@ -12,13 +12,13 @@ fn main() { let fail20 = 2_i8; // let fail21 = 4_i16; // - let fail24 = 12.34_f64; + let ok24 = 12.34_64; let fail25 = 1E2_f32; let fail26 = 43E7_f64; let fail27 = 243E17_f32; #[allow(overflowing_literals)] let fail28 = 241_251_235E723_f64; - let fail29 = 42_279.911_f32; + let ok29 = 42279.911_32; let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 6de447f4021..8a451807aeb 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] fn main() { let fail14 = 2_32; @@ -12,13 +12,13 @@ fn main() { let fail20 = 2__8; // let fail21 = 4___16; // - let fail24 = 12.34_64; + let ok24 = 12.34_64; let fail25 = 1E2_32; let fail26 = 43E7_64; let fail27 = 243E17_32; #[allow(overflowing_literals)] let fail28 = 241251235E723_64; - let fail29 = 42279.911_32; + let ok29 = 42279.911_32; let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index 48a7ae90494..f4c5adfe82c 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -36,12 +36,6 @@ error: mistyped literal suffix LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 - | -LL | let fail24 = 12.34_64; - | ^^^^^^^^ help: did you mean to write: `12.34_f64` - error: mistyped literal suffix --> $DIR/mistyped_literal_suffix.rs:16:18 | @@ -66,17 +60,11 @@ error: mistyped literal suffix LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:21:18 - | -LL | let fail29 = 42279.911_32; - | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` - error: mistyped literal suffix --> $DIR/mistyped_literal_suffix.rs:23:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 5554641fceb640116316201bf669c04e1f86fcc3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 5 Oct 2020 22:32:04 +0200 Subject: Fix rustup fallout --- clippy_lints/src/attrs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c8f153e7201..f6eadbdef0b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -137,17 +137,17 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// // Good (as inner attribute) - /// #![inline(always)] + /// #![allow(dead_code)] /// /// fn this_is_fine() { } /// /// // Bad - /// #[inline(always)] + /// #[allow(dead_code)] /// /// fn not_quite_good_code() { } /// /// // Good (as outer attribute) - /// #[inline(always)] + /// #[allow(dead_code)] /// fn this_is_fine_too() { } /// ``` pub EMPTY_LINE_AFTER_OUTER_ATTR, -- cgit 1.4.1-3-g733a5 From da57a16872297de5b6bb7754f722381631d4dca4 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Oct 2020 08:26:16 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 341d9e601ee..fcf817b82c8 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -20,7 +20,6 @@ edition = "2018" cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" -lazy_static = "1.0.2" pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ad8d12e6d..826a059f92a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -7,6 +7,7 @@ #![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(once_cell)] #![feature(or_patterns)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 065c7c042d3..b4b4b3dc18d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -18,9 +18,9 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust + /// ```rust,ignore /// #[macro_use] - /// use lazy_static; + /// use some_macro; /// ``` pub MACRO_USE_IMPORTS, pedantic, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 03f8c5a2c07..dd2fd0bb445 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -2,10 +2,10 @@ #![deny(clippy::missing_docs_in_private_items)] -use lazy_static::lazy_static; use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem}; use rustc_span::source_map; use source_map::Span; +use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::{env, fmt, fs, io}; @@ -54,9 +54,8 @@ impl From for Error { } } -lazy_static! { - static ref ERRORS: Mutex> = Mutex::new(Vec::new()); -} +/// Vec of errors that might be collected during config toml parsing +static ERRORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); macro_rules! define_Conf { ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { @@ -82,6 +81,7 @@ macro_rules! define_Conf { use serde::Deserialize; pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { use super::super::{ERRORS, Error}; + Ok( <$Ty>::deserialize(deserializer).unwrap_or_else(|e| { ERRORS -- cgit 1.4.1-3-g733a5 From 7021d70d2e57e081597ea34eb18226a71b665ecd Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 6 Oct 2020 23:58:32 +0800 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index dadd0f8ebb7..a9650e531b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -400,8 +400,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call using `_.flat_map(_)` + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.flat_map(_)` /// /// **Known problems:** /// @@ -424,8 +424,8 @@ declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter(_).map(_)`, /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.filter_map(_)`. /// /// **Known problems:** Often requires a condition + Option/Iterator creation /// inside the closure. @@ -452,8 +452,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find_map(_)`. /// /// **Known problems:** None /// @@ -496,8 +496,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.find(_).map(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find_map(_)`. /// /// **Known problems:** Often requires a condition + Option/Iterator creation /// inside the closure. @@ -1276,8 +1276,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.as_deref()`. /// /// **Known problems:** None. /// -- cgit 1.4.1-3-g733a5 From b05aeaa9bce36d69d9ee13d1b5f6a271b6e8c1e5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 6 Oct 2020 23:32:38 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index d2a322e1223..71a30d1c33d 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await( - db, - &obligation, - ); - if let Trait(trait_pred, _) = - obligation.predicate.skip_binders() - { + infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred, _) = obligation.predicate.skip_binders() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e0651f9ab5d..ec70b2f1e20 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1668,9 +1668,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let ty::Opaque(def_id, _) = *ret_ty.kind() { // one of the associated types must be Self for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - if let ty::PredicateAtom::Projection(projection_predicate) = - predicate.skip_binders() - { + if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self if contains_ty(projection_predicate.ty, self_ty) { return; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 247effde19b..b2a8e3f08d5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1287,8 +1287,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateAtom::Trait(trait_predicate, _) = predicate.skip_binders() { - if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() - { + if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { return true; } } -- cgit 1.4.1-3-g733a5 From a5ef305cb5a10437963ecda82a833b6932dc70d1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 5 Oct 2020 12:19:35 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ad8d12e6d..6ae8ae9e38b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1475,7 +1475,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), - LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1618,7 +1617,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(&try_err::TRY_ERR), @@ -1831,6 +1829,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_borrow::NEEDLESS_BORROW), LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), + LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&transmute::USELESS_TRANSMUTE), LintId::of(&use_self::USE_SELF), ]); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 15b66684eab..203af06a734 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -80,7 +80,7 @@ declare_clippy_lint! { /// let bs = b"a byte string"; /// ``` pub STRING_LIT_AS_BYTES, - style, + nursery, "calling `as_bytes` on a string literal instead of using a byte string literal" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0dba5a71c50..959ac6d42bf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2161,7 +2161,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "string_lit_as_bytes", - group: "style", + group: "nursery", desc: "calling `as_bytes` on a string literal instead of using a byte string literal", deprecation: None, module: "strings", -- cgit 1.4.1-3-g733a5 From 0e159a55d66f4c3cd56237f28a36f31599442102 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 6 Oct 2020 19:46:03 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72ad8d12e6d..a5a24536772 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1171,6 +1171,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), @@ -1504,7 +1505,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::TYPE_COMPLEXITY), LintId::of(&types::UNIT_ARG), @@ -1805,7 +1805,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), - LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), ]); diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 17d950169fd..5e83b6c81ec 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -242,7 +242,7 @@ declare_clippy_lint! { /// fn foo(interned: Rc) { ... } /// ``` pub RC_BUFFER, - perf, + restriction, "shared ownership of a buffer type" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0dba5a71c50..cc66b1a2db6 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1888,7 +1888,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "rc_buffer", - group: "perf", + group: "restriction", desc: "shared ownership of a buffer type", deprecation: None, module: "types", -- cgit 1.4.1-3-g733a5 From 5bad9175fb363917ffee2c2da7223e96daa2f5ac Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:37:32 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 826a059f92a..d11eeff8032 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -281,6 +281,7 @@ mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; mod ptr; +mod ptr_eq; mod ptr_offset_with_cast; mod question_mark; mod ranges; @@ -778,6 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ptr::CMP_NULL, &ptr::MUT_FROM_REF, &ptr::PTR_ARG, + &ptr_eq::PTR_EQ, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &question_mark::QUESTION_MARK, &ranges::RANGE_MINUS_ONE, @@ -916,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold)); store.register_late_pass(|| box ptr::Ptr); + store.register_late_pass(|| box ptr_eq::PtrEq); store.register_late_pass(|| box needless_bool::NeedlessBool); store.register_late_pass(|| box needless_bool::BoolComparison); store.register_late_pass(|| box approx_const::ApproxConstant); @@ -1456,6 +1459,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), @@ -1612,6 +1616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs new file mode 100644 index 00000000000..a05cb6270b7 --- /dev/null +++ b/clippy_lints/src/ptr_eq.rs @@ -0,0 +1,96 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Use `std::ptr::eq` when applicable + /// + /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(PtrEq => [PTR_EQ]); + +static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; + +impl LateLintPass<'_> for PtrEq { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if utils::in_macro(expr.span) { + return; + } + + if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if BinOpKind::Eq == op.node { + let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs), + _ => (&**left, &**right), + }; + + if_chain! { + if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); + if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); + if let Some(left_snip) = utils::snippet_opt(cx, left_var.span); + if let Some(right_snip) = utils::snippet_opt(cx, right_var.span); + then { + utils::span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + LINT_MSG, + "try", + format!("std::ptr::eq({}, {})", left_snip, right_snip), + Applicability::MachineApplicable, + ); + } + } + } + } + } +} + +// If the given expression is a cast to an usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} + +// If the given expression is a cast to a `*const` pointer, return the lhs of the cast +// E.g., `foo as *const _` returns `foo`. +fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed new file mode 100644 index 00000000000..209081e6e80 --- /dev/null +++ b/tests/ui/ptr_eq.fixed @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = std::ptr::eq(a, b); + let _ = std::ptr::eq(a, b); + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs new file mode 100644 index 00000000000..69162870807 --- /dev/null +++ b/tests/ui/ptr_eq.rs @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = a as *const _ as usize == b as *const _ as usize; + let _ = a as *const _ == b as *const _; + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr new file mode 100644 index 00000000000..45d8c60382b --- /dev/null +++ b/tests/ui/ptr_eq.stderr @@ -0,0 +1,16 @@ +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:20:13 + | +LL | let _ = a as *const _ as usize == b as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + | + = note: `-D clippy::ptr-eq` implied by `-D warnings` + +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:21:13 + | +LL | let _ = a as *const _ == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 6edde811b5437f926c26b1b844acd60e3dd0a0c9 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 17:11:11 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index a05cb6270b7..3be792ce5e4 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Use `std::ptr::eq` when applicable /// - /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// **Why is this bad?** `ptr::eq` can be used to compare `&T` references /// (which coerce to `*const T` implicitly) by their address rather than /// comparing the values they point to. /// -- cgit 1.4.1-3-g733a5 From 11672577debb4880edb5641c470c78175b121bcb Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 8 Oct 2020 01:07:00 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index dfc158661cb..95594e38c9e 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -143,7 +143,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { let mut parser = regex_syntax::ParserBuilder::new() - .unicode(utf8) + .unicode(true) .allow_invalid_utf8(!utf8) .build(); diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 9767e5bf76a..f7f3b195ccc 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -71,6 +71,9 @@ fn trivial_regex() { let non_trivial_ends_with = Regex::new("foo|bar"); let non_trivial_binary = BRegex::new("foo|bar"); let non_trivial_binary_builder = BRegexBuilder::new("foo|bar"); + + // #6005: unicode classes in bytes::Regex + let a_byte_of_unicode = BRegex::new(r"\p{C}"); } fn main() { -- cgit 1.4.1-3-g733a5 From 738ed383066a561ae4f3ef0a6a2d570eaba7b6fa Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Wed, 7 Oct 2020 21:41:54 -0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 82549c12d0a..ffe81b3966c 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -6,6 +6,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use crate::utils::span_lint; @@ -60,12 +61,18 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: intravisit::FnKind<'tcx>, + fn_kind: intravisit::FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, _: Span, hir_id: HirId, ) { + if let Some(header) = fn_kind.header() { + if header.abi == Abi::Cdecl { + return; + } + } + // If the method is an impl for a trait, don't warn. let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_node = cx.tcx.hir().find(parent_id); -- cgit 1.4.1-3-g733a5 From 15150c07eaeec50138a9c96c5bdecbdf92c3101e Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Wed, 7 Oct 2020 22:49:50 -0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index ffe81b3966c..b222475486d 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { hir_id: HirId, ) { if let Some(header) = fn_kind.header() { - if header.abi == Abi::Cdecl { + if header.abi == Abi::C { return; } } diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index c0a52d832c0..377a0fb502f 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -174,3 +174,8 @@ mod issue_3739 { }; } } + +/// Issue #5542 +/// +/// This shouldn't warn for `boxed_local` as it is a function implemented in C. +pub extern "C" fn do_now_warn_me(_c_pointer: Box) -> () {} -- cgit 1.4.1-3-g733a5 From 3a6f59ecae90892c3162df95d9633c4263ab91fa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 8 Oct 2020 04:50:24 -0700 Subject: Document string_lit_as_bytes known problems --- clippy_lints/src/strings.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 203af06a734..415c07b2261 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -69,7 +69,27 @@ declare_clippy_lint! { /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used /// instead. They are shorter but less discoverable than `as_bytes()`. /// - /// **Known Problems:** None. + /// **Known Problems:** + /// `"str".as_bytes()` and the suggested replacement of `b"str"` are not + /// equivalent because they have different types. The former is `&[u8]` + /// while the latter is `&[u8; 3]`. That means in general they will have a + /// different set of methods and different trait implementations. + /// + /// ```rust + /// fn f(v: Vec) {} + /// + /// f("...".as_bytes().to_owned()); // works + /// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec + /// + /// fn g(r: impl std::io::Read) {} + /// + /// g("...".as_bytes()); // works + /// g(b"..."); // does not work + /// ``` + /// + /// The actual equivalent of `"str".as_bytes()` with the same type is not + /// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not + /// more readable than a function call. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From c81bea45f480d9d5a785778c944a3ad3daf30d27 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 8 Oct 2020 04:54:17 -0700 Subject: Make clippy_lints's doc tests succeed --- clippy_lints/src/strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 415c07b2261..3783bd78de2 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -75,7 +75,7 @@ declare_clippy_lint! { /// while the latter is `&[u8; 3]`. That means in general they will have a /// different set of methods and different trait implementations. /// - /// ```rust + /// ```compile_fail /// fn f(v: Vec) {} /// /// f("...".as_bytes().to_owned()); // works -- cgit 1.4.1-3-g733a5 From cc26924cce6d6e6ee7913ba5026d205eefd42702 Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Thu, 8 Oct 2020 09:03:11 -0300 Subject: clippy_lint: Extend BoxedLocal ignored ABI to all non-rust ABIs. --- clippy_lints/src/escape.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index b222475486d..8b022912573 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { hir_id: HirId, ) { if let Some(header) = fn_kind.header() { - if header.abi == Abi::C { + if header.abi != Abi::Rust { return; } } -- cgit 1.4.1-3-g733a5 From 732d370404198ca68a96901b25a5f2ab3ba9d562 Mon Sep 17 00:00:00 2001 From: Jean SIMARD Date: Fri, 9 Oct 2020 09:47:53 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4a3eb9c983a..383a62da821 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -13,14 +13,14 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more /// idiomatically done with `Option::map_or` (if the else bit is a pure /// expression) or `Option::map_or_else` (if the else bit is an impure - /// expresion). + /// expression). /// /// **Why is this bad?** /// Using the dedicated functions of the Option type is clearer and - /// more concise than an if let expression. + /// more concise than an `if let` expression. /// /// **Known problems:** /// This lint uses a deliberately conservative metric for checking -- cgit 1.4.1-3-g733a5 From 7b7ddfa55da889b41e90243ac59a04eed832a71e Mon Sep 17 00:00:00 2001 From: Sebastian Andersson Date: Fri, 9 Oct 2020 20:23:03 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index d6541010bca..26da058598e 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, + is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, span_lint_and_then, }; use if_chain::if_chain; @@ -132,7 +132,11 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option [], }` if tup.is_empty() { - return Some(format!("{:?}.to_string()", s.as_str())); + if let Some(s_src) = snippet_opt(cx, lit.span) { + // Simulate macro expansion, converting {{ and }} to { and }. + let s_expand = s_src.replace("{{", "{").replace("}}", "}"); + return Some(format!("{}.to_string()", s_expand)) + } } else if s.as_str().is_empty() { return on_argumentv1_new(cx, &tup[0], arms); } diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 30651476999..740a22a07d7 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -13,7 +13,8 @@ fn main() { "foo".to_string(); "{}".to_string(); "{} abc {}".to_string(); - "foo {}\n\" bar".to_string(); + r##"foo {} +" bar"##.to_string(); "foo".to_string(); format!("{:?}", "foo"); // Don't warn about `Debug`. diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 9734492154e..96df7f37f77 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -25,7 +25,13 @@ LL | / format!( LL | | r##"foo {{}} LL | | " bar"## LL | | ); - | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();` + | |______^ + | +help: consider using `.to_string()` + | +LL | r##"foo {} +LL | " bar"##.to_string(); + | error: useless use of `format!` --> $DIR/format.rs:21:5 -- cgit 1.4.1-3-g733a5 From 26e4de9557e5924d001652138ec0f8f40dc40372 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 9 Oct 2020 00:49:27 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 062c9bd2d9e..c5e33b288a9 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -40,6 +40,8 @@ pub enum Constant { Tuple(Vec), /// A raw pointer. RawPtr(u128), + /// A reference + Ref(Box), /// A literal with syntax error. Err(Symbol), } @@ -66,6 +68,7 @@ impl PartialEq for Constant { (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -110,6 +113,9 @@ impl Hash for Constant { Self::RawPtr(u) => { u.hash(state); }, + Self::Ref(ref r) => { + r.hash(state); + }, Self::Err(ref s) => { s.hash(state); }, @@ -144,6 +150,7 @@ impl Constant { x => x, } }, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), // TODO: are there any useful inter-type orderings? _ => None, } @@ -239,7 +246,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { UnOp::UnNot => self.constant_not(&o, self.typeck_results.expr_ty(e)), UnOp::UnNeg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), - UnOp::UnDeref => Some(o), + UnOp::UnDeref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), ExprKind::Call(ref callee, ref args) => { @@ -269,6 +276,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } }, ExprKind::Index(ref arr, ref index) => self.index(arr, index), + ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), // TODO: add other expressions. _ => None, } diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 9fa0e5f5c07..586784b73e6 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -2,6 +2,7 @@ #![allow( unused, clippy::no_effect, + clippy::op_ref, clippy::unnecessary_operation, clippy::cast_lossless, clippy::many_single_char_names @@ -116,4 +117,8 @@ fn main() { 1.23f64.signum() != x64.signum(); 1.23f64.signum() != -(x64.signum()); 1.23f64.signum() != 3.21f64.signum(); + + // the comparison should also look through references + &0.0 == &ZERO; + &&&&0.0 == &&&&ZERO; } diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index f7c380fc915..bb4051c4662 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:65:5 + --> $DIR/float_cmp.rs:66:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:70:5 + --> $DIR/float_cmp.rs:71:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -16,7 +16,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:73:5 + --> $DIR/float_cmp.rs:74:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:93:5 + --> $DIR/float_cmp.rs:94:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:99:5 | LL | a1 == a2; | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:99:5 + --> $DIR/float_cmp.rs:100:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` -- cgit 1.4.1-3-g733a5 From a98f9d21fcdad85ae95d00d3931cf438f9d5a9de Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 22:22:21 +0200 Subject: (Hacky) Fix for ICE #6139 --- clippy_lints/src/types.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5e83b6c81ec..a982b92bb2b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -541,6 +541,11 @@ impl Types { _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); + // HACK(flip1995): This is a fix for an ICE occuring when `ty_ty` is a + // trait object with a lifetime, e.g. `dyn T<'_>`. Since trait objects + // don't have a known size, this shouldn't introduce FNs. But there + // should be a better solution. + if !matches!(ty_ty.kind(), ty::Dynamic(..)); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size <= self.vec_box_size_threshold; -- cgit 1.4.1-3-g733a5 From cf81975d7765bf0cf82fee9f3e991c880f77cd13 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 10 Oct 2020 14:04:14 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 8a2dbdc50ea..4525b12689f 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,6 +1,7 @@ use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -120,7 +121,11 @@ fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bo size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span) }, Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)), - Adt(..) => cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(span), cx.param_env), + Adt(..) => { + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.has_escaping_bound_vars() + && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + }, _ => false, } } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a982b92bb2b..9a948af8bfc 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -17,6 +17,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -541,11 +542,7 @@ impl Types { _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); - // HACK(flip1995): This is a fix for an ICE occuring when `ty_ty` is a - // trait object with a lifetime, e.g. `dyn T<'_>`. Since trait objects - // don't have a known size, this shouldn't introduce FNs. But there - // should be a better solution. - if !matches!(ty_ty.kind(), ty::Dynamic(..)); + if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size <= self.vec_box_size_threshold; -- cgit 1.4.1-3-g733a5 From 6021c231599eabcb07b3a8207bddbb3796c93eee Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 11 Oct 2020 13:27:20 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb6cf75d96..f21768c4498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1918,6 +1918,7 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 50b39cf4ea7..212a3100637 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,8 +1,9 @@ use crate::utils::{ - attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path, - must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, - trait_ref_of_method, type_is_unsafe_function, + attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, + span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, }; +use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -169,6 +171,52 @@ declare_clippy_lint! { "function or method that could take a `#[must_use]` attribute" } +declare_clippy_lint! { + /// **What it does:** Checks for public functions that return a `Result` + /// with an `Err` type of `()`. It suggests using a custom type that + /// implements [`std::error::Error`]. + /// + /// **Why is this bad?** Unit does not implement `Error` and carries no + /// further information about what went wrong. + /// + /// **Known problems:** Of course, this lint assumes that `Result` is used + /// for a fallible operation (which is after all the intended use). However + /// code may opt to (mis)use it as a basic two-variant-enum. In that case, + /// the suggestion is misguided, and the code should use a custom enum + /// instead. + /// + /// **Examples:** + /// ```rust + /// pub fn read_u8() -> Result { Err(()) } + /// ``` + /// should become + /// ```rust,should_panic + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct EndOfStream; + /// + /// impl fmt::Display for EndOfStream { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } + /// } + /// + /// impl std::error::Error for EndOfStream { } + /// + /// pub fn read_u8() -> Result { Err(EndOfStream) } + ///# fn main() { + ///# read_u8().unwrap(); + ///# } + /// ``` + /// + /// Note that there are crates that simplify creating the error type, e.g. + /// [`thiserror`](https://docs.rs/thiserror). + pub RESULT_UNIT_ERR, + style, + "public function returning `Result` with an `Err` type of `()`" +} + #[derive(Copy, Clone)] pub struct Functions { threshold: u64, @@ -188,6 +236,7 @@ impl_lint_pass!(Functions => [ MUST_USE_UNIT, DOUBLE_MUST_USE, MUST_USE_CANDIDATE, + RESULT_UNIT_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -233,15 +282,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attr = must_use_attr(&item.attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); return; } - if cx.access_levels.is_exported(item.hir_id) - && !is_proc_macro(cx.sess(), &item.attrs) - && attr_by_name(&item.attrs, "no_mangle").is_none() - { + if is_public && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() { check_must_use_candidate( cx, &sig.decl, @@ -257,11 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public && trait_ref_of_method(cx, item.hir_id).is_none() { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); - } else if cx.access_levels.is_exported(item.hir_id) + } else if is_public && !is_proc_macro(cx.sess(), &item.attrs) && trait_ref_of_method(cx, item.hir_id).is_none() { @@ -284,18 +338,21 @@ impl<'tcx> LateLintPass<'tcx> for Functions { if sig.header.abi == Abi::Rust { self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); } + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); } if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) - { + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), &item.attrs) { check_must_use_candidate( cx, &sig.decl, @@ -411,6 +468,29 @@ impl<'tcx> Functions { } } +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { + if_chain! { + if !in_external_macro(cx.sess(), item_span); + if let hir::FnRetTy::Return(ref ty) = decl.output; + if let hir::TyKind::Path(ref qpath) = ty.kind; + if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type)); + if let Some(ref args) = last_path_segment(qpath).args; + if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; + if let hir::TyKind::Tup(t) = err_ty.kind; + if t.is_empty(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "This returns a `Result<_, ()>", + None, + "Use a custom Error type instead", + ); + } + } +} + fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 097eca0af57..26a727687b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -582,6 +582,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, &functions::NOT_UNSAFE_PTR_ARG_DEREF, + &functions::RESULT_UNIT_ERR, &functions::TOO_MANY_ARGUMENTS, &functions::TOO_MANY_LINES, &future_not_send::FUTURE_NOT_SEND, @@ -1327,6 +1328,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), LintId::of(&identity_op::IDENTITY_OP), @@ -1558,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96c9d12d75f..d0fc8f0c8a9 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2005,6 +2005,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, + Lint { + name: "result_unit_err", + group: "style", + desc: "public function returning `Result` with an `Err` type of `()`", + deprecation: None, + module: "functions", + }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 445fc8d31d7..f47b81a450e 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,5 +1,6 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] +#![allow(clippy::result_unit_err)] use std::io; diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index f44d6693d30..c7b616e2897 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:6:1 + --> $DIR/doc_errors.rs:7:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:10:1 + --> $DIR/doc_errors.rs:11:1 | LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:15:1 + --> $DIR/doc_errors.rs:16:1 | LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:20:1 + --> $DIR/doc_errors.rs:21:1 | LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:50:5 + --> $DIR/doc_errors.rs:51:5 | LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:55:5 + --> $DIR/doc_errors.rs:56:5 | LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -49,7 +49,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:84:5 + --> $DIR/doc_errors.rs:85:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index a48e675e4ea..05e087b08bc 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -1,4 +1,5 @@ #![warn(clippy::double_must_use)] +#![allow(clippy::result_unit_err)] #[must_use] pub fn must_use_result() -> Result<(), ()> { diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr index bc37785294f..8290ece1cad 100644 --- a/tests/ui/double_must_use.stderr +++ b/tests/ui/double_must_use.stderr @@ -1,5 +1,5 @@ error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:4:1 + --> $DIR/double_must_use.rs:5:1 | LL | pub fn must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn must_use_result() -> Result<(), ()> { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:9:1 + --> $DIR/double_must_use.rs:10:1 | LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:14:1 + --> $DIR/double_must_use.rs:15:1 | LL | pub fn must_use_array() -> [Result<(), ()>; 1] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs new file mode 100644 index 00000000000..a66f581b215 --- /dev/null +++ b/tests/ui/result_unit_error.rs @@ -0,0 +1,38 @@ +#[warn(clippy::result_unit_err)] +#[allow(unused)] + +pub fn returns_unit_error() -> Result { + Err(()) +} + +fn private_unit_errors() -> Result { + Err(()) +} + +pub trait HasUnitError { + fn get_that_error(&self) -> Result; + + fn get_this_one_too(&self) -> Result { + Err(()) + } +} + +impl HasUnitError for () { + fn get_that_error(&self) -> Result { + Ok(true) + } +} + +trait PrivateUnitError { + fn no_problem(&self) -> Result; +} + +pub struct UnitErrorHolder; + +impl UnitErrorHolder { + pub fn unit_error(&self) -> Result { + Ok(0) + } +} + +fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr new file mode 100644 index 00000000000..986d9718acd --- /dev/null +++ b/tests/ui/result_unit_error.stderr @@ -0,0 +1,35 @@ +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:4:1 + | +LL | pub fn returns_unit_error() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:13:5 + | +LL | fn get_that_error(&self) -> Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:15:5 + | +LL | fn get_this_one_too(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:33:5 + | +LL | pub fn unit_error(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 74ae116131696e4385d5b8e5da34deaad0d25ec9 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 11 Oct 2020 22:15:56 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 212a3100637..fd45a6da61c 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -194,19 +194,19 @@ declare_clippy_lint! { /// use std::fmt; /// /// #[derive(Debug)] - /// struct EndOfStream; + /// pub struct EndOfStream; /// /// impl fmt::Display for EndOfStream { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "End of Stream") - /// } + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } /// } /// /// impl std::error::Error for EndOfStream { } /// /// pub fn read_u8() -> Result { Err(EndOfStream) } ///# fn main() { - ///# read_u8().unwrap(); + ///# read_u8().unwrap(); ///# } /// ``` /// @@ -483,9 +483,9 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span cx, RESULT_UNIT_ERR, fn_header_span, - "This returns a `Result<_, ()>", + "this returns a `Result<_, ()>", None, - "Use a custom Error type instead", + "use a custom Error type instead", ); } } diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 986d9718acd..b8230032491 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,35 +1,35 @@ -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:4:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:13:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:15:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:33:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 32fdb8fb0c15ddc202eed70b82babca8d529e39b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 8 Oct 2020 23:02:16 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index e16ec783fab..7126c98a0b4 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,9 +1,13 @@ +use crate::utils::ast_utils::eq_expr; use crate::utils::{ eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; +use if_chain::if_chain; +use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -23,6 +27,12 @@ declare_clippy_lint! { /// # let x = 1; /// if x + 1 == x + 1 {} /// ``` + /// or + /// ```rust + /// # let a = 3; + /// # let b = 4; + /// assert_eq!(a, a); + /// ``` pub EQ_OP, correctness, "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)" @@ -52,6 +62,31 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); +impl EarlyLintPass for EqOp { + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + if_chain! { + if mac.path == sym!(assert_eq); + let tokens = mac.args.inner_tokens(); + let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + if let Ok(left) = parser.parse_expr(); + if parser.eat(&token::Comma); + if let Ok(right) = parser.parse_expr(); + let left_expr = left.into_inner(); + let right_expr = right.into_inner(); + if eq_expr(&left_expr, &right_expr); + + then { + span_lint( + cx, + EQ_OP, + left_expr.span.to(right_expr.span), + "identical args used in this `assert_eq!` macro call", + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fc4afde9d9e..dd99b6b9040 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,6 +348,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); + store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 3df8be6c232..e369f62f8bf 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] +#![allow(clippy::eq_op)] extern crate proc_macro; diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 9c7590c7dd6..ff1dc76ab63 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code)] +#![allow(dead_code, clippy::eq_op)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs new file mode 100644 index 00000000000..cf5660ea98d --- /dev/null +++ b/tests/ui/eq_op_early.rs @@ -0,0 +1,15 @@ +#![warn(clippy::eq_op)] + +fn main() { + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` (see #3574) + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr new file mode 100644 index 00000000000..9206e9026e9 --- /dev/null +++ b/tests/ui/eq_op_early.stderr @@ -0,0 +1,16 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:8:16 + | +LL | assert_eq!(a, a); + | ^^^^ + | + = note: `-D clippy::eq-op` implied by `-D warnings` + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:9:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 8e0243c49aa..d8bda7e8f48 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -3,7 +3,7 @@ #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::blacklisted_name, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] #[macro_use] -- cgit 1.4.1-3-g733a5 From a3e0446afe0ebd7a420f65cd6aec1c56687f0ef5 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 09:31:53 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 7126c98a0b4..a95d71042ee 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -7,6 +7,7 @@ use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,10 +65,20 @@ declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl EarlyLintPass for EqOp { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + let macro_list = [ + sym!(assert_eq), + sym!(assert_ne), + sym!(debug_assert_eq), + sym!(debug_assert_ne), + ]; if_chain! { - if mac.path == sym!(assert_eq); + if !in_external_macro(cx.sess, mac.span()); + if mac.path.segments.len() == 1; + let macro_name = mac.path.segments[0].ident.name; + if macro_list.contains(¯o_name); let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + let mut parser = parser::Parser::new( + &cx.sess.parse_sess, tokens, false, None); if let Ok(left) = parser.parse_expr(); if parser.eat(&token::Comma); if let Ok(right) = parser.parse_expr(); @@ -80,7 +91,7 @@ impl EarlyLintPass for EqOp { cx, EQ_OP, left_expr.span.to(right_expr.span), - "identical args used in this `assert_eq!` macro call", + &format!("identical args used in this `{}!` macro call", macro_name), ); } } diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs index cf5660ea98d..25e1c6ac6b7 100644 --- a/tests/ui/eq_op_early.rs +++ b/tests/ui/eq_op_early.rs @@ -7,9 +7,32 @@ fn main() { // lint identical args in `assert_eq!` (see #3574) assert_eq!(a, a); assert_eq!(a + 1, a + 1); - // ok assert_eq!(a, b); assert_eq!(a, a + 1); assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); } diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr index 9206e9026e9..1df094fae18 100644 --- a/tests/ui/eq_op_early.stderr +++ b/tests/ui/eq_op_early.stderr @@ -12,5 +12,41 @@ error: identical args used in this `assert_eq!` macro call LL | assert_eq!(a + 1, a + 1); | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:16:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:17:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:24:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:25:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:32:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:33:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From e2124086b8107a59129e163aa120dec50add0f77 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 11:17:51 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 10a64769585..6c969c3ead0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,4 +1,4 @@ -use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; +use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; @@ -220,6 +220,10 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { }; let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if in_macro(lhs.span) || in_macro(rhs.span) { + return false; + } // Do not spawn warning if `IFS_SAME_COND` already produced it. if eq_expr_value(cx, lhs, rhs) { return false; diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 686867cf5c6..7f28f025790 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -77,4 +77,14 @@ fn ifs_same_cond_fn() { } } -fn main() {} +fn main() { + // macro as condition (see #6168) + let os = if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "windows") { + "windows" + } else { + "linux" + }; + println!("{}", os); +} -- cgit 1.4.1-3-g733a5 From 121a047645270d5e9ac965d57c324301ea1f21c0 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 23:46:23 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index a95d71042ee..9653e62cad0 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,14 +1,11 @@ -use crate::utils::ast_utils::eq_expr; use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, + eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::{ast, token}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_parse::parser; +use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -63,44 +60,38 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -impl EarlyLintPass for EqOp { - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - let macro_list = [ - sym!(assert_eq), - sym!(assert_ne), - sym!(debug_assert_eq), - sym!(debug_assert_ne), - ]; - if_chain! { - if !in_external_macro(cx.sess, mac.span()); - if mac.path.segments.len() == 1; - let macro_name = mac.path.segments[0].ident.name; - if macro_list.contains(¯o_name); - let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new( - &cx.sess.parse_sess, tokens, false, None); - if let Ok(left) = parser.parse_expr(); - if parser.eat(&token::Comma); - if let Ok(right) = parser.parse_expr(); - let left_expr = left.into_inner(); - let right_expr = right.into_inner(); - if eq_expr(&left_expr, &right_expr); - - then { - span_lint( - cx, - EQ_OP, - left_expr.span.to(right_expr.span), - &format!("identical args used in this `{}!` macro call", macro_name), - ); - } - } - } -} +const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Block(ref block, _) = e.kind { + for stmt in block.stmts { + for amn in &ASSERT_MACRO_NAMES { + if_chain! { + if is_expn_of(stmt.span, amn).is_some(); + if let StmtKind::Semi(ref matchexpr) = stmt.kind; + if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; + if let Some(ref matchheader) = matchblock.expr; + if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; + if let ExprKind::Tup(ref conditions) = headerexpr.kind; + if conditions.len() == 2; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if eq_expr_value(cx, lhs, rhs); + + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", amn), + ); + } + } + } + } + } if let ExprKind::Binary(op, ref left, ref right) = e.kind { if e.span.from_expansion() { return; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dd99b6b9040..fc4afde9d9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,7 +348,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); - store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -911,7 +910,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); - store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31..3ab4dfc439b 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,6 +60,8 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; + + check_assert_identical_args(); } #[rustfmt::skip] @@ -85,3 +87,54 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn check_assert_identical_args() { + // lint also in macro definition + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078ee..21a63aec7a1 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,98 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:94:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `#[deny(clippy::eq_op)]` on by default + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:95:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:110:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:111:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:118:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:119:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:96:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:97:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:126:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:127:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:134:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:135:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 39 previous errors diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs deleted file mode 100644 index 25e1c6ac6b7..00000000000 --- a/tests/ui/eq_op_early.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![warn(clippy::eq_op)] - -fn main() { - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` (see #3574) - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); -} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr deleted file mode 100644 index 1df094fae18..00000000000 --- a/tests/ui/eq_op_early.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:8:16 - | -LL | assert_eq!(a, a); - | ^^^^ - | - = note: `-D clippy::eq-op` implied by `-D warnings` - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:9:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:16:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:17:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:24:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:25:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:32:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:33:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 8 previous errors - -- cgit 1.4.1-3-g733a5 From e82264860dd275fe95c335929ee9a231c3a61236 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 14 Oct 2020 23:15:01 +1100 Subject: Add a known problem for transmute_ptr_to_ref lint --- clippy_lints/src/transmute.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index c75adb62f25..47c650ac27d 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -98,7 +98,11 @@ declare_clippy_lint! { /// /// **Why is this bad?** This can always be rewritten with `&` and `*`. /// - /// **Known problems:** None. + /// **Known problems:** + /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0, + /// while dereferencing raw pointer is not stable yet. + /// If you need to do this in those places, + /// you would have to use `transmute` instead. /// /// **Example:** /// ```rust,ignore -- cgit 1.4.1-3-g733a5 From 07b2da884cda8103af50beb327723dec8204fc61 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 6 Oct 2020 11:49:08 +0200 Subject: 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 | 46 +++++++------- 12 files changed, 345 insertions(+), 76 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index f21768c4498..93ce6bb85d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,6 +1781,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/less_concise_than.rs new file mode 100644 index 00000000000..097aff4b178 --- /dev/null +++ b/clippy_lints/src/less_concise_than.rs @@ -0,0 +1,107 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// match int_optional { + /// Some(v) => v, + /// None => 1, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// int_optional.unwrap_or(1) + /// ``` + pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" +} + +declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); + +impl LateLintPass<'_> for LessConciseThan { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if utils::in_macro(expr.span) { + return; + } + if lint_option_unwrap_or_case(cx, expr) { + return; + } + } +} + +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + #[allow(clippy::needless_bool)] + fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + if_chain! { + if arms.len() == 2; + if arms.iter().all(|arm| arm.guard.is_none()); + if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| + if_chain! { + if let PatKind::Path(ref qpath) = arm.pat.kind; + if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); + then { true } + else { false } + } + ); + let some_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; + if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); + if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + if let def::Res::Local(body_path_hir_id) = body_path.res; + if body_path_hir_id == binding_hir_id; + then { Some(none_arm) } + else { None } + } + } + if_chain! { + if !utils::usage::contains_return_break_continue_macro(expr); + if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(match_expr); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, + "this pattern can be more concisely encoded with `Option::unwrap_or`", + "replace with", + format!( + "{}.{}({}{})", + match_expr_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { false} + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26a727687b1..2e9900815d9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,6 +224,7 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; +mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -609,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, + &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -1126,6 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box less_concise_than::LessConciseThan); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1210,6 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 383a62da821..eb7624b25a3 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,10 +5,8 @@ use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,53 +82,6 @@ struct OptionIfLetElseOccurence { wrap_braces: bool, } -struct ReturnBreakContinueMacroVisitor { - seen_return_break_continue: bool, -} - -impl ReturnBreakContinueMacroVisitor { - fn new() -> ReturnBreakContinueMacroVisitor { - ReturnBreakContinueMacroVisitor { - seen_return_break_continue: false, - } - } -} - -impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self.seen_return_break_continue { - // No need to look farther if we've already seen one of them - return; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - self.seen_return_break_continue = true; - }, - // Something special could be done here to handle while or for loop - // desugaring, as this will detect a break if there's a while loop - // or a for loop inside the expression. - _ => { - if utils::in_macro(ex.span) { - self.seen_return_break_continue = true; - } else { - rustc_hir::intravisit::walk_expr(self, ex); - } - }, - } - } -} - -fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); - recursive_visitor.visit_expr(expression); - recursive_visitor.seen_return_break_continue -} - /// Extracts the body of a given arm. If the arm contains only an expression, /// then it returns the expression. Otherwise, it returns the entire block fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { @@ -208,8 +159,8 @@ fn detect_option_if_let_else<'tcx>( if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(arms[0].body); - if !contains_return_break_continue_macro(arms[1].body); + if !utils::usage::contains_return_break_continue_macro(arms[0].body); + if !utils::usage::contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 6938d9971d9..30e812c284b 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -82,7 +82,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { /// Identify some potentially computationally expensive patterns. /// This function is named so to stress that its implementation is non-exhaustive. /// It returns FNs and FPs. -fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { // Searches an expression for method calls or function calls that aren't ctors struct FunCallFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index ea1dc3be29b..2fd6046ebcf 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,10 +1,11 @@ +use crate::utils; use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, HirId, Path}; +use rustc_hir::{Expr, ExprKind, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -174,3 +175,50 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } + +struct ReturnBreakContinueMacroVisitor { + seen_return_break_continue: bool, +} + +impl ReturnBreakContinueMacroVisitor { + fn new() -> ReturnBreakContinueMacroVisitor { + ReturnBreakContinueMacroVisitor { + seen_return_break_continue: false, + } + } +} + +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + if utils::in_macro(ex.span) { + self.seen_return_break_continue = true; + } else { + rustc_hir::intravisit::walk_expr(self, ex); + } + }, + } + } +} + +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { + let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 624223ff706..6dc95fcfdb2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,6 +1075,13 @@ vec![ deprecation: None, module: "len_zero", }, + Lint { + name: "less_concise_than_option_unwrap_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + deprecation: None, + module: "less_concise_than", + }, Lint { name: "let_and_return", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/less_concise_than.fixed new file mode 100644 index 00000000000..52b69ebba3e --- /dev/null +++ b/tests/ui/less_concise_than.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + Some(1).unwrap_or(42); + + // richer none expr + Some(1).unwrap_or_else(|| 1 + 42); + + // multiline case + Some(1).unwrap_or_else(|| { + let a = 1 + 42; + let b = a + 42; + b + 42 + }); + + // string case + Some("Bob").unwrap_or("Alice"); + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.rs b/tests/ui/less_concise_than.rs new file mode 100644 index 00000000000..bb2a8f2050a --- /dev/null +++ b/tests/ui/less_concise_than.rs @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + match Some(1) { + Some(i) => i, + None => 42, + }; + + // richer none expr + match Some(1) { + Some(i) => i, + None => 1 + 42, + }; + + // multiline case + match Some(1) { + Some(i) => i, + None => { + let a = 1 + 42; + let b = a + 42; + b + 42 + }, + }; + + // string case + match Some("Bob") { + Some(i) => i, + None => "Alice", + }; + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/less_concise_than.stderr new file mode 100644 index 00000000000..e3e8a406db1 --- /dev/null +++ b/tests/ui/less_concise_than.stderr @@ -0,0 +1,52 @@ +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:7:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + | + = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:13:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:19:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => { +LL | | let a = 1 + 42; +... | +LL | | }, +LL | | }; + | |_____^ + | +help: replace with + | +LL | Some(1).unwrap_or_else(|| { +LL | let a = 1 + 42; +LL | let b = a + 42; +LL | b + 42 +LL | }); + | + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:29:5 + | +LL | / match Some("Bob") { +LL | | Some(i) => i, +LL | | None => "Alice", +LL | | }; + | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index bd91ae4e934..e7441293d45 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,6 +8,7 @@ #![allow( unused_parens, unused_variables, + clippy::less_concise_than_option_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 8a831375b41..7c1ad2949e9 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,135 +1,135 @@ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:26:5 + --> $DIR/shadow.rs:27:5 | LL | let x = &mut x; | ^^^^^^^^^^^^^^^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:25:13 + --> $DIR/shadow.rs:26:13 | LL | let mut x = 1; | ^ error: `x` is shadowed by itself in `{ x }` - --> $DIR/shadow.rs:27:5 + --> $DIR/shadow.rs:28:5 | LL | let x = { x }; | ^^^^^^^^^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:26:9 + --> $DIR/shadow.rs:27:9 | LL | let x = &mut x; | ^ error: `x` is shadowed by itself in `(&*x)` - --> $DIR/shadow.rs:28:5 + --> $DIR/shadow.rs:29:5 | LL | let x = (&*x); | ^^^^^^^^^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:27:9 + --> $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 + --> $DIR/shadow.rs:31:9 | LL | let x = id(x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:30:13 + --> $DIR/shadow.rs:31:13 | LL | let x = id(x); | ^^^^^ note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = { *x + 1 }; | ^ error: `x` is shadowed by `(1, x)` which reuses the original value - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:32:9 | LL | let x = (1, x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:31:13 + --> $DIR/shadow.rs:32:13 | LL | let x = (1, x); | ^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:30:9 + --> $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:32:9 + --> $DIR/shadow.rs:33:9 | LL | let x = first(x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:32:13 + --> $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; | ^ -- cgit 1.4.1-3-g733a5 From 9c9327980becadc15a68307705b3a06c28116ae1 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:42:45 +0200 Subject: manual-unwrap-or / rename files --- clippy_lints/src/less_concise_than.rs | 107 ---------------------------------- clippy_lints/src/manual_unwrap_or.rs | 107 ++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 107 deletions(-) delete mode 100644 clippy_lints/src/less_concise_than.rs create mode 100644 clippy_lints/src/manual_unwrap_or.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/less_concise_than.rs deleted file mode 100644 index 097aff4b178..00000000000 --- a/clippy_lints/src/less_concise_than.rs +++ /dev/null @@ -1,107 +0,0 @@ -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/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs new file mode 100644 index 00000000000..097aff4b178 --- /dev/null +++ b/clippy_lints/src/manual_unwrap_or.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} + } +} -- cgit 1.4.1-3-g733a5 From 6d4eeeabcda6d6d25738e1e8e2b64580daefc4b9 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:55:05 +0200 Subject: 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 ++--- tests/ui/less_concise_than.fixed | 43 ------------- tests/ui/less_concise_than.rs | 55 ----------------- tests/ui/less_concise_than.stderr | 52 ---------------- tests/ui/manual_unwrap_or.fixed | 45 ++++++++++++++ tests/ui/manual_unwrap_or.rs | 60 +++++++++++++++++++ tests/ui/manual_unwrap_or.stderr | 61 +++++++++++++++++++ tests/ui/shadow.rs | 2 +- 11 files changed, 239 insertions(+), 217 deletions(-) delete mode 100644 tests/ui/less_concise_than.fixed delete mode 100644 tests/ui/less_concise_than.rs delete mode 100644 tests/ui/less_concise_than.stderr create mode 100644 tests/ui/manual_unwrap_or.fixed create mode 100644 tests/ui/manual_unwrap_or.rs create mode 100644 tests/ui/manual_unwrap_or.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ce6bb85d8..d82f970b8bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,7 +1781,6 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero -[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use @@ -1797,6 +1796,7 @@ Released 2018-09-13 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2e9900815d9..d4d2f92a6a6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,7 +224,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -235,6 +234,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod manual_strip; +mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_identity; @@ -610,7 +610,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -642,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_strip::MANUAL_STRIP, + &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, &map_err_ignore::MAP_ERR_IGNORE, &map_identity::MAP_IDENTITY, @@ -1128,7 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); - store.register_late_pass(|| box less_concise_than::LessConciseThan); + store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1213,7 +1213,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), @@ -1371,6 +1370,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1666,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 097aff4b178..9d8fc863424 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,12 +2,14 @@ use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -16,7 +18,7 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_optional { + /// match int_option { /// Some(v) => v, /// None => 1, /// } @@ -24,39 +26,35 @@ declare_clippy_lint! { /// /// Use instead: /// ```rust - /// int_optional.unwrap_or(1) + /// int_option.unwrap_or(1) /// ``` - pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, - pedantic, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" } -declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); +declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for LessConciseThan { +impl LateLintPass<'_> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if utils::in_macro(expr.span) { - return; - } - if lint_option_unwrap_or_case(cx, expr) { + if in_external_macro(cx.sess(), expr.span) { return; } + lint_option_unwrap_or_case(cx, expr); } } fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - #[allow(clippy::needless_bool)] fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if_chain! { - if let PatKind::Path(ref qpath) = arm.pat.kind; - if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); - then { true } - else { false } - } + if let PatKind::Path(ref qpath) = arm.pat.kind { + utils::match_qpath(qpath, &utils::paths::OPTION_NONE) + } else { + false + } ); let some_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; @@ -65,43 +63,50 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - then { Some(none_arm) } - else { None } + if !utils::usage::contains_return_break_continue_macro(none_arm.body); + then { + Some(none_arm) + } + else { + None + } } } + if_chain! { - if !utils::usage::contains_return_break_continue_macro(expr); - if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(match_expr); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); - if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); - if let Some(indent) = utils::indent_of(cx, expr.span); - then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; - utils::span_lint_and_sugg( - cx, - LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, - "this pattern can be more concisely encoded with `Option::unwrap_or`", - "replace with", - format!( - "{}.{}({}{})", - match_expr_snippet, - method, - if eager_eval { ""} else { "|| " }, - reindented_none_body - ), - Applicability::MachineApplicable, - ); - true - } else { false} + if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(scrutinee); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `Option::{}`", &method), + "replace with", + format!( + "{}.{}({}{})", + scrutinee_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { + false + } } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6dc95fcfdb2..debd3c31d8b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,13 +1075,6 @@ vec![ deprecation: None, module: "len_zero", }, - Lint { - name: "less_concise_than_option_unwrap_or", - group: "pedantic", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", - deprecation: None, - module: "less_concise_than", - }, Lint { name: "let_and_return", group: "style", @@ -1187,6 +1180,13 @@ vec![ deprecation: None, module: "swap", }, + Lint { + name: "manual_unwrap_or", + group: "complexity", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + deprecation: None, + module: "manual_unwrap_or", + }, Lint { name: "many_single_char_names", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/less_concise_than.fixed deleted file mode 100644 index 52b69ebba3e..00000000000 --- a/tests/ui/less_concise_than.fixed +++ /dev/null @@ -1,43 +0,0 @@ -// 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 deleted file mode 100644 index bb2a8f2050a..00000000000 --- a/tests/ui/less_concise_than.rs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 deleted file mode 100644 index e3e8a406db1..00000000000 --- a/tests/ui/less_concise_than.stderr +++ /dev/null @@ -1,52 +0,0 @@ -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/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed new file mode 100644 index 00000000000..99d30360db1 --- /dev/null +++ b/tests/ui/manual_unwrap_or.fixed @@ -0,0 +1,45 @@ +// run-rustfix +#![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); + + // 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/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs new file mode 100644 index 00000000000..5d03d9db163 --- /dev/null +++ b/tests/ui/manual_unwrap_or.rs @@ -0,0 +1,60 @@ +// run-rustfix +#![allow(dead_code)] + +fn unwrap_or() { + // int case + match Some(1) { + Some(i) => i, + None => 42, + }; + + // int case reversed + match Some(1) { + None => 42, + Some(i) => i, + }; + + // 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/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr new file mode 100644 index 00000000000..03da118a0c4 --- /dev/null +++ b/tests/ui/manual_unwrap_or.stderr @@ -0,0 +1,61 @@ +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:6:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + | + = note: `-D clippy::manual-unwrap-or` implied by `-D warnings` + +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, +LL | | None => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:24: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 reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:34: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 5 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index e7441293d45..e366c75335c 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,7 +8,7 @@ #![allow( unused_parens, unused_variables, - clippy::less_concise_than_option_unwrap_or, + clippy::manual_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] -- cgit 1.4.1-3-g733a5 From fc846c37fcc720c4a5c2e2075102c1957433e703 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 12 Oct 2020 00:06:21 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 9d8fc863424..ced941fac1a 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,3 +1,4 @@ +use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; @@ -18,15 +19,17 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_option { + /// let foo: Option = None; + /// match foo { /// Some(v) => v, /// None => 1, - /// } + /// }; /// ``` /// /// Use instead: /// ```rust - /// int_option.unwrap_or(1) + /// let foo: Option = None; + /// foo.unwrap_or(1); /// ``` pub MANUAL_UNWRAP_OR, complexity, @@ -84,7 +87,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); let method = if eager_eval { "unwrap_or" } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 99d30360db1..a9cc8678c9d 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -9,7 +9,7 @@ fn unwrap_or() { Some(1).unwrap_or(42); // richer none expr - Some(1).unwrap_or_else(|| 1 + 42); + Some(1).unwrap_or(1 + 42); // multiline case Some(1).unwrap_or_else(|| { diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 03da118a0c4..8f6835ed78d 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -18,14 +18,14 @@ LL | | Some(i) => i, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` -error: this pattern reimplements `Option::unwrap_or_else` +error: this pattern reimplements `Option::unwrap_or` --> $DIR/manual_unwrap_or.rs:18:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => 1 + 42, LL | | }; - | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or_else` --> $DIR/manual_unwrap_or.rs:24:5 -- cgit 1.4.1-3-g733a5 From a8fb69f065a427f5d3fc7222b834cad9a2a7a712 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 13 Oct 2020 10:24:00 +0200 Subject: manual-unwrap-or / more pr remarks --- clippy_lints/src/manual_unwrap_or.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index ced941fac1a..719a8b91f66 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -47,7 +47,7 @@ impl LateLintPass<'_> for ManualUnwrapOr { } } -fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; @@ -69,8 +69,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if !utils::usage::contains_return_break_continue_macro(none_arm.body); then { Some(none_arm) - } - else { + } else { None } } @@ -102,14 +101,11 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc "{}.{}({}{})", scrutinee_snippet, method, - if eager_eval { ""} else { "|| " }, + if eager_eval { "" } else { "|| " }, reindented_none_body ), Applicability::MachineApplicable, ); - true - } else { - false } } } -- cgit 1.4.1-3-g733a5 From 690a6a6c0eff1a3edeb5f4c2dcbf9994760c3184 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 14 Oct 2020 22:09:28 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 719a8b91f66..ddb8cc25077 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -83,26 +83,19 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `Option::{}`", &method), + "this pattern reimplements `Option::unwrap_or`", "replace with", format!( - "{}.{}({}{})", + "{}.unwrap_or({})", scrutinee_snippet, - method, - if eager_eval { "" } else { "|| " }, - reindented_none_body + reindented_none_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index debd3c31d8b..6301d623a2b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a9cc8678c9d..a8736f1e6ef 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -12,10 +12,11 @@ fn unwrap_or() { Some(1).unwrap_or(1 + 42); // multiline case - Some(1).unwrap_or_else(|| { - let a = 1 + 42; - let b = a + 42; - b + 42 + #[rustfmt::skip] + Some(1).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 }); // string case @@ -40,6 +41,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 5d03d9db163..bede8cffc32 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -21,13 +21,14 @@ fn unwrap_or() { }; // multiline case + #[rustfmt::skip] match Some(1) { Some(i) => i, None => { - let a = 1 + 42; - let b = a + 42; - b + 42 - }, + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } }; // string case @@ -55,6 +56,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 8f6835ed78d..674f2952635 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -27,29 +27,29 @@ LL | | None => 1 + 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` -error: this pattern reimplements `Option::unwrap_or_else` - --> $DIR/manual_unwrap_or.rs:24:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:25:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => { -LL | | let a = 1 + 42; +LL | | 42 + 42 ... | -LL | | }, +LL | | } LL | | }; | |_____^ | help: replace with | -LL | Some(1).unwrap_or_else(|| { -LL | let a = 1 + 42; -LL | let b = a + 42; -LL | b + 42 +LL | Some(1).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 LL | }); | error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:34:5 + --> $DIR/manual_unwrap_or.rs:35:5 | LL | / match Some("Bob") { LL | | Some(i) => i, -- cgit 1.4.1-3-g733a5 From 32e2021b75f5bb5c83bf753de76ec9ed499d12cc Mon Sep 17 00:00:00 2001 From: Chris Ayoup Date: Wed, 14 Oct 2020 23:49:48 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index c8576bcfcb4..8998fae09de 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -2,7 +2,8 @@ use crate::utils::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -53,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); impl EarlyLintPass for ItemsAfterStatements { fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { - if item.span.from_expansion() { + if in_external_macro(cx.sess(), item.span) { return; } @@ -67,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements { // lint on all further items for stmt in stmts { if let StmtKind::Item(ref it) = *stmt { - if it.span.from_expansion() { + if in_external_macro(cx.sess(), it.span) { return; } if let ItemKind::MacroDef(..) = it.kind { diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index c17a7cbc8d9..377e58e4417 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -28,7 +28,10 @@ fn mac() { // do not lint this, because it needs to be after `a` macro_rules! b { () => {{ - a = 6 + a = 6; + fn say_something() { + println!("something"); + } }}; } b!(); diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index f8f010b5e5c..68a3c81b6a8 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -16,5 +16,18 @@ LL | | println!("foo"); LL | | } | |_____^ -error: aborting due to 2 previous errors +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:32:13 + | +LL | / fn say_something() { +LL | | println!("something"); +LL | | } + | |_____________^ +... +LL | b!(); + | ----- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From ef91de640294e6d0fbf881082196ba83379ea447 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 15 Oct 2020 22:37:53 -0700 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index d2a322e1223..71a30d1c33d 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await( - db, - &obligation, - ); - if let Trait(trait_pred, _) = - obligation.predicate.skip_binders() - { + infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred, _) = obligation.predicate.skip_binders() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index d92eb86fb2e..e90ea0fc200 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -12,8 +12,8 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_target::abi::LayoutOf; -use rustc_target::spec::Target; use rustc_target::spec::abi::Abi; +use rustc_target::spec::Target; declare_clippy_lint! { /// **What it does:** Checks for functions taking arguments by reference, where -- cgit 1.4.1-3-g733a5 From 5a13217ea9c07121e7d3cdcfb0ddd2aa52b90f12 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 16 Oct 2020 17:58:26 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 9653e62cad0..3201adbf9a0 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use if_chain::if_chain; @@ -71,13 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if_chain! { if is_expn_of(stmt.span, amn).is_some(); if let StmtKind::Semi(ref matchexpr) = stmt.kind; - if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; - if let Some(ref matchheader) = matchblock.expr; - if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; - if let ExprKind::Tup(ref conditions) = headerexpr.kind; - if conditions.len() == 2; - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); + if macro_args.len() == 2; + let (lhs, rhs) = (macro_args[0], macro_args[1]); if eq_expr_value(cx, lhs, rhs); then { diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index cc635c2a202..76417aa7ed0 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,7 +1,6 @@ -use crate::utils::{is_direct_expn_of, span_lint}; -use if_chain::if_chain; +use crate::utils::{higher, is_direct_expn_of, span_lint}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp}; +use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; @@ -39,66 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { for dmn in &DEBUG_MACRO_NAMES { if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(span) = extract_call(cx, e) { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } - } -} - -//HACK(hellow554): remove this when #4694 is implemented -fn extract_call<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { - if_chain! { - if let ExprKind::Block(ref block, _) = e.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind; - then { - // debug_assert - if_chain! { - if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; - if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; - if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind; - then { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(condition); - return visitor.expr_span(); - } - } - - // debug_assert_{eq,ne} - if_chain! { - if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; - if let Some(ref matchheader) = matchblock.expr; - if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; - if let ExprKind::Tup(ref conditions) = headerexpr.kind; - if conditions.len() == 2; - then { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind { + if let Some(macro_args) = higher::extract_assert_macro_args(e) { + for arg in macro_args { let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(lhs); + visitor.visit_expr(arg); if let Some(span) = visitor.expr_span() { - return Some(span); - } - } - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(rhs); - if let Some(span) = visitor.expr_span() { - return Some(span); + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!("do not call a function with mutable arguments inside of `{}!`", dmn), + ); } } } } } } - - None } struct MutArgVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 8563b469a30..6d7c5058b4f 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -7,6 +7,7 @@ use crate::utils::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; +use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; /// Converts a hir binary operator to the corresponding `ast` type. @@ -241,3 +242,56 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option(e: &'tcx Expr<'tcx>) -> Option>> { + /// Try to match the AST for a pattern that contains a match, for example when two args are + /// compared + fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option>> { + if_chain! { + if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind; + if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind; + then { + return Some(vec![lhs, rhs]); + } + } + None + } + + if let ExprKind::Block(ref block, _) = e.kind { + if block.stmts.len() == 1 { + if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind { + // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`) + if_chain! { + if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; + if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; + if let ExprKind::Unary(UnOp::UnNot, condition) = droptmp.kind; + then { + return Some(vec![condition]); + } + } + + // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) + if_chain! { + if let ExprKind::Block(ref matchblock,_) = matchexpr.kind; + if let Some(ref matchblock_expr) = matchblock.expr; + then { + return ast_matchblock(matchblock_expr); + } + } + } + } else if let Some(matchblock_expr) = block.expr { + // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) + return ast_matchblock(&matchblock_expr); + } + } + None +} -- cgit 1.4.1-3-g733a5 From bb0ce32423aefcb8b9eb587881973f56a6a6b0ee Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 16 Oct 2020 00:22:35 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 9a948af8bfc..716d027e434 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; +use std::fmt::Display; use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; @@ -1608,18 +1609,23 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!("casting integer literal to `{}` is unnecessary", cast_to), - "try", - format!("{}_{}", n, cast_to), - Applicability::MachineApplicable, - ); + show_unnecessary_cast(cx, expr, n , cast_from, cast_to); return; } } + + match lit.node { + LitKind::Int(num, LitIntType::Unsuffixed) if cast_to.is_integral() => { + show_unnecessary_cast(cx, expr, num, cast_from, cast_to); + return; + }, + LitKind::Float(num, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + show_unnecessary_cast(cx, expr, num, cast_from, cast_to); + return; + }, + _ => (), + }; + match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { @@ -1646,6 +1652,25 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } +fn show_unnecessary_cast( + cx: &LateContext<'_>, + expr: &Expr<'_>, + num: Num, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { + let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + "try", + format!("{}_{}", num, cast_to), + Applicability::MachineApplicable, + ); +} + fn lint_numeric_casts<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31..4e09d19ea21 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -6,6 +6,7 @@ #[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] #[allow(clippy::nonminimal_bool)] #[allow(unused)] +#[allow(clippy::unnecessary_cast)] fn main() { // simple values and comparisons 1 == 1; diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078ee..ad81b35a766 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -1,5 +1,5 @@ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:11:5 + --> $DIR/eq_op.rs:12:5 | LL | 1 == 1; | ^^^^^^ @@ -7,157 +7,157 @@ LL | 1 == 1; = note: `-D clippy::eq-op` implied by `-D warnings` error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:12:5 + --> $DIR/eq_op.rs:13:5 | LL | "no" == "no"; | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:14:5 + --> $DIR/eq_op.rs:15:5 | LL | false != false; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:15:5 + --> $DIR/eq_op.rs:16:5 | LL | 1.5 < 1.5; | ^^^^^^^^^ error: equal expressions as operands to `>=` - --> $DIR/eq_op.rs:16:5 + --> $DIR/eq_op.rs:17:5 | LL | 1u64 >= 1u64; | ^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:19:5 + --> $DIR/eq_op.rs:20:5 | LL | (1 as u64) & (1 as u64); | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `^` - --> $DIR/eq_op.rs:20:5 + --> $DIR/eq_op.rs:21:5 | LL | 1 ^ ((((((1)))))); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:23:5 + --> $DIR/eq_op.rs:24:5 | LL | (-(2) < -(2)); | ^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:24:5 + --> $DIR/eq_op.rs:25:5 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:6 + --> $DIR/eq_op.rs:25:6 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:27 + --> $DIR/eq_op.rs:25:27 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:25:5 + --> $DIR/eq_op.rs:26:5 | LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:28:5 + --> $DIR/eq_op.rs:29:5 | LL | ([1] != [1]); | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:29:5 + --> $DIR/eq_op.rs:30:5 | LL | ((1, 2) != (1, 2)); | ^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:33:5 + --> $DIR/eq_op.rs:34:5 | LL | 1 + 1 == 2; | ^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:34:5 + --> $DIR/eq_op.rs:35:5 | LL | 1 - 1 == 0; | ^^^^^^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:34:5 + --> $DIR/eq_op.rs:35:5 | LL | 1 - 1 == 0; | ^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:36:5 + --> $DIR/eq_op.rs:37:5 | LL | 1 - 1; | ^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:37:5 + --> $DIR/eq_op.rs:38:5 | LL | 1 / 1; | ^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:38:5 + --> $DIR/eq_op.rs:39:5 | LL | true && true; | ^^^^^^^^^^^^ error: equal expressions as operands to `||` - --> $DIR/eq_op.rs:40:5 + --> $DIR/eq_op.rs:41:5 | LL | true || true; | ^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:46:5 + --> $DIR/eq_op.rs:47:5 | LL | a == b && b == a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:47:5 + --> $DIR/eq_op.rs:48:5 | LL | a != b && b != a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:48:5 + --> $DIR/eq_op.rs:49:5 | LL | a < b && b > a; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:49:5 + --> $DIR/eq_op.rs:50:5 | LL | a <= b && b >= a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:52:5 + --> $DIR/eq_op.rs:53:5 | LL | a == a; | ^^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:62:20 + --> $DIR/eq_op.rs:63:20 | LL | const D: u32 = A / A; | ^^^^^ diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index fb89a9fce3d..ba52fc2703f 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -12,12 +12,19 @@ fn main() { #[rustfmt::skip] let v = vec!(1); &v as &[i32]; - 1.0 as f64; - 1 as u64; 0x10 as f32; 0o10 as f32; 0b10 as f32; 0x11 as f64; 0o11 as f64; 0b11 as f64; + + 1_u32; + 16_i32; + 2_usize; + + 1.0_f64; + 0.5_f32; + + 1.0 as u16; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 4a0c8620dc1..0d2115548fd 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -12,12 +12,19 @@ fn main() { #[rustfmt::skip] let v = vec!(1); &v as &[i32]; - 1.0 as f64; - 1 as u64; 0x10 as f32; 0o10 as f32; 0b10 as f32; 0x11 as f64; 0o11 as f64; 0b11 as f64; + + 1 as u32; + 0x10 as i32; + 0b10 as usize; + + 1.0 as f64; + 0.5 as f32; + + 1.0 as u16; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 8ff1e5dea60..474e62c30d5 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -18,5 +18,35 @@ error: casting integer literal to `f64` is unnecessary LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` -error: aborting due to 3 previous errors +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:22:5 + | +LL | 1 as u32; + | ^^^^^^^^ help: try: `1_u32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:23:5 + | +LL | 0x10 as i32; + | ^^^^^^^^^^^ help: try: `16_i32` + +error: casting integer literal to `usize` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:24:5 + | +LL | 0b10 as usize; + | ^^^^^^^^^^^^^ help: try: `2_usize` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:26:5 + | +LL | 1.0 as f64; + | ^^^^^^^^^^ help: try: `1.0_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:27:5 + | +LL | 0.5 as f32; + | ^^^^^^^^^^ help: try: `0.5_f32` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 915ce3608724e6c900d1b5eb4412cac2fcace33a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 18 Oct 2020 01:11:59 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index ddb8cc25077..f3f1e31abde 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,7 +2,7 @@ use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -10,7 +10,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that reimplement `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -43,32 +43,50 @@ impl LateLintPass<'_> for ManualUnwrapOr { if in_external_macro(cx.sess(), expr.span) { return; } - lint_option_unwrap_or_case(cx, expr); + lint_manual_unwrap_or(cx, expr); } } -fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { +#[derive(Copy, Clone)] +enum Case { + Option, + Result, +} + +impl Case { + fn unwrap_fn_path(&self) -> &str { + match self { + Case::Option => "Option::unwrap_or", + Case::Result => "Result::unwrap_or", + } + } +} + +fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if let PatKind::Path(ref qpath) = arm.pat.kind { - utils::match_qpath(qpath, &utils::paths::OPTION_NONE) - } else { - false + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| + match arm.pat.kind { + PatKind::Path(ref some_qpath) => + utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE), + PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => + utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR), + _ => false, } ); - let some_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; - if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); - if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; - if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + let unwrap_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; + if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME) + || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK); + if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = unwrap_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - if !utils::usage::contains_return_break_continue_macro(none_arm.body); + if !utils::usage::contains_return_break_continue_macro(or_arm.body); then { - Some(none_arm) + Some(or_arm) } else { None } @@ -78,24 +96,35 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) { + Some(Case::Option) + } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) { + Some(Case::Result) + } else { + None + }; + if let Some(or_arm) = applicable_or_arm(match_arms); if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); + if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let reindented_or_body = + utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); + let wrap_in_parens = !matches!(scrutinee, Expr { kind: ExprKind::Call(..), .. }); + let l_paren = if wrap_in_parens { "(" } else { "" }; + let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - "this pattern reimplements `Option::unwrap_or`", + &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), "replace with", format!( - "{}.unwrap_or({})", + "{}{}{}.unwrap_or({})", + l_paren, scrutinee_snippet, - reindented_none_body, + r_paren, + reindented_or_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..b930d9aedcf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a8736f1e6ef..ceb8985d3d5 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,7 +1,7 @@ // run-rustfix #![allow(dead_code)] -fn unwrap_or() { +fn option_unwrap_or() { // int case Some(1).unwrap_or(42); @@ -65,4 +65,46 @@ fn unwrap_or() { }; } +fn result_unwrap_or() { + // int case + (Ok(1) as Result).unwrap_or(42); + + // int case reversed + (Ok(1) as Result).unwrap_or(42); + + // richer none expr + (Ok(1) as Result).unwrap_or(1 + 42); + + // multiline case + #[rustfmt::skip] + (Ok(1) as Result).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + }); + + // string case + (Ok("Bob") as Result<&str, &str>).unwrap_or("Alice"); + + // don't lint + match Ok(1) as Result { + Ok(i) => i + 2, + Err(_) => 42, + }; + match Ok(1) as Result { + Ok(i) => i, + Err(_) => return, + }; + for j in 0..4 { + match Ok(j) as Result { + Ok(i) => i, + Err(_) => continue, + }; + match Ok(j) as Result { + Ok(i) => i, + Err(_) => break, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index bede8cffc32..beca1de0ed1 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,7 +1,7 @@ // run-rustfix #![allow(dead_code)] -fn unwrap_or() { +fn option_unwrap_or() { // int case match Some(1) { Some(i) => i, @@ -80,4 +80,61 @@ fn unwrap_or() { }; } +fn result_unwrap_or() { + // int case + match Ok(1) as Result { + Ok(i) => i, + Err(_) => 42, + }; + + // int case reversed + match Ok(1) as Result { + Err(_) => 42, + Ok(i) => i, + }; + + // richer none expr + match Ok(1) as Result { + Ok(i) => i, + Err(_) => 1 + 42, + }; + + // multiline case + #[rustfmt::skip] + match Ok(1) as Result { + Ok(i) => i, + Err(_) => { + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } + }; + + // string case + match Ok("Bob") as Result<&str, &str> { + Ok(i) => i, + Err(_) => "Alice", + }; + + // don't lint + match Ok(1) as Result { + Ok(i) => i + 2, + Err(_) => 42, + }; + match Ok(1) as Result { + Ok(i) => i, + Err(_) => return, + }; + for j in 0..4 { + match Ok(j) as Result { + Ok(i) => i, + Err(_) => continue, + }; + match Ok(j) as Result { + Ok(i) => i, + Err(_) => break, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 674f2952635..5d465666caf 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -57,5 +57,62 @@ LL | | None => "Alice", LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` -error: aborting due to 5 previous errors +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:85:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:91:5 + | +LL | / match Ok(1) as Result { +LL | | Err(_) => 42, +LL | | Ok(i) => i, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:97:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => 1 + 42, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(1 + 42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:104:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => { +LL | | 42 + 42 +... | +LL | | } +LL | | }; + | |_____^ + | +help: replace with + | +LL | (Ok(1) as Result).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 +LL | }); + | + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:114:5 + | +LL | / match Ok("Bob") as Result<&str, &str> { +LL | | Ok(i) => i, +LL | | Err(_) => "Alice", +LL | | }; + | |_____^ help: replace with: `(Ok("Bob") as Result<&str, &str>).unwrap_or("Alice")` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 114cb218f3ab2a709e3017c380790dd6e407132c Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 19 Oct 2020 10:34:01 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 1b73ced89b3..736730d4084 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -28,7 +28,6 @@ declare_clippy_lint! { /// /// ```rust /// # fn somefunc() -> bool { true }; - /// /// // Bad /// if { let x = somefunc(); x } { /* ... */ } /// diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8b022912573..1bf3b810fb5 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -29,7 +29,6 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// # fn foo(bar: usize) {} - /// /// // Bad /// let x = Box::new(1); /// foo(*x); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b1a4e06d4c3..d93433c607f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -36,7 +36,6 @@ declare_clippy_lint! { /// ```rust /// # fn bar(stool: &str) {} /// # let x = Some("abc"); - /// /// // Bad /// match x { /// Some(ref foo) => bar(foo), @@ -239,7 +238,6 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); - /// /// // Bad /// match x { /// Foo::A(_) => {}, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 9cb1cfb915d..5bc45c87874 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -231,7 +231,6 @@ declare_clippy_lint! { /// ```rust /// # struct TupleStruct(u32, u32, u32); /// # let t = TupleStruct(1, 2, 3); - /// /// // Bad /// match t { /// TupleStruct(0, .., _) => (), -- cgit 1.4.1-3-g733a5 From ec23db9496807f6c962b74fe0d6bf15be6c6d35b Mon Sep 17 00:00:00 2001 From: Patrick José Pereira Date: Sat, 3 Oct 2020 15:46:28 -0300 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..1bf25bcd0f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1936,6 +1936,7 @@ Released 2018-09-13 [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..84ea2d5ca78 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -633,6 +633,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, &loops::SAME_ITEM_PUSH, + &loops::SINGLE_ELEMENT_LOOP, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, @@ -1363,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::SAME_ITEM_PUSH), + LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1664,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), + LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 63d7e3176b1..e50aa9ac15a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, + indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, + match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -452,6 +453,31 @@ declare_clippy_lint! { "the same item is pushed inside of a for loop" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop has a single element. + /// + /// **Why is this bad?** There is no reason to have a loop of a + /// single element. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// for item in &[item1] { + /// println!("{}", item); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item = &item1; + /// println!("{}", item); + /// ``` + pub SINGLE_ELEMENT_LOOP, + complexity, + "there is no reason to have a single element loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -469,6 +495,7 @@ declare_lint_pass!(Loops => [ MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, SAME_ITEM_PUSH, + SINGLE_ELEMENT_LOOP, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -777,6 +804,7 @@ fn check_for_loop<'tcx>( check_for_loop_arg(cx, pat, arg, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); + check_for_single_element_loop(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1866,6 +1894,43 @@ fn check_for_loop_over_map_kv<'tcx>( } } +fn check_for_single_element_loop<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; + if let PatKind::Binding(.., target, _) = pat.kind; + if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind; + if let [arg_expression] = arg_expr_list; + if let ExprKind::Path(ref list_item) = arg_expression.kind; + if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); + if let ExprKind::Block(ref block, _) = body.kind; + if !block.stmts.is_empty(); + + then { + let for_span = get_span_of_entire_for_loop(expr); + let mut block_str = snippet(cx, block.span, "..").into_owned(); + block_str.remove(0); + block_str.pop(); + + + span_lint_and_sugg( + cx, + SINGLE_ELEMENT_LOOP, + for_span, + "for loop over a single element", + "try", + format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str), + Applicability::MachineApplicable + ) + } + } +} + struct MutatePairDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, hir_id_low: Option, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..70369dc582a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2125,6 +2125,13 @@ vec![ deprecation: None, module: "single_component_path_imports", }, + Lint { + name: "single_element_loop", + group: "complexity", + desc: "there is no reason to have a single element loop", + deprecation: None, + module: "loops", + }, Lint { name: "single_match", group: "style", diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed new file mode 100644 index 00000000000..8ca068293a6 --- /dev/null +++ b/tests/ui/single_element_loop.fixed @@ -0,0 +1,11 @@ +// run-rustfix +// Tests from for_loop.rs that don't have suggestions + +#[warn(clippy::single_element_loop)] +fn main() { + let item1 = 2; + { + let item = &item1; + println!("{}", item); + } +} diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs new file mode 100644 index 00000000000..57e9336a31f --- /dev/null +++ b/tests/ui/single_element_loop.rs @@ -0,0 +1,10 @@ +// run-rustfix +// Tests from for_loop.rs that don't have suggestions + +#[warn(clippy::single_element_loop)] +fn main() { + let item1 = 2; + for item in &[item1] { + println!("{}", item); + } +} diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr new file mode 100644 index 00000000000..90be1dc3283 --- /dev/null +++ b/tests/ui/single_element_loop.stderr @@ -0,0 +1,19 @@ +error: for loop over a single element + --> $DIR/single_element_loop.rs:7:5 + | +LL | / for item in &[item1] { +LL | | println!("{}", item); +LL | | } + | |_____^ + | + = note: `-D clippy::single-element-loop` implied by `-D warnings` +help: try + | +LL | { +LL | let item = &item1; +LL | println!("{}", item); +LL | } + | + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 65b52d84f83586752bff2834410e131290dc0155 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 20 Oct 2020 00:38:13 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d7043e7bd8f..c8a5a9c9431 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -414,7 +414,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs walk_ty(&mut visitor, &pred.bounded_ty); - if !visitor.lts.is_empty() { + if !visitor.all_lts().is_empty() { return true; } // if the bounds define new lifetimes, they are fine to occur @@ -424,7 +424,9 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); + if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { + return true; + } }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index d482d466e44..6001ef37eb7 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -357,4 +357,15 @@ mod nested_elision_sites { } } +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 57bf80f77626b134faaf2cd95664403627fba0da Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 15:53:36 -0400 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..9a75911acbe --- /dev/null +++ b/clippy_lints/src/await_holding_refcell_ref.rs @@ -0,0 +1,83 @@ +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..69719986104 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,6 +161,7 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; +mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -510,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -906,6 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1189,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 5e769c690a6..cd9b92efe58 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -93,6 +93,8 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..6e330f47dfc --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,71 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let m = RefCell::new(100); + good(&m); + bad(&m); + bad_mut(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 00000000000..b114945504a --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,77 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:46:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:46:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:58:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:58:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 3ed69cdb13e5953467f9d849d7ad480479ca01d6 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 12:39:20 -0400 Subject: Move existing lint into shared file --- clippy_lints/src/await_holding_invalid.rs | 92 +++++++++++++++++++++++++++++++ clippy_lints/src/await_holding_lock.rs | 92 ------------------------------- clippy_lints/src/lib.rs | 8 +-- src/lintlist/mod.rs | 2 +- tests/ui/await_holding_invalid.rs | 65 ++++++++++++++++++++++ tests/ui/await_holding_invalid.stderr | 63 +++++++++++++++++++++ tests/ui/await_holding_lock.rs | 65 ---------------------- tests/ui/await_holding_lock.stderr | 63 --------------------- 8 files changed, 225 insertions(+), 225 deletions(-) create mode 100644 clippy_lints/src/await_holding_invalid.rs delete mode 100644 clippy_lints/src/await_holding_lock.rs create mode 100644 tests/ui/await_holding_invalid.rs create mode 100644 tests/ui/await_holding_invalid.stderr delete mode 100644 tests/ui/await_holding_lock.rs delete mode 100644 tests/ui/await_holding_lock.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs new file mode 100644 index 00000000000..367534499fd --- /dev/null +++ b/clippy_lints/src/await_holding_invalid.rs @@ -0,0 +1,92 @@ +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 + /// non-async-aware MutexGuard. + /// + /// **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 + /// type. Many asynchronous foundation crates provide such a Mutex type. The + /// other solution is to ensure the mutex is unlocked before calling await, + /// either by introducing a scope or an explicit call to Drop::drop. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_LOCK, + pedantic, + "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(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_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) +} diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs deleted file mode 100644 index 367534499fd..00000000000 --- a/clippy_lints/src/await_holding_lock.rs +++ /dev/null @@ -1,92 +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 - /// non-async-aware MutexGuard. - /// - /// **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 - /// type. Many asynchronous foundation crates provide such a Mutex type. The - /// other solution is to ensure the mutex is unlocked before calling await, - /// either by introducing a scope or an explicit call to Drop::drop. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust,ignore - /// use std::sync::Mutex; - /// - /// async fn foo(x: &Mutex) { - /// let guard = x.lock().unwrap(); - /// *guard += 1; - /// bar.await; - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// use std::sync::Mutex; - /// - /// async fn foo(x: &Mutex) { - /// { - /// let guard = x.lock().unwrap(); - /// *guard += 1; - /// } - /// bar.await; - /// } - /// ``` - pub AWAIT_HOLDING_LOCK, - pedantic, - "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(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_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) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 69719986104..47cc0c90322 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -160,7 +160,7 @@ mod assign_ops; mod async_yields_async; mod atomic_ordering; mod attrs; -mod await_holding_lock; +mod await_holding_invalid; mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; @@ -510,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::MISMATCHED_TARGET_OS, &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, - &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_invalid::AWAIT_HOLDING_LOCK, &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, @@ -907,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -1191,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 21185a08d5c..63e9220ccd5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -65,7 +65,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, - module: "await_holding_lock", + module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs new file mode 100644 index 00000000000..0458950edee --- /dev/null +++ b/tests/ui/await_holding_invalid.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_invalid.stderr b/tests/ui/await_holding_invalid.stderr new file mode 100644 index 00000000000..315d5731b96 --- /dev/null +++ b/tests/ui/await_holding_invalid.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_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_invalid.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_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_invalid.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_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_invalid.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_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_invalid.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_lock.rs b/tests/ui/await_holding_lock.rs deleted file mode 100644 index 0458950edee..00000000000 --- a/tests/ui/await_holding_lock.rs +++ /dev/null @@ -1,65 +0,0 @@ -// 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 deleted file mode 100644 index 21bf49d16f0..00000000000 --- a/tests/ui/await_holding_lock.stderr +++ /dev/null @@ -1,63 +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_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 - -- cgit 1.4.1-3-g733a5 From ee20ebadafc9b2e4995015097e376c0a866d84af Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:15:45 -0400 Subject: 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 | 104 +++++++++++++++++++++--- 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, 276 insertions(+), 292 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 367534499fd..2000bdae363 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -60,12 +60,12 @@ impl LateLintPass<'_> for AwaitHoldingLock { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { if is_mutex_guard(cx, adt.did) { @@ -90,3 +90,79 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) } + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs deleted file mode 100644 index 9a75911acbe..00000000000 --- a/clippy_lints/src/await_holding_refcell_ref.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; -use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for calls to await while holding a - /// `RefCell` `Ref` or `RefMut`. - /// - /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access - /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point - /// risks panics from a mutable ref shared while other refs are outstanding. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// let b = x.borrow_mut()(); - /// *ref += 1; - /// bar.await; - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// { - /// let b = x.borrow_mut(); - /// *ref += 1; - /// } - /// bar.await; - /// } - /// ``` - pub AWAIT_HOLDING_REFCELL_REF, - pedantic, - "Inside an async function, holding a RefCell ref while calling await" -} - -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); - -impl LateLintPass<'_> for AwaitHoldingRefCellRef { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_refcell_ref(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this ref is held through", - ); - } - } - } -} - -fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 47cc0c90322..0c5baf96970 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,7 +161,6 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_invalid; -mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -511,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_invalid::AWAIT_HOLDING_LOCK, - &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, + &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1192,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 63e9220ccd5..c955f37b83a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -72,7 +72,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, - module: "await_holding_refcell_ref", + module: "await_holding_invalid", }, Lint { name: "bad_bit_mask", diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs index 0458950edee..45aa3e64292 100644 --- a/tests/ui/await_holding_invalid.rs +++ b/tests/ui/await_holding_invalid.rs @@ -1,14 +1,15 @@ // edition:2018 -#![warn(clippy::await_holding_lock)] +#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] +use std::cell::RefCell; use std::sync::Mutex; -async fn bad(x: &Mutex) -> u32 { +async fn bad_lock(x: &Mutex) -> u32 { let guard = x.lock().unwrap(); baz().await } -async fn good(x: &Mutex) -> u32 { +async fn good_lock(x: &Mutex) -> u32 { { let guard = x.lock().unwrap(); let y = *guard + 1; @@ -22,7 +23,7 @@ async fn baz() -> u32 { 42 } -async fn also_bad(x: &Mutex) -> u32 { +async fn also_bad_lock(x: &Mutex) -> u32 { let first = baz().await; let guard = x.lock().unwrap(); @@ -34,7 +35,7 @@ async fn also_bad(x: &Mutex) -> u32 { first + second + third } -async fn not_good(x: &Mutex) -> u32 { +async fn not_good_lock(x: &Mutex) -> u32 { let first = baz().await; let second = { @@ -48,18 +49,97 @@ async fn not_good(x: &Mutex) -> u32 { } #[allow(clippy::manual_async_fn)] -fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { +fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); baz().await } } +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(&m); - bad(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); + { + let m = Mutex::new(100); + good_lock(&m); + bad_lock(&m); + also_bad_lock(&m); + not_good_lock(&m); + block_bad_lock(&m); + } + { + let rc = RefCell::new(100); + good_refcell(&rc); + bad_refcell(&rc); + bad_mut_refcell(&rc); + also_bad_refcell(&rc); + less_bad_refcell(&rc); + not_good_refcell(&rc); + block_bad_refcell(&rc); + } } diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr index 315d5731b96..c8d49820c02 100644 --- a/tests/ui/await_holding_invalid.stderr +++ b/tests/ui/await_holding_invalid.stderr @@ -1,12 +1,12 @@ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:7:9 + --> $DIR/await_holding_invalid.rs:8:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = note: `-D clippy::await-holding-lock` implied by `-D warnings` note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:7:5 + --> $DIR/await_holding_invalid.rs:8:5 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -14,13 +14,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:28:9 + --> $DIR/await_holding_invalid.rs:29:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:28:5 + --> $DIR/await_holding_invalid.rs:29:5 | LL | / let guard = x.lock().unwrap(); LL | | @@ -32,13 +32,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:41:13 + --> $DIR/await_holding_invalid.rs:42:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:41:9 + --> $DIR/await_holding_invalid.rs:42:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -46,18 +46,111 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:53:13 + --> $DIR/await_holding_invalid.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:53:9 + --> $DIR/await_holding_invalid.rs:54:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 4 previous errors +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:60:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:60:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:65:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:65:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:82:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:82:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:94:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:94:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:109:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:109:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:121:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:121:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs deleted file mode 100644 index 88841597bb6..00000000000 --- a/tests/ui/await_holding_refcell_ref.rs +++ /dev/null @@ -1,86 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; - -async fn bad(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - let rc = RefCell::new(100); - good(&rc); - bad(&rc); - bad_mut(&rc); - also_bad(&rc); - less_bad(&rc); - not_good(&rc); - block_bad(&rc); -} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr deleted file mode 100644 index b504f045491..00000000000 --- a/tests/ui/await_holding_refcell_ref.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:7:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:7:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:12:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:12:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:33:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:33:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:45:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:45:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:60:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:60:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:72:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:72:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 6 previous errors - -- cgit 1.4.1-3-g733a5 From d8c6bce4407b1c99ed961f75a093ffe767818069 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:20:01 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 2000bdae363..1d201ff095b 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_LOCK, - pedantic, + correctness, "Inside an async function, holding a MutexGuard while calling await" } @@ -126,7 +126,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_REFCELL_REF, - pedantic, + correctness, "Inside an async function, holding a RefCell ref while calling await" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c5baf96970..c627df20976 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1190,8 +1190,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), @@ -1290,6 +1288,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), @@ -1736,6 +1736,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&booleans::LOGIC_BUG), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c955f37b83a..d9e019e2b61 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -62,14 +62,14 @@ vec![ }, Lint { name: "await_holding_lock", - group: "pedantic", + group: "correctness", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", - group: "pedantic", + group: "correctness", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, module: "await_holding_invalid", -- cgit 1.4.1-3-g733a5 From 86f2b29d2ff33862264e2e6dfdc7cc20ad054ad1 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 19 Oct 2020 12:06:43 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 1d201ff095b..fcebb54c6c2 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -49,48 +49,6 @@ declare_clippy_lint! { "Inside an async function, holding a MutexGuard while calling await" } -declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); - -impl LateLintPass<'_> for AwaitHoldingLock { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this lock is held through", - ); - } - } - } -} - -fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) -} - declare_clippy_lint! { /// **What it does:** Checks for calls to await while holding a /// `RefCell` `Ref` or `RefMut`. @@ -130,9 +88,9 @@ declare_clippy_lint! { "Inside an async function, holding a RefCell ref while calling await" } -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); +declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); -impl LateLintPass<'_> for AwaitHoldingRefCellRef { +impl LateLintPass<'_> for AwaitHolding { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -141,14 +99,24 @@ impl LateLintPass<'_> for AwaitHoldingRefCellRef { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this lock is held through", + ); + } if is_refcell_ref(cx, adt.did) { span_lint_and_note( cx, @@ -163,6 +131,15 @@ fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInte } } +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) +} + fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c627df20976..6d00e313ce1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -906,8 +906,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); -- cgit 1.4.1-3-g733a5 From 4a4f998c39b1b7e06c34a5fc8ed90e8752142e20 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Fri, 16 Oct 2020 11:12:37 -0400 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..697e6166fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1979,6 +1979,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..49ff8ad366e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -314,6 +314,7 @@ mod transmuting_null; mod trivially_copy_pass_by_ref; mod try_err; mod types; +mod undropped_manually_drops; mod unicode; mod unit_return_expecting_ord; mod unnamed_address; @@ -862,6 +863,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, @@ -1137,6 +1139,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1790,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), + LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs new file mode 100644 index 00000000000..48a050777b7 --- /dev/null +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -0,0 +1,49 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; +use crate::utils::{match_function_call, is_type_lang_item, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. + /// + /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// unsafe { + /// std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + /// } + /// ``` + pub UNDROPPED_MANUALLY_DROPS, + correctness, + "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" +} + +declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); + +impl LateLintPass<'tcx> for UndroppedManuallyDrops { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) { + let ty = cx.typeck_results().expr_ty(&args[0]); + if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { + span_lint_and_help( + cx, + UNDROPPED_MANUALLY_DROPS, + expr.span, + "the inner value of this ManuallyDrop will not be dropped", + None, + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop" + ); + } + } + } +} \ No newline at end of file diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..25a69e78c8f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2412,6 +2412,13 @@ vec![ deprecation: None, module: "trait_bounds", }, + Lint { + name: "undropped_manually_drops", + group: "correctness", + desc: "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value", + deprecation: None, + module: "undropped_manually_drops", + }, Lint { name: "unicode_not_nfc", group: "pedantic", diff --git a/tests/ui/undropped_manually_drops.rs b/tests/ui/undropped_manually_drops.rs new file mode 100644 index 00000000000..bea62e1751e --- /dev/null +++ b/tests/ui/undropped_manually_drops.rs @@ -0,0 +1,18 @@ +#![warn(clippy::undropped_manually_drops)] + +struct S; + +fn main() { + let f = drop; + let manual = std::mem::ManuallyDrop::new(S); + + // These lines will not drop `S` + drop(std::mem::ManuallyDrop::new(S)); + f(manual); + + // These lines will + unsafe { + std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(manual); + } +} -- cgit 1.4.1-3-g733a5 From e70817e712fd4d4e930ead0d587031e2b4a97a2e Mon Sep 17 00:00:00 2001 From: cgm616 Date: Fri, 16 Oct 2020 16:20:03 -0400 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 49ff8ad366e..97e7cfd1bb2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1524,6 +1524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), + LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 48a050777b7..5443f1601fc 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -1,14 +1,15 @@ -use rustc_lint::{LateLintPass, LateContext}; +use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help}; +use rustc_hir::{lang_items, Expr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; -use crate::utils::{match_function_call, is_type_lang_item, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. /// /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`. /// - /// **Known problems:** None. + /// **Known problems:** Does not catch cases if the user binds `std::mem::drop` + /// to a different name and calls it that way. /// /// **Example:** /// @@ -20,7 +21,7 @@ declare_clippy_lint! { /// ```rust /// struct S; /// unsafe { - /// std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); /// } /// ``` pub UNDROPPED_MANUALLY_DROPS, @@ -41,9 +42,9 @@ impl LateLintPass<'tcx> for UndroppedManuallyDrops { expr.span, "the inner value of this ManuallyDrop will not be dropped", None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop" + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", ); } } } -} \ No newline at end of file +} diff --git a/tests/ui/undropped_manually_drops.rs b/tests/ui/undropped_manually_drops.rs index bea62e1751e..f4cfc92e1cd 100644 --- a/tests/ui/undropped_manually_drops.rs +++ b/tests/ui/undropped_manually_drops.rs @@ -3,16 +3,24 @@ struct S; fn main() { - let f = drop; - let manual = std::mem::ManuallyDrop::new(S); + let f = std::mem::drop; + let g = std::mem::ManuallyDrop::drop; + let mut manual1 = std::mem::ManuallyDrop::new(S); + let mut manual2 = std::mem::ManuallyDrop::new(S); + let mut manual3 = std::mem::ManuallyDrop::new(S); + let mut manual4 = std::mem::ManuallyDrop::new(S); - // These lines will not drop `S` + // These lines will not drop `S` and should be linted drop(std::mem::ManuallyDrop::new(S)); - f(manual); + drop(manual1); - // These lines will + // FIXME: this line is not linted, though it should be + f(manual2); + + // These lines will drop `S` and should be okay. unsafe { - std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); - std::mem::ManuallyDrop::drop(manual); + std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(&mut manual3); + g(&mut manual4); } } diff --git a/tests/ui/undropped_manually_drops.stderr b/tests/ui/undropped_manually_drops.stderr new file mode 100644 index 00000000000..2ac0fe98697 --- /dev/null +++ b/tests/ui/undropped_manually_drops.stderr @@ -0,0 +1,19 @@ +error: the inner value of this ManuallyDrop will not be dropped + --> $DIR/undropped_manually_drops.rs:14:5 + | +LL | drop(std::mem::ManuallyDrop::new(S)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::undropped-manually-drops` implied by `-D warnings` + = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop + +error: the inner value of this ManuallyDrop will not be dropped + --> $DIR/undropped_manually_drops.rs:15:5 + | +LL | drop(manual1); + | ^^^^^^^^^^^^^ + | + = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From c693de350aff4a3930e66bccf65caf79b390dca2 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 15 Oct 2020 22:05:51 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..c9f0406a806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1793,6 +1793,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..cc50655cb00 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -785,6 +785,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ptr_eq::PTR_EQ, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &question_mark::QUESTION_MARK, + &ranges::MANUAL_RANGE_CONTAINS, &ranges::RANGE_MINUS_ONE, &ranges::RANGE_PLUS_ONE, &ranges::RANGE_ZIP_WITH_LEN, @@ -1469,6 +1470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), + LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), @@ -1624,6 +1626,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::PTR_ARG), LintId::of(&ptr_eq::PTR_EQ), LintId::of(&question_mark::QUESTION_MARK), + LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index cc492917b9d..de54711d851 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,15 +2,19 @@ use crate::consts::{constant, Constant}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::Ident; use std::cmp::Ordering; use crate::utils::sugg::Sugg; -use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; +use crate::utils::{ + get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, + span_lint, span_lint_and_sugg, span_lint_and_then, +}; use crate::utils::{higher, SpanlessEq}; declare_clippy_lint! { @@ -128,43 +132,51 @@ declare_clippy_lint! { "reversing the limits of range expressions, resulting in empty ranges" } +declare_clippy_lint! { + /// **What it does:** Checks for expressions like `x >= 3 && x < 8` that could + /// be more readably expressed as `(3..8).contains(x)`. + /// + /// **Why is this bad?** `contains` expresses the intent better and has less + /// failure modes (such as fencepost errors or using `||` instead of `&&`). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // given + /// let x = 6; + /// + /// assert!(x >= 3 && x < 8); + /// ``` + /// Use instead: + /// ```rust + ///# let x = 6; + /// assert!((3..8).contains(&x)); + /// ``` + pub MANUAL_RANGE_CONTAINS, + style, + "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" +} + declare_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, REVERSED_EMPTY_RANGES, + MANUAL_RANGE_CONTAINS, ]); impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - let name = path.ident.as_str(); - if name == "zip" && args.len() == 2 { - let iter = &args[0].kind; - let zip_arg = &args[1]; - if_chain! { - // `.iter()` call - if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter; - if iter_path.ident.name == sym!(iter); - // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); - if is_integer_const(cx, start, 0); - // `.len()` call - if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; - if len_path.ident.name == sym!(len) && len_args.len() == 1; - // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; - if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - expr.span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, iter_args[0].span, "_"))); - } - } - } + match expr.kind { + ExprKind::MethodCall(ref path, _, ref args, _) => { + check_range_zip_with_len(cx, path, args, expr.span); + }, + ExprKind::Binary(ref op, ref l, ref r) => { + check_possible_range_contains(cx, op.node, l, r, expr.span); + }, + _ => {}, } check_exclusive_range_plus_one(cx, expr); @@ -173,6 +185,148 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { } } +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { + let combine_and = match op { + BinOpKind::And | BinOpKind::BitAnd => true, + BinOpKind::Or | BinOpKind::BitOr => false, + _ => return, + }; + // value, name, order (higher/lower), inclusiveness + if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) = + (check_range_bounds(cx, l), check_range_bounds(cx, r)) + { + // we only lint comparisons on the same name and with different + // direction + if lname != rname || lord == rord { + return; + } + let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); + if combine_and && ord == Some(rord) { + // order lower bound and upper bound + let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less { + (lval_span, rval_span, linc, rinc) + } else { + (rval_span, lval_span, rinc, linc) + }; + // we only lint inclusive lower bounds + if !l_inc { + return; + } + let (range_type, range_op) = if u_inc { + ("RangeInclusive", "..=") + } else { + ("Range", "..") + }; + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); + let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_RANGE_CONTAINS, + span, + &format!("manual `{}::contains` implementation", range_type), + "use", + format!("({}{}{}).contains(&{})", lo, range_op, hi, name), + applicability, + ); + } else if !combine_and && ord == Some(lord) { + // `!_.contains(_)` + // order lower bound and upper bound + let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less { + (lval_span, rval_span, linc, rinc) + } else { + (rval_span, lval_span, rinc, linc) + }; + if l_inc { + return; + } + let (range_type, range_op) = if u_inc { + ("Range", "..") + } else { + ("RangeInclusive", "..=") + }; + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); + let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_RANGE_CONTAINS, + span, + &format!("manual `!{}::contains` implementation", range_type), + "use", + format!("!({}{}{}).contains(&{})", lo, range_op, hi, name), + applicability, + ); + } + } +} + +fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> { + if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind { + let (inclusive, ordering) = match op.node { + BinOpKind::Gt => (false, Ordering::Greater), + BinOpKind::Ge => (true, Ordering::Greater), + BinOpKind::Lt => (false, Ordering::Less), + BinOpKind::Le => (true, Ordering::Less), + _ => return None, + }; + if let Some(id) = match_ident(l) { + if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { + return Some((c, id, l.span, r.span, ordering, inclusive)); + } + } else if let Some(id) = match_ident(r) { + if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { + return Some((c, id, r.span, l.span, ordering.reverse(), inclusive)); + } + } + } + None +} + +fn match_ident(e: &Expr<'_>) -> Option { + if let ExprKind::Path(ref qpath) = e.kind { + if let Some(seg) = single_segment_path(qpath) { + if seg.args.is_none() { + return Some(seg.ident); + } + } + } + None +} + +fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) { + let name = path.ident.as_str(); + if name == "zip" && args.len() == 2 { + let iter = &args[0].kind; + let zip_arg = &args[1]; + if_chain! { + // `.iter()` call + if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; + if iter_path.ident.name == sym!(iter); + // range expression in `.zip()` call: `0..x.len()` + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); + if is_integer_const(cx, start, 0); + // `.len()` call + if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; + if len_path.ident.name == sym!(len) && len_args.len() == 1; + // `.iter()` and `.len()` called on same `Path` + if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; + if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + then { + span_lint(cx, + RANGE_ZIP_WITH_LEN, + span, + &format!("it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, iter_args[0].span, "_")) + ); + } + } + } +} + // exclusive range plus one: `x..(y+1)` fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..7bb68acc062 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1159,6 +1159,13 @@ vec![ deprecation: None, module: "manual_non_exhaustive", }, + Lint { + name: "manual_range_contains", + group: "style", + desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`", + deprecation: None, + module: "ranges", + }, Lint { name: "manual_saturating_arithmetic", group: "style", diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed new file mode 100644 index 00000000000..632a6592a28 --- /dev/null +++ b/tests/ui/range_contains.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#[warn(clippy::manual_range_contains)] +#[allow(unused)] +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +fn main() { + let x = 9_u32; + + // order shouldn't matter + (8..12).contains(&x); + (21..42).contains(&x); + (1..100).contains(&x); + + // also with inclusive ranges + (9..=99).contains(&x); + (1..=33).contains(&x); + (1..=999).contains(&x); + + // and the outside + !(8..12).contains(&x); + !(21..42).contains(&x); + !(1..100).contains(&x); + + // also with the outside of inclusive ranges + !(9..=99).contains(&x); + !(1..=33).contains(&x); + !(1..=999).contains(&x); + + // not a range.contains + x > 8 && x < 12; // lower bound not inclusive + x < 8 && x <= 12; // same direction + x >= 12 && 12 >= x; // same bounds + x < 8 && x > 12; // wrong direction + + x <= 8 || x >= 12; + x >= 8 || x >= 12; + x < 12 || 12 < x; + x >= 8 || x <= 12; +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs new file mode 100644 index 00000000000..6af0d034ef6 --- /dev/null +++ b/tests/ui/range_contains.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#[warn(clippy::manual_range_contains)] +#[allow(unused)] +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +fn main() { + let x = 9_u32; + + // order shouldn't matter + x >= 8 && x < 12; + x < 42 && x >= 21; + 100 > x && 1 <= x; + + // also with inclusive ranges + x >= 9 && x <= 99; + x <= 33 && x >= 1; + 999 >= x && 1 <= x; + + // and the outside + x < 8 || x >= 12; + x >= 42 || x < 21; + 100 <= x || 1 > x; + + // also with the outside of inclusive ranges + x < 9 || x > 99; + x > 33 || x < 1; + 999 < x || 1 > x; + + // not a range.contains + x > 8 && x < 12; // lower bound not inclusive + x < 8 && x <= 12; // same direction + x >= 12 && 12 >= x; // same bounds + x < 8 && x > 12; // wrong direction + + x <= 8 || x >= 12; + x >= 8 || x >= 12; + x < 12 || 12 < x; + x >= 8 || x <= 12; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr new file mode 100644 index 00000000000..69b009eafc3 --- /dev/null +++ b/tests/ui/range_contains.stderr @@ -0,0 +1,76 @@ +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:12:5 + | +LL | x >= 8 && x < 12; + | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` + | + = note: `-D clippy::manual-range-contains` implied by `-D warnings` + +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:13:5 + | +LL | x < 42 && x >= 21; + | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` + +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:14:5 + | +LL | 100 > x && 1 <= x; + | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:17:5 + | +LL | x >= 9 && x <= 99; + | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:18:5 + | +LL | x <= 33 && x >= 1; + | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:19:5 + | +LL | 999 >= x && 1 <= x; + | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:22:5 + | +LL | x < 8 || x >= 12; + | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:23:5 + | +LL | x >= 42 || x < 21; + | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:24:5 + | +LL | 100 <= x || 1 > x; + | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:27:5 + | +LL | x < 9 || x > 99; + | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:28:5 + | +LL | x > 33 || x < 1; + | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:29:5 + | +LL | 999 < x || 1 > x; + | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` + +error: aborting due to 12 previous errors + -- cgit 1.4.1-3-g733a5 From 6533d8becfd198299d0bd38550dd6c574cbd194f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 22 Oct 2020 23:39:59 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index f3f1e31abde..cc9ee28d027 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -111,7 +111,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let wrap_in_parens = !matches!(scrutinee, Expr { kind: ExprKind::Call(..), .. }); + let wrap_in_parens = !matches!(scrutinee, Expr { + kind: ExprKind::Call(..) | ExprKind::Path(_), .. + }); let l_paren = if wrap_in_parens { "(" } else { "" }; let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index c784de0f604..582f5c6f7a8 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -70,6 +70,10 @@ fn result_unwrap_or() { // int case Ok::(1).unwrap_or(42); + // int case, scrutinee is a binding + let a = Ok::(1); + a.unwrap_or(42); + // int case, suggestion must surround with parenthesis (Ok(1) as Result).unwrap_or(42); diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index df5f237c3fb..0e2b7ecadb3 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -88,6 +88,13 @@ fn result_unwrap_or() { Err(_) => 42, }; + // int case, scrutinee is a binding + let a = Ok::(1); + match a { + Ok(i) => i, + Err(_) => 42, + }; + // int case, suggestion must surround with parenthesis match Ok(1) as Result { Ok(i) => i, diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 5bc01bf4e68..6603ab43437 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -67,7 +67,16 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:92:5 + --> $DIR/manual_unwrap_or.rs:93:5 + | +LL | / match a { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `a.unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:99:5 | LL | / match Ok(1) as Result { LL | | Ok(i) => i, @@ -76,7 +85,7 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:98:5 + --> $DIR/manual_unwrap_or.rs:105:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -85,7 +94,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:104:5 + --> $DIR/manual_unwrap_or.rs:111:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -94,7 +103,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:111:5 + --> $DIR/manual_unwrap_or.rs:118:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -115,7 +124,7 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:121:5 + --> $DIR/manual_unwrap_or.rs:128:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -123,5 +132,5 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From e8f12d2f02644834282dec0c27710886f1e85ae6 Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Thu, 22 Oct 2020 23:53:50 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 716d027e434..f4bb648d15a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; -use std::fmt::Display; use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; @@ -12,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1225,7 +1224,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts to the same type. + /// **What it does:** Checks for casts to the same type, casts of int literals to integer types + /// and casts of float literals to float types. /// /// **Why is this bad?** It's just unnecessary. /// @@ -1234,6 +1234,7 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let _ = 2i32 as i32; + /// let _ = 0.5 as f32; /// ``` pub UNNECESSARY_CAST, complexity, @@ -1599,7 +1600,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if let ExprKind::Cast(ref ex, _) = expr.kind { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); - if let ExprKind::Lit(ref lit) = ex.kind { + if let Some(lit) = get_numeric_literal(ex) { + let literal_str = snippet_opt(cx, lit.span).unwrap_or_default(); + if_chain! { if let LitKind::Int(n, _) = lit.node; if let Some(src) = snippet_opt(cx, lit.span); @@ -1609,25 +1612,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - show_unnecessary_cast(cx, expr, n , cast_from, cast_to); + show_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); return; } } match lit.node { - LitKind::Int(num, LitIntType::Unsuffixed) if cast_to.is_integral() => { - show_unnecessary_cast(cx, expr, num, cast_from, cast_to); - return; + LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - LitKind::Float(num, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - show_unnecessary_cast(cx, expr, num, cast_from, cast_to); - return; + LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - _ => (), - }; - - match lit.node { - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => (), _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( @@ -1652,13 +1649,21 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } -fn show_unnecessary_cast( - cx: &LateContext<'_>, - expr: &Expr<'_>, - num: Num, - cast_from: Ty<'_>, - cast_to: Ty<'_>, -) { +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { + match expr.kind { + ExprKind::Lit(ref lit) => Some(lit), + ExprKind::Unary(UnOp::UnNeg, e) => { + if let ExprKind::Lit(ref lit) = e.kind { + Some(lit) + } else { + None + } + }, + _ => None, + } +} + +fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; span_lint_and_sugg( cx, @@ -1666,7 +1671,7 @@ fn show_unnecessary_cast( expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", num, cast_to), + format!("{}_{}", literal_str, cast_to), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index ba52fc2703f..54853f4b8a2 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -20,8 +20,10 @@ fn main() { 0b11 as f64; 1_u32; - 16_i32; - 2_usize; + 0x10_i32; + 0b10_usize; + 0o73_u16; + 1_000_000_000_u32; 1.0_f64; 0.5_f32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 0d2115548fd..8da3d947702 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -22,6 +22,8 @@ fn main() { 1 as u32; 0x10 as i32; 0b10 as usize; + 0o73 as u16; + 1_000_000_000 as u32; 1.0 as f64; 0.5 as f32; diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 474e62c30d5..28fb9540afc 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -28,25 +28,37 @@ error: casting integer literal to `i32` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:23:5 | LL | 0x10 as i32; - | ^^^^^^^^^^^ help: try: `16_i32` + | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:24:5 | LL | 0b10 as usize; - | ^^^^^^^^^^^^^ help: try: `2_usize` + | ^^^^^^^^^^^^^ help: try: `0b10_usize` -error: casting float literal to `f64` is unnecessary +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: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 -- cgit 1.4.1-3-g733a5 From 02f01104bfbb935ee1c3c3971ccf055173e4f82b Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 00:04:27 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f4bb648d15a..3a088709a7e 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1601,7 +1601,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); if let Some(lit) = get_numeric_literal(ex) { - let literal_str = snippet_opt(cx, lit.span).unwrap_or_default(); + let literal_str = snippet_opt(cx, ex.span).unwrap_or_default(); if_chain! { if let LitKind::Int(n, _) = lit.node; diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 54853f4b8a2..2a13469b146 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -29,4 +29,7 @@ fn main() { 0.5_f32; 1.0 as u16; + + -1_i32; + -1.0_f32; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 8da3d947702..65ddd3c7fbf 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -29,4 +29,7 @@ fn main() { 0.5 as f32; 1.0 as u16; + + -1 as i32; + -1.0 as f32; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 28fb9540afc..26b23e315e3 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -60,5 +60,17 @@ error: casting float literal to `f32` is unnecessary LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` -error: aborting due to 10 previous errors +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:33:5 + | +LL | -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:34:5 + | +LL | -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 3807634a470b572303d95feb8a5db273c7cea4af Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sun, 11 Oct 2020 16:04:33 -0700 Subject: 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 ++++++++--- tests/ui/empty_loop_no_std.rs | 22 ++++++++++++++++++++++ tests/ui/issue-3746.rs | 22 ---------------------- 5 files changed, 55 insertions(+), 32 deletions(-) create mode 100644 tests/ui/empty_loop_no_std.rs delete mode 100644 tests/ui/issue-3746.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 63d7e3176b1..bae12943869 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -293,9 +293,24 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for empty `loop` expressions. /// - /// **Why is this bad?** Those busy loops burn CPU cycles without doing - /// anything. Think of the environment and either block on something or at least - /// make the thread sleep for some microseconds. + /// **Why is this bad?** These busy loops burn CPU cycles without doing + /// anything. It is _almost always_ a better idea to `panic!` than to have + /// a busy loop. + /// + /// If panicking isn't possible, think of the environment and either: + /// - block on something + /// - sleep the thread for some microseconds + /// - yield or pause the thread + /// + /// For `std` targets, this can be done with + /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) + /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). + /// + /// For `no_std` targets, doing this is more complicated, especially because + /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will + /// probably need to invoke some target-specific intrinsic. Examples include: + /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) + /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) /// /// **Known problems:** None. /// @@ -502,13 +517,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // (even if the "match" or "if let" is used for declaration) if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { // also check for empty `loop {}` statements + // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler]) if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { - span_lint( + span_lint_and_help( cx, EMPTY_LOOP, expr.span, - "empty `loop {}` detected. You may want to either use `panic!()` or add \ - `std::thread::sleep(..);` to the loop body.", + "empty `loop {}` wastes CPU cycles", + None, + "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.", ); } diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index 84e31eaf2e9..bb03ce40355 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -12,13 +12,14 @@ LL | | } | = note: `-D clippy::while-let-loop` implied by `-D warnings` -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/ice-360.rs:10:9 | LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: aborting due to 2 previous errors diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index e44c58ea770..fd3979f259a 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -1,22 +1,27 @@ -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:9:5 | LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:11:9 | LL | loop {} | ^^^^^^^ + | + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:15:9 | LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ + | + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: aborting due to 3 previous errors diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs new file mode 100644 index 00000000000..879d1d5d916 --- /dev/null +++ b/tests/ui/empty_loop_no_std.rs @@ -0,0 +1,22 @@ +// ignore-macos +// ignore-windows + +#![warn(clippy::empty_loop)] +#![feature(lang_items, link_args, start, libc)] +#![link_args = "-nostartfiles"] +#![no_std] + +use core::panic::PanicInfo; + +#[start] +fn main(argc: isize, argv: *const *const u8) -> isize { + loop {} +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/issue-3746.rs b/tests/ui/issue-3746.rs deleted file mode 100644 index 879d1d5d916..00000000000 --- a/tests/ui/issue-3746.rs +++ /dev/null @@ -1,22 +0,0 @@ -// ignore-macos -// ignore-windows - -#![warn(clippy::empty_loop)] -#![feature(lang_items, link_args, start, libc)] -#![link_args = "-nostartfiles"] -#![no_std] - -use core::panic::PanicInfo; - -#[start] -fn main(argc: isize, argv: *const *const u8) -> isize { - loop {} -} - -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} -- cgit 1.4.1-3-g733a5 From d46edd99667ad342e6118b2216a0c24ee009e86c Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 23:40:57 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3a088709a7e..6a33aaaaab2 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1236,6 +1236,13 @@ declare_clippy_lint! { /// let _ = 2i32 as i32; /// let _ = 0.5 as f32; /// ``` + /// + /// Better: + /// + /// ```rust + /// let _ = 2_i32; + /// let _ = 0.5_f32; + /// ``` pub UNNECESSARY_CAST, complexity, "cast to the same type, e.g., `x as i32` where `x: i32`" @@ -1612,7 +1619,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - show_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() }; + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); return; } } @@ -1624,7 +1632,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => (), + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( @@ -1649,6 +1657,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } +fn is_unary_neg(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Unary(UnOp::UnNeg, _)) +} + fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { match expr.kind { ExprKind::Lit(ref lit) => Some(lit), diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 5aeb0340b26..350da4965d1 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -8,6 +8,9 @@ fn main() { 100_f32; 100_f64; 100_f64; + let _ = -100_f32; + let _ = -100_f64; + let _ = -100_f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 0f249c23055..ad2fb2e6289 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -8,6 +8,9 @@ fn main() { 100 as f32; 100 as f64; 100_i32 as f64; + let _ = -100 as f32; + let _ = -100 as f64; + let _ = -100_i32 as f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 5100e9798c4..5a210fc8909 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -18,59 +18,77 @@ error: casting integer literal to `f64` is unnecessary LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:11:13 + | +LL | let _ = -100 as f32; + | ^^^^^^^^^^^ help: try: `-100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:12:13 + | +LL | let _ = -100 as f64; + | ^^^^^^^^^^^ help: try: `-100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:13:13 + | +LL | let _ = -100_i32 as f64; + | ^^^^^^^^^^^^^^^ help: try: `-100_f64` + error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:22:5 + --> $DIR/unnecessary_cast_fixable.rs:25:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:23:5 + --> $DIR/unnecessary_cast_fixable.rs:26:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:24:5 + --> $DIR/unnecessary_cast_fixable.rs:27:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:25:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:31:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:32:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:13 + --> $DIR/unnecessary_cast_fixable.rs:36:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:13 + --> $DIR/unnecessary_cast_fixable.rs:37:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 62f60e1ae5bd2287497746bf90a302903e0ae462 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 24 Oct 2020 09:31:32 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index dc5aa669139..a799a644e97 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,9 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{ + higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -233,6 +235,9 @@ fn check_comparison<'a, 'tcx>( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), ); + if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() { + return; + } if l_ty.is_bool() && r_ty.is_bool() { let mut applicability = Applicability::MachineApplicable; @@ -295,7 +300,14 @@ fn suggest_bool_comparison<'a, 'tcx>( message: &str, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, ) { - let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability); + let hint = if expr.span.from_expansion() { + if applicability != Applicability::Unspecified { + applicability = Applicability::MaybeIncorrect; + } + Sugg::hir_with_macro_callsite(cx, expr, "..") + } else { + Sugg::hir_with_applicability(cx, expr, "..", &mut applicability) + }; span_lint_and_sugg( cx, BOOL_COMPARISON, diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index 91211764759..5a012ff4d27 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,6 +1,7 @@ // run-rustfix -#[warn(clippy::bool_comparison)] +#![warn(clippy::bool_comparison)] + fn main() { let x = true; if x { @@ -127,3 +128,40 @@ fn issue4983() { if b == a {}; if !b == !a {}; } + +macro_rules! m { + ($func:ident) => { + $func() + }; +} + +fn func() -> bool { + true +} + +#[allow(dead_code)] +fn issue3973() { + // ok, don't lint on `cfg` invocation + if false == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == false {} + if true == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == true {} + + // lint, could be simplified + if !m!(func) {} + if !m!(func) {} + if m!(func) {} + if m!(func) {} + + // no lint with a variable + let is_debug = false; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} + let is_debug = true; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} +} diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 01ee35859f0..c534bc25c20 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,6 +1,7 @@ // run-rustfix -#[warn(clippy::bool_comparison)] +#![warn(clippy::bool_comparison)] + fn main() { let x = true; if x == true { @@ -127,3 +128,40 @@ fn issue4983() { if b == a {}; if !b == !a {}; } + +macro_rules! m { + ($func:ident) => { + $func() + }; +} + +fn func() -> bool { + true +} + +#[allow(dead_code)] +fn issue3973() { + // ok, don't lint on `cfg` invocation + if false == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == false {} + if true == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == true {} + + // lint, could be simplified + if false == m!(func) {} + if m!(func) == false {} + if true == m!(func) {} + if m!(func) == true {} + + // no lint with a variable + let is_debug = false; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} + let is_debug = true; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} +} diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index 55d94b8257d..31522d4a525 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -1,5 +1,5 @@ error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:6:8 + --> $DIR/bool_comparison.rs:7:8 | LL | if x == true { | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -7,106 +7,130 @@ LL | if x == true { = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:11:8 + --> $DIR/bool_comparison.rs:12:8 | LL | if x == false { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:16:8 + --> $DIR/bool_comparison.rs:17:8 | LL | if true == x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:21:8 + --> $DIR/bool_comparison.rs:22:8 | LL | if false == x { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:26:8 + --> $DIR/bool_comparison.rs:27:8 | LL | if x != true { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:31:8 + --> $DIR/bool_comparison.rs:32:8 | LL | if x != false { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:36:8 + --> $DIR/bool_comparison.rs:37:8 | LL | if true != x { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:41:8 + --> $DIR/bool_comparison.rs:42:8 | LL | if false != x { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:46:8 + --> $DIR/bool_comparison.rs:47:8 | LL | if x < true { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:51:8 + --> $DIR/bool_comparison.rs:52:8 | LL | if false < x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:56:8 + --> $DIR/bool_comparison.rs:57:8 | LL | if x > false { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:61:8 + --> $DIR/bool_comparison.rs:62:8 | LL | if true > x { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:67:8 + --> $DIR/bool_comparison.rs:68:8 | LL | if x < y { | ^^^^^ help: try simplifying it as shown: `!x & y` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:72:8 + --> $DIR/bool_comparison.rs:73:8 | LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:120:8 + --> $DIR/bool_comparison.rs:121:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:121:8 + --> $DIR/bool_comparison.rs:122:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:125:8 + --> $DIR/bool_comparison.rs:126:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:126:8 + --> $DIR/bool_comparison.rs:127:8 | LL | if !b == a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: aborting due to 18 previous errors +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:151:8 + | +LL | if false == m!(func) {} + | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:152:8 + | +LL | if m!(func) == false {} + | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:153:8 + | +LL | if true == m!(func) {} + | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:154:8 + | +LL | if m!(func) == true {} + | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From 0d21ae0e194fd8f7f1f67bf1921910e0ca21a32c Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 24 Oct 2020 11:35:05 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index cc9ee28d027..22aa37e41fe 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,5 +1,6 @@ use crate::consts::constant_simple; use crate::utils; +use crate::utils::sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath}; @@ -104,28 +105,20 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { None }; if let Some(or_arm) = applicable_or_arm(match_arms); - if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { let reindented_or_body = utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let wrap_in_parens = !matches!(scrutinee, Expr { - kind: ExprKind::Call(..) | ExprKind::Path(_), .. - }); - let l_paren = if wrap_in_parens { "(" } else { "" }; - let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), "replace with", format!( - "{}{}{}.unwrap_or({})", - l_paren, - scrutinee_snippet, - r_paren, + "{}.unwrap_or({})", + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(), reindented_or_body, ), Applicability::MachineApplicable, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 582f5c6f7a8..5aa5a43cb92 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -74,9 +74,19 @@ fn result_unwrap_or() { let a = Ok::(1); a.unwrap_or(42); - // int case, suggestion must surround with parenthesis + // int case, suggestion must surround Result expr with parenthesis (Ok(1) as Result).unwrap_or(42); + // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + struct S {} + impl S { + fn method(self) -> Option { + Some(42) + } + } + let s = S {}; + s.method().unwrap_or(42); + // int case reversed Ok::(1).unwrap_or(42); diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 0e2b7ecadb3..df534031f54 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -95,12 +95,25 @@ fn result_unwrap_or() { Err(_) => 42, }; - // int case, suggestion must surround with parenthesis + // int case, suggestion must surround Result expr with parenthesis match Ok(1) as Result { Ok(i) => i, Err(_) => 42, }; + // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + struct S {} + impl S { + fn method(self) -> Option { + Some(42) + } + } + let s = S {}; + match s.method() { + Some(i) => i, + None => 42, + }; + // int case reversed match Ok::(1) { Err(_) => 42, diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 6603ab43437..fc174c4c270 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -84,8 +84,17 @@ LL | | Err(_) => 42, LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:112:5 + | +LL | / match s.method() { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `s.method().unwrap_or(42)` + error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:105:5 + --> $DIR/manual_unwrap_or.rs:118:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -94,7 +103,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:111:5 + --> $DIR/manual_unwrap_or.rs:124:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -103,7 +112,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:118:5 + --> $DIR/manual_unwrap_or.rs:131:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -124,7 +133,7 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:128:5 + --> $DIR/manual_unwrap_or.rs:141:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -132,5 +141,5 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From e8731a926c9a642ca1ddf5b52baf40e0a8873d53 Mon Sep 17 00:00:00 2001 From: Cauê Baasch de Souza Date: Thu, 8 Oct 2020 02:17:32 -0300 Subject: 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 +++++++++++++++++++++ clippy_lints/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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..22f96398153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1779,6 +1779,7 @@ Released 2018-09-13 [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..1a950a7c334 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -278,6 +278,7 @@ mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; mod partialeq_ne_impl; +mod pass_by_ref_or_value; mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; @@ -311,7 +312,6 @@ mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; -mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; @@ -776,6 +776,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &panic_unimplemented::UNIMPLEMENTED, &panic_unimplemented::UNREACHABLE, &partialeq_ne_impl::PARTIALEQ_NE_IMPL, + &pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, + &pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, &pattern_type_mismatch::PATTERN_TYPE_MISMATCH, &precedence::PRECEDENCE, @@ -835,7 +837,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &transmute::USELESS_TRANSMUTE, &transmute::WRONG_TRANSMUTE, &transmuting_null::TRANSMUTING_NULL, - &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF, &try_err::TRY_ERR, &types::ABSURD_EXTREME_COMPARISONS, &types::BORROWED_BOX, @@ -1009,11 +1010,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)); store.register_late_pass(|| box explicit_write::ExplicitWrite); store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue); - let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new( + let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( conf.trivial_copy_size_limit, + conf.pass_by_value_size_limit, &sess.target, ); - store.register_late_pass(move || box trivially_copy_pass_by_ref); + store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); @@ -1237,13 +1239,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), + LintId::of(&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), + LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), LintId::of(&types::CAST_POSSIBLE_TRUNCATION), LintId::of(&types::CAST_POSSIBLE_WRAP), diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs new file mode 100644 index 00000000000..28816c3076d --- /dev/null +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -0,0 +1,256 @@ +use std::cmp; + +use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::attr; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; +use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; +use rustc_target::spec::Target; + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by reference, where + /// the argument type is `Copy` and small enough to be more efficient to always + /// pass by value. + /// + /// **Why is this bad?** In many calling conventions instances of structs will + /// be passed through registers if they fit into two or less general purpose + /// registers. + /// + /// **Known problems:** This lint is target register size dependent, it is + /// limited to 32-bit to try and reduce portability problems between 32 and + /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit + /// will be different. + /// + /// The configuration option `trivial_copy_size_limit` can be set to override + /// this limit for a project. + /// + /// This lint attempts to allow passing arguments by reference if a reference + /// to that argument is returned. This is implemented by comparing the lifetime + /// of the argument and return value for equality. However, this can cause + /// false positives in cases involving multiple lifetimes that are bounded by + /// each other. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// fn foo(v: &u32) {} + /// ``` + /// + /// ```rust + /// // Better + /// fn foo(v: u32) {} + /// ``` + pub TRIVIALLY_COPY_PASS_BY_REF, + pedantic, + "functions taking small copyable arguments by reference" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by value, where + /// the argument type is `Copy` and large enough to be worth considering + /// passing by reference. Does not trigger if the function is being exported, + /// because that might induce API breakage, if the parameter is declared as mutable, + /// or if the argument is a `self`. + /// + /// **Why is this bad?** Arguments passed by value might result in an unnecessary + /// shallow copy, taking up more space in the stack and requiring a call to + /// `memcpy`, which which can be expensive. + /// + /// **Example:** + /// + /// ```rust + /// #[derive(Clone, Copy)] + /// struct TooLarge([u8; 2048]); + /// + /// // Bad + /// fn foo(v: TooLarge) {} + /// ``` + /// ```rust + /// #[derive(Clone, Copy)] + /// struct TooLarge([u8; 2048]); + /// + /// // Good + /// fn foo(v: &TooLarge) {} + /// ``` + pub LARGE_TYPES_PASSED_BY_VALUE, + pedantic, + "functions taking large arguments by value" +} + +#[derive(Copy, Clone)] +pub struct PassByRefOrValue { + ref_min_size: u64, + value_max_size: u64, +} + +impl<'tcx> PassByRefOrValue { + pub fn new(ref_min_size: Option, value_max_size: u64, target: &Target) -> Self { + let ref_min_size = ref_min_size.unwrap_or_else(|| { + let bit_width = u64::from(target.pointer_width); + // Cap the calculated bit width at 32-bits to reduce + // portability problems between 32 and 64-bit targets + let bit_width = cmp::min(bit_width, 32); + #[allow(clippy::integer_division)] + let byte_width = bit_width / 8; + // Use a limit of 2 times the register byte width + byte_width * 2 + }); + + Self { + ref_min_size, + value_max_size, + } + } + + fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { + let fn_def_id = cx.tcx.hir().local_def_id(hir_id); + + let fn_sig = cx.tcx.fn_sig(fn_def_id); + let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); + + let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id)); + + for (index, (input, &ty)) in decl.inputs.iter().zip(fn_sig.inputs()).enumerate() { + // All spans generated from a proc-macro invocation are the same... + match span { + Some(s) if s == input.span => return, + _ => (), + } + + match ty.kind() { + ty::Ref(input_lt, ty, Mutability::Not) => { + // Use lifetimes to determine if we're returning a reference to the + // argument. In that case we can't switch to pass-by-value as the + // argument will not live long enough. + let output_lts = match *fn_sig.output().kind() { + ty::Ref(output_lt, _, _) => vec![output_lt], + ty::Adt(_, substs) => substs.regions().collect(), + _ => vec![], + }; + + if_chain! { + if !output_lts.contains(&input_lt); + if is_copy(cx, ty); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size <= self.ref_min_size; + if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; + then { + let value_type = if is_self_ty(decl_ty) { + "self".into() + } else { + snippet(cx, decl_ty.span, "_").into() + }; + span_lint_and_sugg( + cx, + TRIVIALLY_COPY_PASS_BY_REF, + input.span, + &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size), + "consider passing by value instead", + value_type, + Applicability::Unspecified, + ); + } + } + }, + + ty::Adt(_, _) | ty::Array(_, _) | ty::Tuple(_) => { + // if function has a body and parameter is annotated with mut, ignore + if let Some(param) = fn_body.and_then(|body| body.params.get(index)) { + match param.pat.kind { + PatKind::Binding(BindingAnnotation::Unannotated, _, _, _) => {}, + _ => continue, + } + } + + if_chain! { + if !cx.access_levels.is_exported(hir_id); + if is_copy(cx, ty); + if !is_self_ty(input); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size > self.value_max_size; + then { + span_lint_and_sugg( + cx, + LARGE_TYPES_PASSED_BY_VALUE, + input.span, + &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size), + "consider passing by reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); + } + } + }, + + _ => {}, + } + } + } +} + +impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]); + +impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if item.span.from_expansion() { + return; + } + + if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { + self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + _body: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + if span.from_expansion() { + return; + } + + match kind { + FnKind::ItemFn(.., header, _, attrs) => { + if header.abi != Abi::Rust { + return; + } + for a in attrs { + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } + } + } + }, + FnKind::Method(..) => (), + FnKind::Closure(..) => return, + } + + // Exclude non-inherent impls + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | + ItemKind::Trait(..)) + { + return; + } + } + + self.check_poly_fn(cx, hir_id, decl, Some(span)); + } +} diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs deleted file mode 100644 index e90ea0fc200..00000000000 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::cmp; - -use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::attr; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; -use rustc_target::abi::LayoutOf; -use rustc_target::spec::abi::Abi; -use rustc_target::spec::Target; - -declare_clippy_lint! { - /// **What it does:** Checks for functions taking arguments by reference, where - /// the argument type is `Copy` and small enough to be more efficient to always - /// pass by value. - /// - /// **Why is this bad?** In many calling conventions instances of structs will - /// be passed through registers if they fit into two or less general purpose - /// registers. - /// - /// **Known problems:** This lint is target register size dependent, it is - /// limited to 32-bit to try and reduce portability problems between 32 and - /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit - /// will be different. - /// - /// The configuration option `trivial_copy_size_limit` can be set to override - /// this limit for a project. - /// - /// This lint attempts to allow passing arguments by reference if a reference - /// to that argument is returned. This is implemented by comparing the lifetime - /// of the argument and return value for equality. However, this can cause - /// false positives in cases involving multiple lifetimes that are bounded by - /// each other. - /// - /// **Example:** - /// - /// ```rust - /// // Bad - /// fn foo(v: &u32) {} - /// ``` - /// - /// ```rust - /// // Better - /// fn foo(v: u32) {} - /// ``` - pub TRIVIALLY_COPY_PASS_BY_REF, - pedantic, - "functions taking small copyable arguments by reference" -} - -#[derive(Copy, Clone)] -pub struct TriviallyCopyPassByRef { - limit: u64, -} - -impl<'tcx> TriviallyCopyPassByRef { - pub fn new(limit: Option, target: &Target) -> Self { - let limit = limit.unwrap_or_else(|| { - let bit_width = u64::from(target.pointer_width); - // Cap the calculated bit width at 32-bits to reduce - // portability problems between 32 and 64-bit targets - let bit_width = cmp::min(bit_width, 32); - #[allow(clippy::integer_division)] - let byte_width = bit_width / 8; - // Use a limit of 2 times the register byte width - byte_width * 2 - }); - Self { limit } - } - - fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { - let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - - let fn_sig = cx.tcx.fn_sig(fn_def_id); - let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); - - // Use lifetimes to determine if we're returning a reference to the - // argument. In that case we can't switch to pass-by-value as the - // argument will not live long enough. - let output_lts = match *fn_sig.output().kind() { - ty::Ref(output_lt, _, _) => vec![output_lt], - ty::Adt(_, substs) => substs.regions().collect(), - _ => vec![], - }; - - for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) { - // All spans generated from a proc-macro invocation are the same... - match span { - Some(s) if s == input.span => return, - _ => (), - } - - if_chain! { - if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind(); - if !output_lts.contains(&input_lt); - if is_copy(cx, ty); - if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); - if size <= self.limit; - if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; - then { - let value_type = if is_self_ty(decl_ty) { - "self".into() - } else { - snippet(cx, decl_ty.span, "_").into() - }; - span_lint_and_sugg( - cx, - TRIVIALLY_COPY_PASS_BY_REF, - input.span, - &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit), - "consider passing by value instead", - value_type, - Applicability::Unspecified, - ); - } - } - } - } -} - -impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]); - -impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if item.span.from_expansion() { - return; - } - - if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - _body: &'tcx Body<'_>, - span: Span, - hir_id: HirId, - ) { - if span.from_expansion() { - return; - } - - match kind { - FnKind::ItemFn(.., header, _, attrs) => { - if header.abi != Abi::Rust { - return; - } - for a in attrs { - if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym!(proc_macro_derive)) - || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) - { - return; - } - } - } - }, - FnKind::Method(..) => (), - FnKind::Closure(..) => return, - } - - // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { - return; - } - } - - self.check_poly_fn(cx, hir_id, decl, Some(span)); - } -} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index dd2fd0bb445..0ac8fff69f0 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -150,6 +150,8 @@ define_Conf! { (literal_representation_threshold, "literal_representation_threshold": u64, 16384), /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit, "trivial_copy_size_limit": Option, None), + /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value. + (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256), /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have (too_many_lines_threshold, "too_many_lines_threshold": u64, 100), /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..f3536f26339 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1061,6 +1061,13 @@ vec![ deprecation: None, module: "large_stack_arrays", }, + Lint { + name: "large_types_passed_by_value", + group: "pedantic", + desc: "functions taking large arguments by value", + deprecation: None, + module: "pass_by_ref_or_value", + }, Lint { name: "len_without_is_empty", group: "style", @@ -2389,7 +2396,7 @@ vec![ group: "pedantic", desc: "functions taking small copyable arguments by reference", deprecation: None, - module: "trivially_copy_pass_by_ref", + module: "pass_by_ref_or_value", }, Lint { name: "try_err", diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 103ec27e7d7..a58e7e918e2 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/large_types_passed_by_value.rs b/tests/ui/large_types_passed_by_value.rs new file mode 100644 index 00000000000..e4a2e9df4d7 --- /dev/null +++ b/tests/ui/large_types_passed_by_value.rs @@ -0,0 +1,66 @@ +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" + +#![warn(clippy::large_types_passed_by_value)] + +pub struct Large([u8; 2048]); + +#[derive(Clone, Copy)] +pub struct LargeAndCopy([u8; 2048]); + +pub struct Small([u8; 4]); + +#[derive(Clone, Copy)] +pub struct SmallAndCopy([u8; 4]); + +fn small(a: Small, b: SmallAndCopy) {} +fn not_copy(a: Large) {} +fn by_ref(a: &Large, b: &LargeAndCopy) {} +fn mutable(mut a: LargeAndCopy) {} +fn bad(a: LargeAndCopy) {} +pub fn bad_but_pub(a: LargeAndCopy) {} + +impl LargeAndCopy { + fn self_is_ok(self) {} + fn other_is_not_ok(self, other: LargeAndCopy) {} + fn unless_other_can_change(self, mut other: LargeAndCopy) {} + pub fn or_were_in_public(self, other: LargeAndCopy) {} +} + +trait LargeTypeDevourer { + fn devoure_array(&self, array: [u8; 6666]); + fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)); + fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); +} + +pub trait PubLargeTypeDevourer { + fn devoure_array_in_public(&self, array: [u8; 6666]); +} + +struct S {} +impl LargeTypeDevourer for S { + fn devoure_array(&self, array: [u8; 6666]) { + todo!(); + } + fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)) { + todo!(); + } + fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)) { + todo!(); + } +} + +#[inline(always)] +fn foo_always(x: LargeAndCopy) { + todo!(); +} +#[inline(never)] +fn foo_never(x: LargeAndCopy) { + todo!(); +} +#[inline] +fn foo(x: LargeAndCopy) { + todo!(); +} + +fn main() {} diff --git a/tests/ui/large_types_passed_by_value.stderr b/tests/ui/large_types_passed_by_value.stderr new file mode 100644 index 00000000000..5f42dcfb9b5 --- /dev/null +++ b/tests/ui/large_types_passed_by_value.stderr @@ -0,0 +1,52 @@ +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:20:11 + | +LL | fn bad(a: LargeAndCopy) {} + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + | + = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:25:37 + | +LL | fn other_is_not_ok(self, other: LargeAndCopy) {} + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:31:36 + | +LL | fn devoure_array(&self, array: [u8; 6666]); + | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:32:34 + | +LL | fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:33:50 + | +LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:33:67 + | +LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:58:17 + | +LL | fn foo_never(x: LargeAndCopy) { + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:62:11 + | +LL | fn foo(x: LargeAndCopy) { + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From f82f9c2c55392ef9d7649bf2e485f7e509fd0038 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 2 Oct 2020 09:05:30 +0800 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d995cc9701..ba080835f5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1843,6 +1843,7 @@ Released 2018-09-13 [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref [`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock [`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound [`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8d53b9799f0..3a108bcfe6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -255,6 +255,7 @@ mod modulo_arithmetic; mod multiple_crate_versions; mod mut_key; mod mut_mut; +mod mut_mutex_lock; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; @@ -744,6 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, &mut_key::MUTABLE_KEY_TYPE, &mut_mut::MUT_MUT, + &mut_mutex_lock::MUT_MUTEX_LOCK, &mut_reference::UNNECESSARY_MUT_PASSED, &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, &mutex_atomic::MUTEX_ATOMIC, @@ -1112,6 +1114,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); + store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); @@ -1446,6 +1449,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mutex_atomic::MUTEX_ATOMIC), LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), @@ -1780,6 +1784,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs new file mode 100644 index 00000000000..4f3108355ca --- /dev/null +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -0,0 +1,75 @@ +use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `&mut Mutex::lock` calls + /// + /// **Why is this bad?** `Mutex::lock` is less efficient than + /// calling `Mutex::get_mut` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.lock().unwrap(); + /// do_stuff(value); + /// ``` + /// Use instead: + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.get_mut().unwrap(); + /// do_stuff(value); + /// ``` + pub MUT_MUTEX_LOCK, + correctness, + "`&mut Mutex::lock` does unnecessary locking" +} + +declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); + +impl<'tcx> LateLintPass<'tcx> for MutMutexLock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { + if_chain! { + if is_mut_mutex_lock_call(cx, ex).is_some(); + then { + span_lint_and_help( + cx, + MUT_MUTEX_LOCK, + ex.span, + "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", + None, + "use `&mut Mutex::get_mut` instead", + ); + } + } + } +} + +fn is_mut_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; + if path.ident.name == sym!(lock); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); + if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); + then { + Some(&args[0]) + } else { + None + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 98ad994ea7b..5e48757a4c5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1502,6 +1502,13 @@ vec![ deprecation: None, module: "mut_mut", }, + Lint { + name: "mut_mutex_lock", + group: "correctness", + desc: "`&mut Mutex::lock` does unnecessary locking", + deprecation: None, + module: "mut_mutex_lock", + }, Lint { name: "mut_range_bound", group: "complexity", diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs new file mode 100644 index 00000000000..516d44bb7a9 --- /dev/null +++ b/tests/ui/mut_mutex_lock.rs @@ -0,0 +1,19 @@ +#![warn(clippy::mut_mutex_lock)] + +use std::sync::{Arc, Mutex}; + +fn mut_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + + let value = value_mutex.lock().unwrap(); + *value += 1; +} + +fn no_owned_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value = value_rc.lock().unwrap(); + *value += 1; +} + +fn main() {} diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr new file mode 100644 index 00000000000..426e0240830 --- /dev/null +++ b/tests/ui/mut_mutex_lock.stderr @@ -0,0 +1,19 @@ +error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable + --> $DIR/mut_mutex_lock.rs:10:6 + | +LL | let value = value_mutex.lock().unwrap(); + | ----- help: consider changing this to be mutable: `mut value` +LL | *value += 1; + | ^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable + --> $DIR/mut_mutex_lock.rs:16:6 + | +LL | let value = value_rc.lock().unwrap(); + | ----- help: consider changing this to be mutable: `mut value` +LL | *value += 1; + | ^^^^^ cannot borrow as mutable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`. -- cgit 1.4.1-3-g733a5 From fb8a9cb38d73f3876b57f250502895c7cc60d421 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 2 Oct 2020 10:54:44 +0800 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 4f3108355ca..0680e578537 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -21,8 +21,8 @@ declare_clippy_lint! { /// let mut value_rc = Arc::new(Mutex::new(42_u8)); /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); /// - /// let value = value_mutex.lock().unwrap(); - /// do_stuff(value); + /// let mut value = value_mutex.lock().unwrap(); + /// *value += 1; /// ``` /// Use instead: /// ```rust @@ -32,7 +32,7 @@ declare_clippy_lint! { /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); /// /// let value = value_mutex.get_mut().unwrap(); - /// do_stuff(value); + /// *value += 1; /// ``` pub MUT_MUTEX_LOCK, correctness, diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 516d44bb7a9..9cd98e90c29 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -6,13 +6,13 @@ fn mut_mutex_lock() { let mut value_rc = Arc::new(Mutex::new(42_u8)); let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - let value = value_mutex.lock().unwrap(); + let mut value = value_mutex.lock().unwrap(); *value += 1; } fn no_owned_mutex_lock() { let mut value_rc = Arc::new(Mutex::new(42_u8)); - let value = value_rc.lock().unwrap(); + let mut value = value_rc.lock().unwrap(); *value += 1; } diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index 426e0240830..d521ebb56c4 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,19 +1,11 @@ -error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable - --> $DIR/mut_mutex_lock.rs:10:6 +error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference + --> $DIR/mut_mutex_lock.rs:9:21 | -LL | let value = value_mutex.lock().unwrap(); - | ----- help: consider changing this to be mutable: `mut value` -LL | *value += 1; - | ^^^^^ cannot borrow as mutable - -error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable - --> $DIR/mut_mutex_lock.rs:16:6 +LL | let mut value = value_mutex.lock().unwrap(); + | ^^^^^^^^^^^^^^^^^^ | -LL | let value = value_rc.lock().unwrap(); - | ----- help: consider changing this to be mutable: `mut value` -LL | *value += 1; - | ^^^^^ cannot borrow as mutable + = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` + = help: use `&mut Mutex::get_mut` instead -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0596`. -- cgit 1.4.1-3-g733a5 From ec0c3afa7303383ee039c252b59b56023052ae2e Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sat, 3 Oct 2020 07:53:45 +0800 Subject: Run update_lints --- clippy_lints/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3a108bcfe6a..648c65ef7b0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1785,8 +1785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), -- cgit 1.4.1-3-g733a5 From 77e34a69bbbec0ef05dee9750ff3e7db4eb35d59 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Mon, 5 Oct 2020 11:44:54 +0800 Subject: Inline is_mut_mutex_lock_call --- clippy_lints/src/mut_mutex_lock.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 0680e578537..ca3371a5d75 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -44,7 +44,11 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if is_mut_mutex_lock_call(cx, ex).is_some(); + if let ExprKind::MethodCall(path, _span, args, _) = &ex.kind; + if path.ident.name == sym!(lock); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); + if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { span_lint_and_help( cx, @@ -58,18 +62,3 @@ impl<'tcx> LateLintPass<'tcx> for MutMutexLock { } } } - -fn is_mut_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; - if path.ident.name == sym!(lock); - let ty = cx.typeck_results().expr_ty(&args[0]); - if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); - if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); - then { - Some(&args[0]) - } else { - None - } - } -} -- cgit 1.4.1-3-g733a5 From 292cb9bfb6bc51595404425b0ada90f21e6d9661 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sat, 10 Oct 2020 18:07:47 +0800 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index ca3371a5d75..82ed2d6d69c 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -1,5 +1,6 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -9,7 +10,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `&mut Mutex::lock` calls /// /// **Why is this bad?** `Mutex::lock` is less efficient than - /// calling `Mutex::get_mut` + /// calling `Mutex::get_mut`. In addition you also have a statically + /// guarantee that the mutex isn't locked, instead of just a runtime + /// guarantee. /// /// **Known problems:** None. /// @@ -44,19 +47,20 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &ex.kind; + if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind; if path.ident.name == sym!(lock); let ty = cx.typeck_results().expr_ty(&args[0]); if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { - span_lint_and_help( + span_lint_and_sugg( cx, MUT_MUTEX_LOCK, - ex.span, + *method_span, "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", - None, - "use `&mut Mutex::get_mut` instead", + "change this to", + "get_mut".to_owned(), + Applicability::MachineApplicable, ); } } diff --git a/tests/ui/mut_mutex_lock.fixed b/tests/ui/mut_mutex_lock.fixed new file mode 100644 index 00000000000..36bc52e3374 --- /dev/null +++ b/tests/ui/mut_mutex_lock.fixed @@ -0,0 +1,21 @@ +// run-rustfix +#![allow(dead_code, unused_mut)] +#![warn(clippy::mut_mutex_lock)] + +use std::sync::{Arc, Mutex}; + +fn mut_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + + let mut value = value_mutex.get_mut().unwrap(); + *value += 1; +} + +fn no_owned_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let mut value = value_rc.lock().unwrap(); + *value += 1; +} + +fn main() {} diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 9cd98e90c29..ea60df5ae1b 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -1,3 +1,5 @@ +// run-rustfix +#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index d521ebb56c4..21c1b3486ca 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,11 +1,10 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> $DIR/mut_mutex_lock.rs:9:21 + --> $DIR/mut_mutex_lock.rs:11:33 | LL | let mut value = value_mutex.lock().unwrap(); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^ help: change this to: `get_mut` | = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` - = help: use `&mut Mutex::get_mut` instead error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From e7e03b791217645d48e407b28ed023723cedb24c Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sun, 18 Oct 2020 09:09:07 +0800 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 648c65ef7b0..9d88cc22d10 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1621,6 +1621,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), @@ -1784,7 +1785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 82ed2d6d69c..df1cecb328c 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// *value += 1; /// ``` pub MUT_MUTEX_LOCK, - correctness, + style, "`&mut Mutex::lock` does unnecessary locking" } @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for MutMutexLock { "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", "change this to", "get_mut".to_owned(), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5e48757a4c5..25ede21da77 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1504,7 +1504,7 @@ vec![ }, Lint { name: "mut_mutex_lock", - group: "correctness", + group: "style", desc: "`&mut Mutex::lock` does unnecessary locking", deprecation: None, module: "mut_mutex_lock", -- cgit 1.4.1-3-g733a5 From d5713898acd3e9afe04223522aad50c9da8f05e2 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 25 Oct 2020 19:30:00 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index fd45a6da61c..9c0efef95de 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -579,9 +579,8 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet< if let hir::PatKind::Wild = pat.kind { return false; // ignore `_` patterns } - let def_id = pat.hir_id.owner.to_def_id(); - if cx.tcx.has_typeck_results(def_id) { - is_mutable_ty(cx, &cx.tcx.typeck(def_id.expect_local()).pat_ty(pat), pat.span, tys) + if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { + is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) } else { false } @@ -694,11 +693,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { Call(_, args) | MethodCall(_, _, args, _) => { let mut tys = FxHashSet::default(); for arg in args { - let def_id = arg.hir_id.owner.to_def_id(); - if self.cx.tcx.has_typeck_results(def_id) + if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) && is_mutable_ty( self.cx, - self.cx.tcx.typeck(def_id.expect_local()).expr_ty(arg), + self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), arg.span, &mut tys, ) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 44f2674eaa2..23ca35fffaa 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2051,12 +2051,11 @@ fn check_for_mutation<'tcx>( span_low: None, span_high: None, }; - let def_id = body.hir_id.owner.to_def_id(); cx.tcx.infer_ctxt().enter(|infcx| { ExprUseVisitor::new( &mut delegate, &infcx, - def_id.expect_local(), + body.hir_id.owner, cx.param_env, cx.typeck_results(), ) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a9d26d48b12..8297b9d128d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -299,7 +299,7 @@ pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) - hir::QPath::Resolved(_, path) => path.res, hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { if cx.tcx.has_typeck_results(id.owner.to_def_id()) { - cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id) + cx.tcx.typeck(id.owner).qpath_res(qpath, id) } else { Res::Err } diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 2fd6046ebcf..8b327b2d467 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -19,12 +19,11 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: FxHashSet::default(), skip: false, }; - let def_id = expr.hir_id.owner.to_def_id(); cx.tcx.infer_ctxt().enter(|infcx| { ExprUseVisitor::new( &mut delegate, &infcx, - def_id.expect_local(), + expr.hir_id.owner, cx.param_env, cx.typeck_results(), ) -- cgit 1.4.1-3-g733a5 From db8380c4a0e7f707112bbce19a6074a5fac2de59 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Wed, 31 Oct 2018 17:14:55 +0100 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd79deb56c..c0dd7b352ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2017,6 +2017,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unusual_byte_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_grouping [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b330b66776c..5d6900f6b96 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,6 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &literal_representation::LARGE_DIGIT_GROUPS, &literal_representation::MISTYPED_LITERAL_SUFFIXES, &literal_representation::UNREADABLE_LITERAL, + &literal_representation::UNUSUAL_BYTE_GROUPING, &loops::EMPTY_LOOP, &loops::EXPLICIT_COUNTER_LOOP, &loops::EXPLICIT_INTO_ITER_LOOP, @@ -1365,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1587,6 +1589,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index c54103b25c2..b41cfe32cfe 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -82,6 +82,25 @@ declare_clippy_lint! { "integer literals with digits grouped inconsistently" } +declare_clippy_lint! { + /// **What it does:** Warns if hexadecimal or binary literals are not grouped + /// by nibble or byte. + /// + /// **Why is this bad?** Negatively impacts readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x: u32 = 0xFFF_FFF; + /// let y: u8 = 0b01_011_101; + /// ``` + pub UNUSUAL_BYTE_GROUPING, + style, + "binary or hex literals that aren't grouped by four" +} + declare_clippy_lint! { /// **What it does:** Warns if the digits of an integral or floating-point /// constant are grouped into groups that @@ -125,6 +144,7 @@ enum WarningType { LargeDigitGroups, DecimalRepresentation, MistypedLiteralSuffix, + UnusualByteGrouping, } impl WarningType { @@ -175,6 +195,15 @@ impl WarningType { suggested_format, Applicability::MachineApplicable, ), + Self::UnusualByteGrouping => span_lint_and_sugg( + cx, + UNUSUAL_BYTE_GROUPING, + span, + "digits of hex or binary literal not grouped by four", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), }; } } @@ -184,6 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [ INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, + UNUSUAL_BYTE_GROUPING, ]); impl EarlyLintPass for LiteralDigitGrouping { @@ -217,9 +247,9 @@ impl LiteralDigitGrouping { let result = (|| { - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?; + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?; if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?; + let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?; let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, @@ -229,6 +259,7 @@ impl LiteralDigitGrouping { return Err(WarningType::InconsistentDigitGrouping); }; } + Ok(()) })(); @@ -237,6 +268,7 @@ impl LiteralDigitGrouping { let should_warn = match warning_type { | WarningType::UnreadableLiteral | WarningType::InconsistentDigitGrouping + | WarningType::UnusualByteGrouping | WarningType::LargeDigitGroups => { !in_macro(lit.span) } @@ -331,11 +363,15 @@ impl LiteralDigitGrouping { /// Returns the size of the digit groups (or None if ungrouped) if successful, /// otherwise returns a `WarningType` for linting. - fn get_group_size<'a>(groups: impl Iterator) -> Result, WarningType> { + fn get_group_size<'a>(groups: impl Iterator, radix: Radix) -> Result, WarningType> { let mut groups = groups.map(str::len); let first = groups.next().expect("At least one group"); + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i % 4 != 0) { + return Err(WarningType::UnusualByteGrouping); + } + if let Some(second) = groups.next() { if !groups.all(|x| x == second) || first > second { Err(WarningType::InconsistentDigitGrouping) diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 52d3c2c1daf..d02603d7702 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -1,6 +1,6 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum Radix { Binary, Octal, @@ -11,8 +11,8 @@ pub enum Radix { impl Radix { /// Returns a reasonable digit group size for this radix. #[must_use] - fn suggest_grouping(&self) -> usize { - match *self { + fn suggest_grouping(self) -> usize { + match self { Self::Binary | Self::Hexadecimal => 4, Self::Octal | Self::Decimal => 3, } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4bf77dae637..fc8efb81cfc 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2643,6 +2643,13 @@ vec![ deprecation: None, module: "unused_unit", }, + Lint { + name: "unusual_byte_grouping", + group: "style", + desc: "binary or hex literals that aren\'t grouped by four", + deprecation: None, + module: "literal_representation", + }, Lint { name: "unwrap_in_result", group: "restriction", diff --git a/tests/ui/large_digit_groups.fixed b/tests/ui/large_digit_groups.fixed index 859fad2f54d..3430c137ec2 100644 --- a/tests/ui/large_digit_groups.fixed +++ b/tests/ui/large_digit_groups.fixed @@ -11,7 +11,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x1_234_567, + 0x0123_4567, 1_2345_6789, 1234_f32, 1_234.12_f32, diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index b6d9672a78e..fe472e66949 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -1,24 +1,30 @@ -error: digit groups should be smaller +error: digits of hex or binary literal not grouped by four + --> $DIR/large_digit_groups.rs:14:9 + | +LL | 0x1_234_567, + | ^^^^^^^^^^^ help: consider: `0x0123_4567` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + +error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:22:9 | LL | 0b1_10110_i64, | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64` - | - = note: `-D clippy::large-digit-groups` implied by `-D warnings` -error: digits grouped inconsistently by underscores +error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:23:9 | LL | 0xd_e_adbee_f_usize, | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` - | - = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:24:9 | LL | 1_23456_f32, | ^^^^^^^^^^^ help: consider: `123_456_f32` + | + = note: `-D clippy::large-digit-groups` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:25:9 @@ -38,5 +44,5 @@ error: digit groups should be smaller LL | 1_23456.12345_6_f64, | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index c299b16c8ce..2608638ff80 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,10 +7,10 @@ fn main() { let ok1 = 0xABCD; - let ok3 = 0xab_cd; - let ok4 = 0xab_cd_i32; - let ok5 = 0xAB_CD_u32; - let ok5 = 0xAB_CD_isize; + let ok3 = 0xabcd; + let ok4 = 0xabcd_i32; + let ok5 = 0xABCD_u32; + let ok5 = 0xABCD_isize; let fail1 = 0xabCD; let fail2 = 0xabCD_u32; let fail2 = 0xabCD_isize; @@ -33,4 +33,9 @@ fn main() { let fail19 = 12_3456_21; let fail22 = 3__4___23; let fail23 = 3__16___23; + + let fail24 = 0xAB_ABC_AB; + let fail25 = 0b01_100_101; + let ok26 = 0x6_A0_BF; + let ok27 = 0b1_0010_0101; } diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 0b3af2d8bc3..c88848a40fc 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -69,5 +69,25 @@ error: digits grouped inconsistently by underscores LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` -error: aborting due to 8 previous errors +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:37:18 + | +LL | let fail24 = 0xAB_ABC_AB; + | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:38:18 + | +LL | let fail25 = 0b01_100_101; + | ^^^^^^^^^^^^ help: consider: `0b0110_0101` + +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:39:16 + | +LL | let ok26 = 0x6_A0_BF; + | ^^^^^^^^^ help: consider: `0x0006_A0BF` + +error: aborting due to 11 previous errors diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 3f358d9ecaa..4043d53299f 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -14,7 +14,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x1_234_567, + 0x0123_4567, 65536, 1_2345_6789, 1234_f32, diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 1b2ff6bff04..fa4c3fe13e3 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,3 +1,11 @@ +error: digits of hex or binary literal not grouped by four + --> $DIR/unreadable_literal.rs:17:9 + | +LL | 0x1_234_567, + | ^^^^^^^^^^^ help: consider: `0x0123_4567` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + error: long literal lacking separators --> $DIR/unreadable_literal.rs:25:17 | @@ -54,5 +62,5 @@ error: long literal lacking separators LL | let _fail12: i128 = 0xabcabcabcabcabcabc; | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From f5a88b6de558d17ed088a93756facb901c7c0888 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 09:18:06 -0400 Subject: Allow hex literals to pass w/ groups of 2 --- clippy_lints/src/literal_representation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b41cfe32cfe..813f3b6f378 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -368,7 +368,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i % 4 != 0) { + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 || i != 2) { return Err(WarningType::UnusualByteGrouping); } -- cgit 1.4.1-3-g733a5 From e7e4b35bdf695c4db5a1f34319d01b12a9d54b34 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 09:34:46 -0400 Subject: Fix logic mistake --- clippy_lints/src/literal_representation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 813f3b6f378..f51a0edc9c5 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -368,7 +368,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 || i != 2) { + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) { return Err(WarningType::UnusualByteGrouping); } -- cgit 1.4.1-3-g733a5 From 3ce820bf83657879493aa7b107634f1951e7c219 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 21 Oct 2020 22:33:41 +0900 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 23ca35fffaa..c3f75f283f4 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3001,7 +3001,14 @@ impl IterFunction { IterFunctionKind::IntoIter => String::new(), IterFunctionKind::Len => String::from(".count()"), IterFunctionKind::IsEmpty => String::from(".next().is_none()"), - IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")), + IterFunctionKind::Contains(span) => { + let s = snippet(cx, *span, ".."); + if let Some(stripped) = s.strip_prefix('&') { + format!(".any(|x| x == {})", stripped) + } else { + format!(".any(|x| x == *{})", s) + } + }, } } fn get_suggestion_text(&self) -> &'static str { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 0c1e61d7496..7b8e227f304 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -48,7 +48,7 @@ LL | | indirect_contains.contains(&&5); help: Check if the original Iterator contains an element instead of collecting then checking | LL | -LL | sample.iter().any(|x| x == &&5); +LL | sample.iter().any(|x| x == &5); | error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 312bbff6968dbebd367ca90677c676e2cf5198d2 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 11:31:24 -0400 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index c0dd7b352ad..25f3b5da198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2017,7 +2017,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit -[`unusual_byte_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_grouping +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5d6900f6b96..3be8bc0e36d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,7 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &literal_representation::LARGE_DIGIT_GROUPS, &literal_representation::MISTYPED_LITERAL_SUFFIXES, &literal_representation::UNREADABLE_LITERAL, - &literal_representation::UNUSUAL_BYTE_GROUPING, + &literal_representation::UNUSUAL_BYTE_GROUPINGS, &loops::EMPTY_LOOP, &loops::EXPLICIT_COUNTER_LOOP, &loops::EXPLICIT_INTO_ITER_LOOP, @@ -1366,7 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1589,7 +1589,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index f51a0edc9c5..e8a741683da 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -96,7 +96,7 @@ declare_clippy_lint! { /// let x: u32 = 0xFFF_FFF; /// let y: u8 = 0b01_011_101; /// ``` - pub UNUSUAL_BYTE_GROUPING, + pub UNUSUAL_BYTE_GROUPINGS, style, "binary or hex literals that aren't grouped by four" } @@ -144,7 +144,7 @@ enum WarningType { LargeDigitGroups, DecimalRepresentation, MistypedLiteralSuffix, - UnusualByteGrouping, + UnusualByteGroupings, } impl WarningType { @@ -195,9 +195,9 @@ impl WarningType { suggested_format, Applicability::MachineApplicable, ), - Self::UnusualByteGrouping => span_lint_and_sugg( + Self::UnusualByteGroupings => span_lint_and_sugg( cx, - UNUSUAL_BYTE_GROUPING, + UNUSUAL_BYTE_GROUPINGS, span, "digits of hex or binary literal not grouped by four", "consider", @@ -213,7 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [ INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, - UNUSUAL_BYTE_GROUPING, + UNUSUAL_BYTE_GROUPINGS, ]); impl EarlyLintPass for LiteralDigitGrouping { @@ -268,7 +268,7 @@ impl LiteralDigitGrouping { let should_warn = match warning_type { | WarningType::UnreadableLiteral | WarningType::InconsistentDigitGrouping - | WarningType::UnusualByteGrouping + | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { !in_macro(lit.span) } @@ -369,7 +369,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) { - return Err(WarningType::UnusualByteGrouping); + return Err(WarningType::UnusualByteGroupings); } if let Some(second) = groups.next() { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index fc8efb81cfc..6272ce45efb 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2644,7 +2644,7 @@ vec![ module: "unused_unit", }, Lint { - name: "unusual_byte_grouping", + name: "unusual_byte_groupings", group: "style", desc: "binary or hex literals that aren\'t grouped by four", deprecation: None, diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index fe472e66949..13d108b56e0 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -4,7 +4,7 @@ error: digits of hex or binary literal not grouped by four LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:22:9 diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 2608638ff80..a72a74b9131 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,10 +7,10 @@ fn main() { let ok1 = 0xABCD; - let ok3 = 0xabcd; - let ok4 = 0xabcd_i32; - let ok5 = 0xABCD_u32; - let ok5 = 0xABCD_isize; + let ok3 = 0xab_cd; + let ok4 = 0xab_cd_i32; + let ok5 = 0xAB_CD_u32; + let ok5 = 0xAB_CD_isize; let fail1 = 0xabCD; let fail2 = 0xabCD_u32; let fail2 = 0xabCD_isize; diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index e321f2a1cef..64ceeb316d8 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -75,7 +75,7 @@ error: digits of hex or binary literal not grouped by four LL | let fail24 = 0xAB_ABC_AB; | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digits of hex or binary literal not grouped by four --> $DIR/literals.rs:38:18 diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index fa4c3fe13e3..8645cabeabb 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -4,7 +4,7 @@ error: digits of hex or binary literal not grouped by four LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: long literal lacking separators --> $DIR/unreadable_literal.rs:25:17 -- cgit 1.4.1-3-g733a5 From de5a6d3420d9f2f2c4c995e325412e862a9dc583 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 00:31:25 +0100 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f96398153..869e9949167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1664,6 +1664,7 @@ Released 2018-09-13 [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute diff --git a/Cargo.toml b/Cargo.toml index 1ddcd18598d..dae3e03074f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,13 @@ path = "src/main.rs" name = "clippy-driver" path = "src/driver.rs" +[target.'cfg(NOT_A_PLATFORM)'.dependencies] +rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } +rustc_driver = { path = "/home/urcra/rust/compiler/rustc_driver" } +rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } +rustc_interface = { path = "/home/urcra/rust/compiler/rustc_interface" } +rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } + [dependencies] # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..167db15be20 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -16,6 +16,28 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] edition = "2018" +[target.'cfg(NOT_A_PLATFORM)'.dependencies] +rustc_ast = { path = "/home/urcra/rust/compiler/rustc_ast" } +rustc_ast_pretty = { path = "/home/urcra/rust/compiler/rustc_ast_pretty" } +rustc_attr = { path = "/home/urcra/rust/compiler/rustc_attr" } +rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } +rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } +rustc_hir = { path = "/home/urcra/rust/compiler/rustc_hir" } +rustc_hir_pretty = { path = "/home/urcra/rust/compiler/rustc_hir_pretty" } +rustc_index = { path = "/home/urcra/rust/compiler/rustc_index" } +rustc_infer = { path = "/home/urcra/rust/compiler/rustc_infer" } +rustc_lexer = { path = "/home/urcra/rust/compiler/rustc_lexer" } +rustc_lint = { path = "/home/urcra/rust/compiler/rustc_lint" } +rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } +rustc_mir = { path = "/home/urcra/rust/compiler/rustc_mir" } +rustc_parse = { path = "/home/urcra/rust/compiler/rustc_parse" } +rustc_parse_format = { path = "/home/urcra/rust/compiler/rustc_parse_format" } +rustc_session = { path = "/home/urcra/rust/compiler/rustc_session" } +rustc_span = { path = "/home/urcra/rust/compiler/rustc_span" } +rustc_target = { path = "/home/urcra/rust/compiler/rustc_target" } +rustc_trait_selection = { path = "/home/urcra/rust/compiler/rustc_trait_selection" } +rustc_typeck = { path = "/home/urcra/rust/compiler/rustc_typeck" } + [dependencies] cargo_metadata = "0.12" if_chain = "1.0.0" diff --git a/clippy_lints/src/comparison_to_empty.rs b/clippy_lints/src/comparison_to_empty.rs new file mode 100644 index 00000000000..3b55336722c --- /dev/null +++ b/clippy_lints/src/comparison_to_empty.rs @@ -0,0 +1,155 @@ +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, ItemKind, TraitItemRef}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{Span, Spanned}; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub COMPARISON_TO_EMPTY, + style, + "default lint description" +} + +declare_lint_pass!(ComparisonToEmpty => [COMPARISON_TO_EMPTY]); + +impl LateLintPass<'_> for ComparisonToEmpty { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + match cmp { + BinOpKind::Eq => { + check_cmp(cx, expr.span, left, right, "", 0); // len == 0 + check_cmp(cx, expr.span, right, left, "", 0); // 0 == len + }, + BinOpKind::Ne => { + check_cmp(cx, expr.span, left, right, "!", 0); // len != 0 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len + }, + BinOpKind::Gt => { + check_cmp(cx, expr.span, left, right, "!", 0); // len > 0 + check_cmp(cx, expr.span, right, left, "", 1); // 1 > len + }, + BinOpKind::Lt => { + check_cmp(cx, expr.span, left, right, "", 1); // len < 1 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len + }, + BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1 + BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len + _ => (), + } + } + } + +} + + +fn check_cmp(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str, compare_to: u32) { + check_empty_expr(cx, span, lit1, lit2, op) +} + +fn check_empty_expr( + cx: &LateContext<'_>, + span: Span, + lit1: &Expr<'_>, + lit2: &Expr<'_>, + op: &str +) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + &format!("comparison to empty slice"), + &format!("using `{}is_empty` is clearer and more explicit", op), + format!( + "{}{}.is_empty()", + op, + snippet_with_applicability(cx, lit1.span, "_", &mut applicability) + ), + applicability, + ); + } +} + +fn is_empty_string(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(lit, _) = lit.node { + let lit = lit.as_str(); + return lit == ""; + } + } + false +} + +fn is_empty_array(expr: &Expr<'_>) -> bool { + if let ExprKind::Array(ref arr) = expr.kind { + return arr.is_empty(); + } + false +} + + +/// Checks if this type has an `is_empty` method. +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. + fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { + if let ty::AssocKind::Fn = item.kind { + if item.ident.name.as_str() == "is_empty" { + let sig = cx.tcx.fn_sig(item.def_id); + let ty = sig.skip_binder(); + ty.inputs().len() == 1 + } else { + false + } + } else { + false + } + } + + /// Checks the inherent impl's items for an `is_empty(self)` method. + fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + cx.tcx.inherent_impls(id).iter().any(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }) + } + + let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); + match ty.kind() { + ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + cx.tcx + .associated_items(principal.def_id()) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }), + ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), + ty::Adt(id, _) => has_is_empty_impl(cx, id.did), + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1a950a7c334..75629e13a8e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,6 +171,7 @@ mod checked_conversions; mod cognitive_complexity; mod collapsible_if; mod comparison_chain; +mod comparison_to_empty; mod copies; mod copy_iterator; mod create_dir; @@ -523,6 +524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, + &comparison_to_empty::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -1139,6 +1141,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_late_pass(|| box comparison_to_empty::ComparisonToEmpty); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1299,6 +1302,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1555,6 +1559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index da865a66c45..c2b2236bc66 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -291,6 +291,13 @@ vec![ deprecation: None, module: "comparison_chain", }, + Lint { + name: "comparison_to_empty", + group: "style", + desc: "default lint description", + deprecation: None, + module: "comparison_to_empty", + }, Lint { name: "copy_iterator", group: "pedantic", diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed new file mode 100644 index 00000000000..261024caca7 --- /dev/null +++ b/tests/ui/comparison_to_empty.fixed @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::comparison_to_empty)] + +fn main() { + // Disallow comparisons to empty + let s = String::new(); + let _ = s.is_empty(); + let _ = !s.is_empty(); + + let v = vec![0]; + let _ = v.is_empty(); + let _ = !v.is_empty(); + + // Allow comparisons to non-empty + let s = String::new(); + let _ = s == " "; + let _ = s != " "; + + let v = vec![0]; + let _ = v == [0]; + let _ = v != [0]; +} diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs new file mode 100644 index 00000000000..98ddd974951 --- /dev/null +++ b/tests/ui/comparison_to_empty.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::comparison_to_empty)] + +fn main() { + // Disallow comparisons to empty + let s = String::new(); + let _ = s == ""; + let _ = s != ""; + + let v = vec![0]; + let _ = v == []; + let _ = v != []; + + // Allow comparisons to non-empty + let s = String::new(); + let _ = s == " "; + let _ = s != " "; + + let v = vec![0]; + let _ = v == [0]; + let _ = v != [0]; +} diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr new file mode 100644 index 00000000000..f69d6bd5255 --- /dev/null +++ b/tests/ui/comparison_to_empty.stderr @@ -0,0 +1,28 @@ +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:8:13 + | +LL | let _ = s == ""; + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + | + = note: `-D clippy::comparison-to-empty` implied by `-D warnings` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:9:13 + | +LL | let _ = s != ""; + | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:12:13 + | +LL | let _ = v == []; + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:13:13 + | +LL | let _ = v != []; + | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 7f7faa1ccf4683582563d2f7a63aba988cb24cc8 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 00:51:18 +0100 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_to_empty.rs b/clippy_lints/src/comparison_to_empty.rs deleted file mode 100644 index 3b55336722c..00000000000 --- a/clippy_lints/src/comparison_to_empty.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, Expr, ExprKind, ItemKind, TraitItemRef}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned}; - -declare_clippy_lint! { - /// **What it does:** - /// - /// **Why is this bad?** - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub COMPARISON_TO_EMPTY, - style, - "default lint description" -} - -declare_lint_pass!(ComparisonToEmpty => [COMPARISON_TO_EMPTY]); - -impl LateLintPass<'_> for ComparisonToEmpty { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { - match cmp { - BinOpKind::Eq => { - check_cmp(cx, expr.span, left, right, "", 0); // len == 0 - check_cmp(cx, expr.span, right, left, "", 0); // 0 == len - }, - BinOpKind::Ne => { - check_cmp(cx, expr.span, left, right, "!", 0); // len != 0 - check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len - }, - BinOpKind::Gt => { - check_cmp(cx, expr.span, left, right, "!", 0); // len > 0 - check_cmp(cx, expr.span, right, left, "", 1); // 1 > len - }, - BinOpKind::Lt => { - check_cmp(cx, expr.span, left, right, "", 1); // len < 1 - check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len - }, - BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1 - BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len - _ => (), - } - } - } - -} - - -fn check_cmp(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str, compare_to: u32) { - check_empty_expr(cx, span, lit1, lit2, op) -} - -fn check_empty_expr( - cx: &LateContext<'_>, - span: Span, - lit1: &Expr<'_>, - lit2: &Expr<'_>, - op: &str -) { - if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - COMPARISON_TO_EMPTY, - span, - &format!("comparison to empty slice"), - &format!("using `{}is_empty` is clearer and more explicit", op), - format!( - "{}{}.is_empty()", - op, - snippet_with_applicability(cx, lit1.span, "_", &mut applicability) - ), - applicability, - ); - } -} - -fn is_empty_string(expr: &Expr<'_>) -> bool { - if let ExprKind::Lit(ref lit) = expr.kind { - if let LitKind::Str(lit, _) = lit.node { - let lit = lit.as_str(); - return lit == ""; - } - } - false -} - -fn is_empty_array(expr: &Expr<'_>) -> bool { - if let ExprKind::Array(ref arr) = expr.kind { - return arr.is_empty(); - } - false -} - - -/// Checks if this type has an `is_empty` method. -fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. - fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if let ty::AssocKind::Fn = item.kind { - if item.ident.name.as_str() == "is_empty" { - let sig = cx.tcx.fn_sig(item.def_id); - let ty = sig.skip_binder(); - ty.inputs().len() == 1 - } else { - false - } - } else { - false - } - } - - /// Checks the inherent impl's items for an `is_empty(self)` method. - fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { - cx.tcx.inherent_impls(id).iter().any(|imp| { - cx.tcx - .associated_items(*imp) - .in_definition_order() - .any(|item| is_is_empty(cx, &item)) - }) - } - - let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); - match ty.kind() { - ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { - cx.tcx - .associated_items(principal.def_id()) - .in_definition_order() - .any(|item| is_is_empty(cx, &item)) - }), - ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), - ty::Adt(id, _) => has_is_empty_impl(cx, id.did), - ty::Array(..) | ty::Slice(..) | ty::Str => true, - _ => false, - } -} diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index c9c4891bb08..75e9c5c973b 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -68,7 +68,45 @@ declare_clippy_lint! { "traits or impls with a public `len` method but no corresponding `is_empty` method" } -declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]); +declare_clippy_lint! { + /// **What it does:** Checks for comparing to an empty slice such as "" or [],` + /// and suggests using `.is_empty()` where applicable. + /// + /// **Why is this bad?** Some structures can answer `.is_empty()` much faster + /// than checking for equality. So it is good to get into the habit of using + /// `.is_empty()`, and having it is cheap. + /// Besides, it makes the intent clearer than a manual comparison in some contexts. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```ignore + /// if s == "" { + /// .. + /// } + + /// if arr == [] { + /// .. + /// } + /// ``` + /// Use instead: + /// ```ignore + /// if s.is_empty() { + /// .. + /// } + + /// if arr.is_empty() { + /// .. + /// } + /// ``` + pub COMPARISON_TO_EMPTY, + style, + "default lint description" +} + + +declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); impl<'tcx> LateLintPass<'tcx> for LenZero { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -221,6 +259,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> } check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to) + } else { + check_empty_expr(cx, span, method, lit, op) } } @@ -258,6 +298,48 @@ fn check_len( } } +fn check_empty_expr( + cx: &LateContext<'_>, + span: Span, + lit1: &Expr<'_>, + lit2: &Expr<'_>, + op: &str +) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + &format!("comparison to empty slice"), + &format!("using `{}is_empty` is clearer and more explicit", op), + format!( + "{}{}.is_empty()", + op, + snippet_with_applicability(cx, lit1.span, "_", &mut applicability) + ), + applicability, + ); + } +} + +fn is_empty_string(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(lit, _) = lit.node { + let lit = lit.as_str(); + return lit == ""; + } + } + false +} + +fn is_empty_array(expr: &Expr<'_>) -> bool { + if let ExprKind::Array(ref arr) = expr.kind { + return arr.is_empty(); + } + false +} + /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 75629e13a8e..3ad2e1e9d57 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,7 +171,6 @@ mod checked_conversions; mod cognitive_complexity; mod collapsible_if; mod comparison_chain; -mod comparison_to_empty; mod copies; mod copy_iterator; mod create_dir; @@ -524,7 +523,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, - &comparison_to_empty::COMPARISON_TO_EMPTY, + &len_zero::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -1141,7 +1140,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); - store.register_late_pass(|| box comparison_to_empty::ComparisonToEmpty); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1302,7 +1300,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1559,7 +1557,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), -- cgit 1.4.1-3-g733a5 From deecfb5d13325d05842fb610a7ef506be4ac6175 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:02:10 +0100 Subject: Add description to lint --- clippy_lints/src/len_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 75e9c5c973b..9eba3750d2a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -102,7 +102,7 @@ declare_clippy_lint! { /// ``` pub COMPARISON_TO_EMPTY, style, - "default lint description" + "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead" } -- cgit 1.4.1-3-g733a5 From 4cf5b5ff7018abc91d0d0a81c73b3405228811a9 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:21:34 +0100 Subject: Run update lints --- clippy_lints/src/lib.rs | 6 +++--- src/lintlist/mod.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3ad2e1e9d57..8dff5ae42c3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -523,7 +523,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, - &len_zero::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -609,6 +608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_const_arrays::LARGE_CONST_ARRAYS, &large_enum_variant::LARGE_ENUM_VARIANT, &large_stack_arrays::LARGE_STACK_ARRAYS, + &len_zero::COMPARISON_TO_EMPTY, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, &let_if_seq::USELESS_LET_IF_SEQ, @@ -1300,7 +1300,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1350,6 +1349,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&int_plus_one::INT_PLUS_ONE), LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), @@ -1557,7 +1557,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), @@ -1573,6 +1572,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&inherent_to_string::INHERENT_TO_STRING), + LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c2b2236bc66..695c7408e23 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -294,9 +294,9 @@ vec![ Lint { name: "comparison_to_empty", group: "style", - desc: "default lint description", + desc: "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead", deprecation: None, - module: "comparison_to_empty", + module: "len_zero", }, Lint { name: "copy_iterator", -- cgit 1.4.1-3-g733a5 From 4532dd9e431e3df719941514ef2a3dbce7fff962 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:31:13 +0100 Subject: run cargo fmt --- clippy_lints/src/len_zero.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 9eba3750d2a..542b4afcdf9 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -105,7 +105,6 @@ declare_clippy_lint! { "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead" } - declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); impl<'tcx> LateLintPass<'tcx> for LenZero { @@ -298,13 +297,7 @@ fn check_len( } } -fn check_empty_expr( - cx: &LateContext<'_>, - span: Span, - lit1: &Expr<'_>, - lit2: &Expr<'_>, - op: &str -) { +fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( -- cgit 1.4.1-3-g733a5 From 45aa3ef8dde9741c82bd3c98405d0b0464a8b79f Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 01:55:44 +0100 Subject: Remove unnecesary format --- clippy_lints/src/len_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 542b4afcdf9..d0e7dea4a54 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -304,7 +304,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex cx, COMPARISON_TO_EMPTY, span, - &format!("comparison to empty slice"), + "comparison to empty slice", &format!("using `{}is_empty` is clearer and more explicit", op), format!( "{}{}.is_empty()", -- cgit 1.4.1-3-g733a5 From f0cf3bdca198ead0e1d76115bf30a2eef72e8c58 Mon Sep 17 00:00:00 2001 From: HMPerson1 Date: Sun, 25 Oct 2020 21:20:57 -0400 Subject: 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 (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..287cf6bb725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1802,6 +1802,7 @@ Released 2018-09-13 [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..138d1ab9b50 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -697,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, + &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, @@ -1420,6 +1421,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OPTION_AS_REF_DEREF), @@ -1615,6 +1617,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OPTION_MAP_OR_NONE), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0824bacbc7..5f9bed90845 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1383,6 +1383,27 @@ declare_clippy_lint! { "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map(_).collect::()`. + /// + /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// (0..3).map(|t| Err(t)).collect::>(); + /// ``` + /// Use instead: + /// ```rust + /// (0..3).try_for_each(|t| Err(t)); + /// ``` + pub MAP_COLLECT_RESULT_UNIT, + style, + "using `.map(_).collect::()`, which can be replaced with `try_for_each`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1433,6 +1454,7 @@ declare_lint_pass!(Methods => [ FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, + MAP_COLLECT_RESULT_UNIT, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1515,6 +1537,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), + ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), _ => {}, } @@ -3501,6 +3524,42 @@ fn lint_option_as_ref_deref<'tcx>( } } +fn lint_map_collect( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + map_args: &[hir::Expr<'_>], + collect_args: &[hir::Expr<'_>], +) { + if_chain! { + // called on Iterator + if let [map_expr] = collect_args; + if match_trait_method(cx, map_expr, &paths::ITERATOR); + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, collect_ret_ty, sym!(result_type)); + if let ty::Adt(_, substs) = collect_ret_ty.kind(); + if let Some(result_t) = substs.types().next(); + if result_t.is_unit(); + // get parts for snippet + if let [iter, map_fn] = map_args; + then { + span_lint_and_sugg( + cx, + MAP_COLLECT_RESULT_UNIT, + expr.span, + "`.map().collect()` can be replaced with `.try_for_each()`", + "try this", + format!( + "{}.try_for_each({})", + snippet(cx, iter.span, ".."), + snippet(cx, map_fn.span, "..") + ), + Applicability::MachineApplicable, + ); + } + } +} + /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..d9b3e5c17f4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1222,6 +1222,13 @@ vec![ deprecation: None, module: "map_clone", }, + Lint { + name: "map_collect_result_unit", + group: "style", + desc: "using `.map(_).collect::()`, which can be replaced with `try_for_each`", + deprecation: None, + module: "methods", + }, Lint { name: "map_entry", group: "perf", diff --git a/tests/ui/map_collect_result_unit.fixed b/tests/ui/map_collect_result_unit.fixed new file mode 100644 index 00000000000..e66c9cc2420 --- /dev/null +++ b/tests/ui/map_collect_result_unit.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::map_collect_result_unit)] + +fn main() { + { + let _ = (0..3).try_for_each(|t| Err(t + 1)); + let _: Result<(), _> = (0..3).try_for_each(|t| Err(t + 1)); + + let _ = (0..3).try_for_each(|t| Err(t + 1)); + } +} + +fn _ignore() { + let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/tests/ui/map_collect_result_unit.rs b/tests/ui/map_collect_result_unit.rs new file mode 100644 index 00000000000..6f08f4c3c53 --- /dev/null +++ b/tests/ui/map_collect_result_unit.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::map_collect_result_unit)] + +fn main() { + { + let _ = (0..3).map(|t| Err(t + 1)).collect::>(); + let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); + + let _ = (0..3).try_for_each(|t| Err(t + 1)); + } +} + +fn _ignore() { + let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr new file mode 100644 index 00000000000..8b06e13baa6 --- /dev/null +++ b/tests/ui/map_collect_result_unit.stderr @@ -0,0 +1,16 @@ +error: `.map().collect()` can be replaced with `.try_for_each()` + --> $DIR/map_collect_result_unit.rs:6:17 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + | + = note: `-D clippy::map-collect-result-unit` implied by `-D warnings` + +error: `.map().collect()` can be replaced with `.try_for_each()` + --> $DIR/map_collect_result_unit.rs:7:32 + | +LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From bab338685f42e4b184c0f645c3a1a24a79890e17 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 24 Oct 2020 10:50:11 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 909e79f661a..308e92057b7 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -7,6 +7,7 @@ use rustc_hir::{ StmtKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::DesugaringKind; @@ -271,13 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { k: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, - _: Span, + span: Span, _: HirId, ) { if let FnKind::Closure(_) = k { // Does not apply to closures return; } + if in_external_macro(cx.tcx.sess, span) { + return; + } for arg in iter_input_pats(decl, body) { if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind { span_lint( @@ -293,13 +297,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if_chain! { + if !in_external_macro(cx.tcx.sess, stmt.span); if let StmtKind::Local(ref local) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; if let Some(ref init) = local.init; if !higher::is_from_for_desugar(local); then { if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { - let sugg_init = if init.span.from_expansion() { + // use the macro callsite when the init span (but not the whole local span) + // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` + let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, init, "..") } else { Sugg::hir(cx, init, "..") @@ -310,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", sugg_init.addr()) }; let tyopt = if let Some(ref ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_")) + format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) } else { String::new() }; @@ -326,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { "try", format!( "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, "_"), + name=snippet(cx, name.span, ".."), tyopt=tyopt, initref=initref, ), diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 0bbb9534928..93303865e17 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -56,3 +56,17 @@ macro_rules! option_env_unwrap_external { option_env!($env).expect($message) }; } + +#[macro_export] +macro_rules! ref_arg_binding { + () => { + let ref _y = 42; + }; +} + +#[macro_export] +macro_rules! ref_arg_function { + () => { + fn fun_example(ref _x: usize) {} + }; +} diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index 33605aca019..b129d95c560 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,7 +1,17 @@ // run-rustfix +// aux-build:macro_rules.rs #![warn(clippy::toplevel_ref_arg)] +#[macro_use] +extern crate macro_rules; + +macro_rules! gen_binding { + () => { + let _y = &42; + }; +} + fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -26,4 +36,15 @@ fn main() { // ok for ref _x in 0..10 {} + + // lint in macro + #[allow(unused)] + { + gen_binding!(); + } + + // do not lint in external macro + { + ref_arg_binding!(); + } } diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 59759f11893..73eb4ff7306 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,7 +1,17 @@ // run-rustfix +// aux-build:macro_rules.rs #![warn(clippy::toplevel_ref_arg)] +#[macro_use] +extern crate macro_rules; + +macro_rules! gen_binding { + () => { + let ref _y = 42; + }; +} + fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -26,4 +36,15 @@ fn main() { // ok for ref _x in 0..10 {} + + // lint in macro + #[allow(unused)] + { + gen_binding!(); + } + + // do not lint in external macro + { + ref_arg_binding!(); + } } diff --git a/tests/ui/toplevel_ref_arg.stderr b/tests/ui/toplevel_ref_arg.stderr index 19d69496709..15cb933fedc 100644 --- a/tests/ui/toplevel_ref_arg.stderr +++ b/tests/ui/toplevel_ref_arg.stderr @@ -1,5 +1,5 @@ error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:10:9 + --> $DIR/toplevel_ref_arg.rs:20:9 | LL | let ref _x = 1; | ----^^^^^^----- help: try: `let _x = &1;` @@ -7,28 +7,39 @@ LL | let ref _x = 1; = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:12:9 + --> $DIR/toplevel_ref_arg.rs:22:9 | LL | let ref _y: (&_, u8) = (&1, 2); | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:14:9 + --> $DIR/toplevel_ref_arg.rs:24:9 | LL | let ref _z = 1 + 2; | ----^^^^^^--------- help: try: `let _z = &(1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:16:9 + --> $DIR/toplevel_ref_arg.rs:26:9 | LL | let ref mut _z = 1 + 2; | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:21:9 + --> $DIR/toplevel_ref_arg.rs:31:9 | LL | let ref _x = vec![1, 2, 3]; | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];` -error: aborting due to 5 previous errors +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:11:13 + | +LL | let ref _y = 42; + | ----^^^^^^------ help: try: `let _y = &42;` +... +LL | gen_binding!(); + | --------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.rs b/tests/ui/toplevel_ref_arg_non_rustfix.rs index 42cac2ba4de..1a493fbce0e 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.rs +++ b/tests/ui/toplevel_ref_arg_non_rustfix.rs @@ -1,11 +1,33 @@ +// aux-build:macro_rules.rs + #![warn(clippy::toplevel_ref_arg)] #![allow(unused)] +#[macro_use] +extern crate macro_rules; + fn the_answer(ref mut x: u8) { *x = 42; } +macro_rules! gen_function { + () => { + fn fun_example(ref _x: usize) {} + }; +} + fn main() { let mut x = 0; the_answer(x); + + // lint in macro + #[allow(unused)] + { + gen_function!(); + } + + // do not lint in external macro + { + ref_arg_function!(); + } } diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 295e2f35608..6c36141a58c 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,10 +1,21 @@ error: `ref` directly on a function argument is ignored. Consider using a reference type instead. - --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15 + --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { | ^^^^^^^^^ | = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` -error: aborting due to previous error +error: `ref` directly on a function argument is ignored. Consider using a reference type instead. + --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24 + | +LL | fn fun_example(ref _x: usize) {} + | ^^^^^^ +... +LL | gen_function!(); + | ---------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 2911d9c7de4c0acf7bdd6a2d6983d1fccbeb6a69 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 18 Oct 2020 13:09:06 +0200 Subject: Use better placeholders for some methods lint messages --- clippy_lints/src/methods/mod.rs | 58 ++++++++++++------------ clippy_lints/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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0824bacbc7..fc77cd52b8f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1748,7 +1748,7 @@ fn lint_or_fun_call<'tcx>( "try this", format!( "{}.unwrap_or_default()", - snippet_with_applicability(cx, self_expr.span, "_", &mut applicability) + snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) ), applicability, ); @@ -2155,7 +2155,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; - let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); + let snippet = snippet_with_macro_callsite(cx, arg.span, ".."); span_lint_and_sugg( cx, @@ -2191,9 +2191,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E "try this", format!( "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0].span, "..", &mut applicability), ref_str, - snippet_with_applicability(cx, target.span, "_", &mut applicability) + snippet_with_applicability(cx, target.span, "..", &mut applicability) ), applicability, ); @@ -2460,7 +2460,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: let mut applicability = Applicability::MachineApplicable; let expr_ty = cx.typeck_results().expr_ty(&get_args[0]); let get_args_str = if get_args.len() > 1 { - snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability) + snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability) } else { return; // not linting on a .get().unwrap() chain or variant }; @@ -2520,7 +2520,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: format!( "{}{}[{}]", borrow_str, - snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability), + snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability), get_args_str ), applicability, @@ -2536,7 +2536,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[ cx, ITER_SKIP_NEXT, expr.span.trim_start(caller.span).unwrap(), - "called `skip(x).next()` on an iterator", + "called `skip(..).next()` on an iterator", "use `nth` instead", hint, Applicability::MachineApplicable, @@ -2739,11 +2739,11 @@ fn lint_map_unwrap_or_else<'tcx>( // lint message let msg = if is_option { - "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(g, f)` instead" + "called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling \ + `map_or_else(, )` instead" } else { - "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(g, f)` instead" + "called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(, )` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2809,8 +2809,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map if is_option { let self_snippet = snippet(cx, map_or_args[0].span, ".."); let func_snippet = snippet(cx, map_or_args[2].span, ".."); - let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \ - `and_then(f)` instead"; + let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ + `and_then(..)` instead"; ( OPTION_MAP_OR_NONE, msg, @@ -2848,8 +2848,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.filter().next()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(p)` instead."; + let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { // add note if not multi-line @@ -2879,9 +2879,9 @@ fn lint_skip_while_next<'tcx>( cx, SKIP_WHILE_NEXT, expr.span, - "called `skip_while(p).next()` on an `Iterator`", + "called `skip_while(

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

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

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

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

)` instead error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 3fec6f568daababf3520e2a818b4c1db79266b92 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 19 Oct 2020 18:47:02 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc77cd52b8f..d3e26a09ad4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,8 +32,7 @@ use crate::utils::{ is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, - SpanlessEq, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -2753,16 +2752,15 @@ fn lint_map_unwrap_or_else<'tcx>( let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); if same_span && !multiline { - span_lint_and_note( + let var_snippet = snippet(cx, map_args[0].span, ".."); + span_lint_and_sugg( cx, MAP_UNWRAP_OR, expr.span, msg, - None, - &format!( - "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`", - map_snippet, unwrap_snippet, - ), + "try this", + format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + Applicability::MachineApplicable, ); return true; } else if same_span && multiline { @@ -2852,14 +2850,16 @@ fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, fil `.find(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, filter_args[0].span, ".."); // add note if not multi-line - span_lint_and_note( + span_lint_and_sugg( cx, FILTER_NEXT, expr.span, msg, - None, - &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet), + "try this", + format!("{}.find({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, ); } else { span_lint(cx, FILTER_NEXT, expr.span, msg); @@ -2908,13 +2908,15 @@ fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { - span_lint_and_note( + let iter_snippet = snippet(cx, filter_args[0].span, ".."); + span_lint_and_sugg( cx, FILTER_MAP_NEXT, expr.span, msg, - None, - &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet), + "try this", + format!("{}.find_map({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, ); } else { span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index 01281ebaf6a..bcedf11e536 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -2,10 +2,9 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly --> $DIR/filter_map_next.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` - = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:10:26 diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 6fa2800e9bd..1975abb3e9f 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -109,9 +109,7 @@ LL | let _ = opt.map(|x| x + 1) | _____________^ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or_else(|| 0); - | |_____________________________^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:55:13 @@ -137,25 +135,19 @@ error: called `map().unwrap_or_else()` on a `Result` value. This can be do --> $DIR/map_unwrap_or.rs:88:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:90:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:91:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: aborting due to 13 previous errors diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 74d8533d4da..2df1941aaaa 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -12,10 +12,9 @@ error: called `filter(..).next()` on an `Iterator`. This is more succinctly expr --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` | = note: `-D clippy::filter-next` implied by `-D warnings` - = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:129:13 -- cgit 1.4.1-3-g733a5 From 213dbf7aacb01cc4d05d47f010833aa6e9c2a7d0 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 12 Oct 2020 23:58:59 +0200 Subject: clippy_lint: Add 'ref_option_ref' --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/ref_option_ref.rs | 65 ++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/ref_option_ref.rs | 5 +++ tests/ui/ref_option_ref.stderr | 10 ++++++ 6 files changed, 93 insertions(+) create mode 100644 clippy_lints/src/ref_option_ref.rs create mode 100644 tests/ui/ref_option_ref.rs create mode 100644 tests/ui/ref_option_ref.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..db834fe108b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1917,6 +1917,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..1ab36231758 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; +mod ref_option_ref; mod reference; mod regex; mod repeat_once; @@ -803,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + &ref_option_ref::REF_OPTION_REF, &reference::DEREF_ADDROF, &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, @@ -1024,6 +1026,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &sess.target, ); store.register_late_pass(move || box pass_by_ref_or_value); + store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); @@ -1493,6 +1496,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), @@ -1648,6 +1652,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs new file mode 100644 index 00000000000..fbee3263556 --- /dev/null +++ b/clippy_lints/src/ref_option_ref.rs @@ -0,0 +1,65 @@ +use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; +use rustc_hir::{GenericArg, Local, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use if_chain::if_chain; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `&Option<&T>`. + /// + /// **Why is this bad?** Since `&` is Copy, it's useless to have a + /// reference on `Option<&T>`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // example code where clippy issues a warning + /// let x: &Option<&u32> = &Some(&0u32); + /// ``` + /// Use instead: + /// ```rust,ignore + /// // example code which does not raise clippy warning + /// let x: Option<&u32> = Some(&0u32); + /// ``` + pub REF_OPTION_REF, + style, + "use `Option<&T>` instead of `&Option<&T>`" +} + +declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); + +impl<'tcx> LateLintPass<'tcx> for RefOptionRef { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + if_chain! { + if let Some(ref ty) = local.ty; + if let TyKind::Rptr(_, ref mut_ty) = ty.kind; + if mut_ty.mutbl == Mutability::Not; + if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; + if let Some(def_id) = cx.typeck_results().qpath_res(qpath, local.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::OPTION); + if let Some(ref params) = last_path_segment(qpath).args ; + if !params.parenthesized; + if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(inner_ty) => Some(inner_ty), + _ => None, + }); + if let TyKind::Rptr(_, _) = inner_ty.kind; + + then { + span_lint_and_sugg( + cx, + REF_OPTION_REF, + ty.span, + "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", + "try", + format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..b3bd1923d4e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2013,6 +2013,13 @@ vec![ deprecation: None, module: "reference", }, + Lint { + name: "ref_option_ref", + group: "style", + desc: "use `Option<&T>` instead of `&Option<&T>`", + deprecation: None, + module: "ref_option_ref", + }, Lint { name: "repeat_once", group: "complexity", diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs new file mode 100644 index 00000000000..7f05990c0a0 --- /dev/null +++ b/tests/ui/ref_option_ref.rs @@ -0,0 +1,5 @@ +#![warn(clippy::ref_option_ref)] + +fn main() { + let x: &Option<&u32> = &None; +} diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr new file mode 100644 index 00000000000..90bcaef7570 --- /dev/null +++ b/tests/ui/ref_option_ref.stderr @@ -0,0 +1,10 @@ +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:4:12 + | +LL | let x: &Option<&u32> = &None; + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + | + = note: `-D clippy::ref-option-ref` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From d1baa25f046c8067ee1c9fa9e134de3ca0f64834 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 14:16:33 +0200 Subject: clippy_lint: Add 'ref_option_ref' refactor code --- clippy_lints/src/ref_option_ref.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index fbee3263556..40ac2b3f4f1 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,5 +1,5 @@ use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; -use rustc_hir::{GenericArg, Local, Mutability, TyKind}; +use rustc_hir::{GenericArg, Local, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,12 +34,20 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + + if let Some(ref ty) = local.ty { + self.check_ref_option_ref(cx, ty); + } + } +} + +impl RefOptionRef { + fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if_chain! { - if let Some(ref ty) = local.ty; if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; - if let Some(def_id) = cx.typeck_results().qpath_res(qpath, local.hir_id).opt_def_id(); + if let Some(def_id) = cx.typeck_results().qpath_res(qpath, ty.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::OPTION); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; @@ -57,9 +65,9 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", "try", format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), - Applicability::MachineApplicable, + Applicability::Unspecified, ); } } } -} +} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From 469b2fc7811c14fa645dceadfb976fb4f4ef248a Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:13:54 +0200 Subject: clippy_lint: Add 'ref_option_ref' move to check_ty and add type alias test --- clippy_lints/src/ref_option_ref.rs | 16 ++++++++-------- tests/ui/ref_option_ref.rs | 4 ++++ tests/ui/ref_option_ref.stderr | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 40ac2b3f4f1..4b5bc93d8a1 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -33,11 +33,8 @@ declare_clippy_lint! { declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - - if let Some(ref ty) = local.ty { - self.check_ref_option_ref(cx, ty); - } + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + self.check_ref_option_ref(cx, ty); } } @@ -46,8 +43,11 @@ impl RefOptionRef { if_chain! { if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; - if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; - if let Some(def_id) = cx.typeck_results().qpath_res(qpath, ty.hir_id).opt_def_id(); + if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; + let last = last_path_segment(qpath); + if let Some(res) = last.res; + if let Some(def_id) = res.opt_def_id(); + if match_def_path(cx, def_id, &paths::OPTION); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; @@ -70,4 +70,4 @@ impl RefOptionRef { } } } -} \ No newline at end of file +} diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 7f05990c0a0..24db42efb80 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,5 +1,9 @@ +#![allow(unused)] #![warn(clippy::ref_option_ref)] +type OptRefU32<'a> = &'a Option<&'a u32>; +type OptRef<'a, T> = &'a Option<&'a T>; + fn main() { let x: &Option<&u32> = &None; } diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 90bcaef7570..26f4065d311 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,10 +1,22 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:4:12 + --> $DIR/ref_option_ref.rs:4:22 | -LL | let x: &Option<&u32> = &None; - | ^^^^^^^^^^^^^ help: try: `Option<&u32>` +LL | type OptRefU32<'a> = &'a Option<&'a u32>; + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` | = note: `-D clippy::ref-option-ref` implied by `-D warnings` -error: aborting due to previous error +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:5:22 + | +LL | type OptRef<'a, T> = &'a Option<&'a T>; + | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:8:12 + | +LL | let x: &Option<&u32> = &None; + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From c1f3bab6b19ee27bce5b8c4df0d8413da7728473 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:46:47 +0200 Subject: clippy_lint: Add 'ref_option_ref' remove unused import --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 4b5bc93d8a1..3b9465ec13d 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,5 +1,5 @@ use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; -use rustc_hir::{GenericArg, Local, Mutability, Ty, TyKind}; +use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -- cgit 1.4.1-3-g733a5 From b41b38cb7f16e1a622738aff591268083dca2e39 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 23:01:57 +0200 Subject: clippy_lint: Refactor 'ref_option_ref' --- clippy_lints/src/ref_option_ref.rs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 3b9465ec13d..d4b09848ab6 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -34,12 +34,6 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { - self.check_ref_option_ref(cx, ty); - } -} - -impl RefOptionRef { - fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if_chain! { if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; -- cgit 1.4.1-3-g733a5 From 8e26004a5ff2d16a7ad37723f5d284703cff2b94 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 19 Oct 2020 21:37:36 +0200 Subject: Update clippy_lints/src/ref_option_ref.rs doctest Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index d4b09848ab6..902cd06f527 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -17,12 +17,10 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust,ignore - /// // example code where clippy issues a warning /// let x: &Option<&u32> = &Some(&0u32); /// ``` /// Use instead: /// ```rust,ignore - /// // example code which does not raise clippy warning /// let x: Option<&u32> = Some(&0u32); /// ``` pub REF_OPTION_REF, -- cgit 1.4.1-3-g733a5 From 1566db704d113ea6ed90f2b3927306bd84946dc2 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 19 Oct 2020 21:38:36 +0200 Subject: Update clippy_lints/src/ref_option_ref.rs Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 902cd06f527..4ab2d8025e0 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -40,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(res) = last.res; if let Some(def_id) = res.opt_def_id(); - if match_def_path(cx, def_id, &paths::OPTION); + if cx.tcx.is_diagnostic_item(sym!(option_type), def_id); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { -- cgit 1.4.1-3-g733a5 From 7fd74c6bf676e535592f5baadd412bf7c98e078c Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 19 Oct 2020 22:01:37 +0200 Subject: clippy_lint: Add Known Problems message --- clippy_lints/src/ref_option_ref.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 4ab2d8025e0..715938df138 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,4 +1,4 @@ -use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; +use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -12,7 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** Since `&` is Copy, it's useless to have a /// reference on `Option<&T>`. /// - /// **Known problems:** None. + /// **Known problems:** It may be irrevelent to use this lint on + /// public API code as it will make a breaking change to apply it. /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From db40a07665f74e58fbf2f161dcd618f6945ec0e3 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 20 Oct 2020 01:23:39 +0200 Subject: Update clippy_lints/src/ref_option_ref.rs Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 715938df138..a83f11bf670 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// let x: Option<&u32> = Some(&0u32); /// ``` pub REF_OPTION_REF, - style, + pedantic, "use `Option<&T>` instead of `&Option<&T>`" } -- cgit 1.4.1-3-g733a5 From ffddb669e02e5f38620d839756bb2d02fff6c5e8 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 20 Oct 2020 07:11:25 +0200 Subject: clippy_lint: run after changing category to pendantic --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1ab36231758..cdfad908fc2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1255,6 +1255,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), @@ -1496,7 +1497,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), @@ -1652,7 +1652,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b3bd1923d4e..8398c132b33 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2015,7 +2015,7 @@ vec![ }, Lint { name: "ref_option_ref", - group: "style", + group: "pedantic", desc: "use `Option<&T>` instead of `&Option<&T>`", deprecation: None, module: "ref_option_ref", -- cgit 1.4.1-3-g733a5 From 8337c467e9968b91277f0c0ae113e2d4917ac55e Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 26 Oct 2020 22:33:48 +0100 Subject: Change Applicability to MaybeIncorrect Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index a83f11bf670..e8c60842e76 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", "try", format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), - Applicability::Unspecified, + Applicability::MaybeIncorrect, ); } } -- cgit 1.4.1-3-g733a5 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: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6a33aaaaab2..45f3bc3ea85 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, - TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, + BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, + ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, + StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -678,17 +678,30 @@ impl Types { // details. return; } + + // When trait objects or opaque types have lifetime or auto-trait bounds, + // we need to add parentheses to avoid a syntax error due to its ambiguity. + // Originally reported as the issue #3128. + let inner_snippet = snippet(cx, inner.span, ".."); + let suggestion = match &inner.kind { + TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => { + format!("&{}({})", ltopt, &inner_snippet) + }, + TyKind::Path(qpath) + if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) + .map_or(false, |bounds| bounds.len() > 1) => + { + format!("&{}({})", ltopt, &inner_snippet) + }, + _ => format!("&{}{}", ltopt, &inner_snippet), + }; span_lint_and_sugg( cx, BORROWED_BOX, hir_ty.span, "you seem to be trying to use `&Box`. Consider using just `&T`", "try", - format!( - "&{}{}", - ltopt, - &snippet(cx, inner.span, "..") - ), + suggestion, // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item // because the trait impls of it will break otherwise; // and there may be other cases that result in invalid code. @@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { false } +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { + if_chain! { + if let Some(did) = qpath_res(cx, qpath, id).opt_def_id(); + if let Some(node) = cx.tcx.hir().get_if_local(did); + if let Node::GenericParam(generic_param) = node; + if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; + if synthetic == Some(SyntheticTyParamKind::ImplTrait); + then { + Some(generic_param.bounds) + } else { + None + } + } +} + declare_clippy_lint! { /// **What it does:** Checks for binding a unit value. /// diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 1901de46ca8..b606f773cfb 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -3,6 +3,8 @@ #![allow(unused_variables)] #![allow(dead_code)] +use std::fmt::Display; + pub fn test1(foo: &mut Box) { // Although this function could be changed to "&mut bool", // avoiding the Box, mutable references to boxes are not @@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) { *boxed_slice = data.into_boxed_slice(); } +// The suggestion should include proper parentheses to avoid a syntax error. +pub fn test14(_display: &Box) {} +pub fn test15(_display: &Box) {} +pub fn test16<'a>(_display: &'a Box) {} + +pub fn test17(_display: &Box) {} +pub fn test18(_display: &Box) {} +pub fn test19<'a>(_display: &'a Box) {} + +// This exists only to check what happens when parentheses are already present. +// Even though the current implementation doesn't put extra parentheses, +// it's fine that unnecessary parentheses appear in the future for some reason. +pub fn test20(_display: &Box<(dyn Display + Send)>) {} + fn main() { test1(&mut Box::new(false)); test2(); diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index b5db691f89f..3eac32815be 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:19:14 + --> $DIR/borrow_box.rs:21:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:23:10 + --> $DIR/borrow_box.rs:25:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:27:17 + --> $DIR/borrow_box.rs:29:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` -error: aborting due to 3 previous errors +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:95:25 + | +LL | pub fn test14(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:96:25 + | +LL | pub fn test15(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:97:29 + | +LL | pub fn test16<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:99:25 + | +LL | pub fn test17(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:100:25 + | +LL | pub fn test18(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:101:29 + | +LL | pub fn test19<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:106:25 + | +LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 111b9023dad65721300a39c3cf337f6bfb96d5d3 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 27 Oct 2020 01:47:40 +0100 Subject: add manual_ok_or lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/manual_ok_or.rs | 97 ++++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/manual_ok_or.fixed | 40 +++++++++++++++++ tests/ui/manual_ok_or.rs | 44 ++++++++++++++++++ tests/ui/manual_ok_or.stderr | 41 +++++++++++++++++ 7 files changed, 234 insertions(+) create mode 100644 clippy_lints/src/manual_ok_or.rs create mode 100644 tests/ui/manual_ok_or.fixed create mode 100644 tests/ui/manual_ok_or.rs create mode 100644 tests/ui/manual_ok_or.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..cd884e0665d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1795,6 +1795,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..9b6f8e73454 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -233,6 +233,7 @@ mod macro_use; mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; +mod manual_ok_or; mod manual_strip; mod manual_unwrap_or; mod map_clone; @@ -645,6 +646,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &manual_ok_or::MANUAL_OK_OR, &manual_strip::MANUAL_STRIP, &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, @@ -1140,6 +1142,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); + store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1229,6 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&manual_ok_or::MANUAL_OK_OR), LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs new file mode 100644 index 00000000000..38298eb813a --- /dev/null +++ b/clippy_lints/src/manual_ok_or.rs @@ -0,0 +1,97 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that reimplement `Option::ok_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = None; + /// foo.ok_or("error"); + /// ``` + pub MANUAL_OK_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::ok_or`" +} + +declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); + +impl LateLintPass<'_> for ManualOkOr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { + if in_external_macro(cx.sess(), scrutinee.span) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind; + if method_segment.ident.name == sym!(map_or); + if args.len() == 3; + let method_receiver = &args[0]; + let ty = cx.typeck_results().expr_ty(method_receiver); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + let or_expr = &args[1]; + if is_ok_wrapping(cx, &args[2]); + if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; + if utils::match_qpath(err_path, &utils::paths::RESULT_ERR); + if let Some(method_receiver_snippet) = utils::snippet_opt(cx, method_receiver.span); + if let Some(err_arg_snippet) = utils::snippet_opt(cx, err_arg.span); + if let Some(indent) = utils::indent_of(cx, scrutinee.span); + then { + let reindented_err_arg_snippet = + utils::reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + utils::span_lint_and_sugg( + cx, + MANUAL_OK_OR, + scrutinee.span, + "this pattern reimplements `Option::ok_or`", + "replace with", + format!( + "{}.ok_or({})", + method_receiver_snippet, + reindented_err_arg_snippet + ), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { + if let ExprKind::Path(ref qpath) = map_expr.kind { + if utils::match_qpath(qpath, &utils::paths::RESULT_OK) { + return true; + } + } + if_chain! { + if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind; + let body = cx.tcx.hir().body(body_id); + if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; + if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; + if utils::match_qpath(ok_path, &utils::paths::RESULT_OK); + if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind; + if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res; + then { param_id == ok_arg_path_id } else { false } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..f438da00720 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1173,6 +1173,13 @@ vec![ deprecation: None, module: "manual_non_exhaustive", }, + Lint { + name: "manual_ok_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::ok_or`", + deprecation: None, + module: "manual_ok_or", + }, Lint { name: "manual_range_contains", group: "style", diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed new file mode 100644 index 00000000000..b42e94bd727 --- /dev/null +++ b/tests/ui/manual_ok_or.fixed @@ -0,0 +1,40 @@ +// run-rustfix +#![warn(clippy::manual_ok_or)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::redundant_closure)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +fn main() { + // basic case + let foo: Option = None; + foo.ok_or("error"); + + // eta expansion case + foo.ok_or("error"); + + // turbo fish syntax + None::.ok_or("error"); + + // multiline case + #[rustfmt::skip] + foo.ok_or(&format!( + "{}{}{}{}{}{}{}", + "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")); + + // not applicable, closure isn't direct `Ok` wrapping + foo.map_or(Err("error"), |v| Ok(v + 1)); + + // not applicable, or side isn't `Result::Err` + foo.map_or(Ok::(1), |v| Ok(v)); + + // not applicatble, expr is not a `Result` value + foo.map_or(42, |v| v); + + // TODO patterns not covered yet + match foo { + Some(v) => Ok(v), + None => Err("error"), + }; + foo.map_or_else(|| Err("error"), |v| Ok(v)); +} diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs new file mode 100644 index 00000000000..e5a6056fbf5 --- /dev/null +++ b/tests/ui/manual_ok_or.rs @@ -0,0 +1,44 @@ +// run-rustfix +#![warn(clippy::manual_ok_or)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::redundant_closure)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +fn main() { + // basic case + let foo: Option = None; + foo.map_or(Err("error"), |v| Ok(v)); + + // eta expansion case + foo.map_or(Err("error"), Ok); + + // turbo fish syntax + None::.map_or(Err("error"), |v| Ok(v)); + + // multiline case + #[rustfmt::skip] + foo.map_or(Err::( + &format!( + "{}{}{}{}{}{}{}", + "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer") + ), + |v| Ok(v), + ); + + // not applicable, closure isn't direct `Ok` wrapping + foo.map_or(Err("error"), |v| Ok(v + 1)); + + // not applicable, or side isn't `Result::Err` + foo.map_or(Ok::(1), |v| Ok(v)); + + // not applicatble, expr is not a `Result` value + foo.map_or(42, |v| v); + + // TODO patterns not covered yet + match foo { + Some(v) => Ok(v), + None => Err("error"), + }; + foo.map_or_else(|| Err("error"), |v| Ok(v)); +} diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr new file mode 100644 index 00000000000..8ea10ac5436 --- /dev/null +++ b/tests/ui/manual_ok_or.stderr @@ -0,0 +1,41 @@ +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:11:5 + | +LL | foo.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` + | + = note: `-D clippy::manual-ok-or` implied by `-D warnings` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:14:5 + | +LL | foo.map_or(Err("error"), Ok); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:17:5 + | +LL | None::.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:21:5 + | +LL | / foo.map_or(Err::( +LL | | &format!( +LL | | "{}{}{}{}{}{}{}", +LL | | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer") +LL | | ), +LL | | |v| Ok(v), +LL | | ); + | |_____^ + | +help: replace with + | +LL | foo.ok_or(&format!( +LL | "{}{}{}{}{}{}{}", +LL | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")); + | + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 27 Oct 2020 07:42:13 +0200 Subject: 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 (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..7c8cb90fe1c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -892,6 +892,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, + &utils::internal_lints::INVALID_PATHS, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, @@ -919,6 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; @@ -1280,6 +1282,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), + LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index bfe426a25eb..6ca72d895c8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,3 +1,4 @@ +use crate::consts::{constant_simple, Constant}; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, @@ -14,9 +15,11 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -229,6 +232,21 @@ declare_clippy_lint! { "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" } +declare_clippy_lint! { + /// **What it does:** + /// Checks the paths module for invalid paths. + /// + /// **Why is this bad?** + /// It indicates a bug in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** None. + pub INVALID_PATHS, + internal, + "invalid path" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -761,3 +779,64 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, path: &[&str]) -> bool { + if path_to_res(cx, path).is_some() { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + for lang_item in lang_items.items() { + if let Some(def_id) = lang_item { + let lang_item_path = cx.get_def_path(*def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*def_id) { + if child.ident.name == *item { + return true; + } + } + } + } + } + } + + false +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path"); + } + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8297b9d128d..a1ecca0961a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -268,6 +268,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { krate: *krate, index: CRATE_DEF_INDEX, }; + let mut current_item = None; let mut items = cx.tcx.item_children(krate); let mut path_it = path.iter().skip(1).peekable(); @@ -277,6 +278,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { None => return None, }; + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + if segment.is_empty() { + continue; + } + let result = SmallVec::<[_; 8]>::new(); for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { if item.ident.name.as_str() == *segment { @@ -284,10 +291,28 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { return Some(item.res); } + current_item = Some(item); items = cx.tcx.item_children(item.res.def_id()); break; } } + + // The segment isn't a child_item. + // Try to find it under an inherent impl. + if_chain! { + if path_it.peek().is_none(); + if let Some(current_item) = current_item; + let item_def_id = current_item.res.def_id(); + if cx.tcx.def_kind(item_def_id) == DefKind::Struct; + then { + // Bad `find_map` suggestion. See #4193. + #[allow(clippy::find_map)] + return cx.tcx.inherent_impls(item_def_id).iter() + .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id)) + .find(|item| item.ident.name.as_str() == *segment) + .map(|item| item.res); + } + } } } else { None diff --git a/tests/ui/invalid_paths.rs b/tests/ui/invalid_paths.rs new file mode 100644 index 00000000000..01e28ae5e9d --- /dev/null +++ b/tests/ui/invalid_paths.rs @@ -0,0 +1,23 @@ +#![warn(clippy::internal)] + +mod paths { + // Good path + pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; + + // Path to method on inherent impl of a primitive type + pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; + + // Path to method on inherent impl + pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; + + // Path with empty segment + pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + + // Path with bad crate + pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + + // Path with bad module + pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; +} + +fn main() {} diff --git a/tests/ui/invalid_paths.stderr b/tests/ui/invalid_paths.stderr new file mode 100644 index 00000000000..bd69d661b71 --- /dev/null +++ b/tests/ui/invalid_paths.stderr @@ -0,0 +1,16 @@ +error: invalid path + --> $DIR/invalid_paths.rs:17:5 + | +LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::clippy-lints-internal` implied by `-D warnings` + +error: invalid path + --> $DIR/invalid_paths.rs:20:5 + | +LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From f79c4afd3a5c408bd9253311b224773702a912df Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 27 Oct 2020 07:43:38 +0200 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index bf8e030cc29..c75efc6e99f 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; @@ -193,10 +193,9 @@ fn check_hash_peq<'tcx>( hash_is_automatically_derived: bool, ) { if_chain! { - if match_path(&trait_ref.path, &paths::HASH); if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait(); - if let Some(def_id) = &trait_ref.trait_def_id(); - if !def_id.is_local(); + if let Some(def_id) = trait_ref.trait_def_id(); + if match_def_path(cx, def_id, &paths::HASH); then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { @@ -352,7 +351,8 @@ fn check_unsafe_derive_deserialize<'tcx>( } if_chain! { - if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); + if let Some(trait_def_id) = trait_ref.trait_def_id(); + if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE); if let ty::Adt(def, _) = ty.kind(); if let Some(local_def_id) = def.did.as_local(); let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index 69818b4d3c6..c1c08597ee6 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,7 +1,8 @@ -use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; +use crate::utils::{match_def_path, paths, span_lint_and_then, sugg}; use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -76,7 +77,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { // right hand side matches either f32::EPSILON or f64::EPSILON if let ExprKind::Path(ref epsilon_path) = rhs.kind; - if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); + if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id); + if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON); // values of the substractions on the left hand side are of the type float let t_val_l = cx.typeck_results().expr_ty(val_l); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd9b92efe58..d5a0e0d1f29 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -32,10 +32,10 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; -pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; -pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"]; -pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"]; +pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; +pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; @@ -47,7 +47,7 @@ pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -pub const HASH: [&str; 2] = ["hash", "Hash"]; +pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; @@ -58,7 +58,7 @@ pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "Into pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; -pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"]; +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; @@ -86,8 +86,8 @@ pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; -pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; -pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; +pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; @@ -107,7 +107,7 @@ pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; -pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"]; +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; -- cgit 1.4.1-3-g733a5 From 09e70536075fd92506ef985c5e662e1b2be3dd3d Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 24 Oct 2020 18:05:02 +0300 Subject: simplify SpanlessEq::eq_path_segment --- clippy_lints/src/utils/hir_utils.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index c9e639e8728..e4ad105c351 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -261,14 +261,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { // The == of idents doesn't work with different contexts, // we have to be explicit about hygiene - if left.ident.as_str() != right.ident.as_str() { - return false; - } - match (&left.args, &right.args) { - (&None, &None) => true, - (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r), - _ => false, - } + left.ident.as_str() == right.ident.as_str() + && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { -- cgit 1.4.1-3-g733a5 From 2b7dd313685279fce06f86bd739d798792135331 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 24 Oct 2020 18:06:07 +0300 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 6c969c3ead0..46ce92ea6d7 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,13 +1,8 @@ -use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; +use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; +use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note}; +use rustc_hir::{Block, Expr}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; -use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -108,48 +103,7 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_clippy_lint! { - /// **What it does:** Checks for `match` with identical arm bodies. - /// - /// **Why is this bad?** This is probably a copy & paste error. If arm bodies - /// are the same on purpose, you can factor them - /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). - /// - /// **Known problems:** False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// - /// **Example:** - /// ```rust,ignore - /// match foo { - /// Bar => bar(), - /// Quz => quz(), - /// Baz => bar(), // <= oops - /// } - /// ``` - /// - /// This should probably be - /// ```rust,ignore - /// match foo { - /// Bar => bar(), - /// Quz => quz(), - /// Baz => baz(), // <= fixed - /// } - /// ``` - /// - /// or if the original code was not a typo: - /// ```rust,ignore - /// match foo { - /// Bar | Baz => bar(), // <= shows the intent better - /// Quz => quz(), - /// } - /// ``` - pub MATCH_SAME_ARMS, - pedantic, - "`match` with identical arm bodies" -} - -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]); +declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -167,7 +121,6 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { lint_same_then_else(cx, &blocks); lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); - lint_match_arms(cx, expr); } } } @@ -243,122 +196,6 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { } } -/// Implementation of `MATCH_SAME_ARMS`. -fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { - fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { - lhs.len() == rhs.len() - && lhs - .iter() - .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) - } - - if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(&arm.body); - h.finish() - }; - - let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { - let min_index = usize::min(lindex, rindex); - let max_index = usize::max(lindex, rindex); - - // Arms with a guard are ignored, those can’t always be merged together - // This is also the case for arms in-between each there is an arm with a guard - (min_index..=max_index).all(|index| arms[index].guard.is_none()) && - SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && - // all patterns should have the same bindings - same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) - }; - - let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { - span_lint_and_then( - cx, - MATCH_SAME_ARMS, - j.body.span, - "this `match` has identical arm bodies", - |diag| { - diag.span_note(i.body.span, "same as this"); - - // Note: this does not use `span_suggestion` on purpose: - // there is no clean way - // to remove the other arm. Building a span and suggest to replace it to "" - // makes an even more confusing error message. Also in order not to make up a - // span for the whole pattern, the suggestion is only shown when there is only - // one pattern. The user should know about `|` if they are already using it… - - let lhs = snippet(cx, i.pat.span, ""); - let rhs = snippet(cx, j.pat.span, ""); - - if let PatKind::Wild = j.pat.kind { - // if the last arm is _, then i could be integrated into _ - // note that i.pat cannot be _, because that would mean that we're - // hiding all the subsequent arms, and rust won't compile - diag.span_note( - i.body.span, - &format!( - "`{}` has the same arm body as the `_` wildcard, consider removing it", - lhs - ), - ); - } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); - } - }, - ); - } - } -} - -/// Returns the list of bindings in a pattern. -fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap> { - fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { - match pat.kind { - PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), - PatKind::TupleStruct(_, pats, _) => { - for pat in pats { - bindings_impl(cx, pat, map); - } - }, - PatKind::Binding(.., ident, ref as_pat) => { - if let Entry::Vacant(v) = map.entry(ident.name) { - v.insert(cx.typeck_results().pat_ty(pat)); - } - if let Some(ref as_pat) = *as_pat { - bindings_impl(cx, as_pat, map); - } - }, - PatKind::Or(fields) | PatKind::Tuple(fields, _) => { - for pat in fields { - bindings_impl(cx, pat, map); - } - }, - PatKind::Struct(_, fields, _) => { - for pat in fields { - bindings_impl(cx, &pat.pat, map); - } - }, - PatKind::Slice(lhs, ref mid, rhs) => { - for pat in lhs { - bindings_impl(cx, pat, map); - } - if let Some(ref mid) = *mid { - bindings_impl(cx, mid, map); - } - for pat in rhs { - bindings_impl(cx, pat, map); - } - }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), - } - } - - let mut result = FxHashMap::default(); - bindings_impl(cx, pat, &mut result); - result -} - fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> where Eq: Fn(&T, &T) -> bool, @@ -370,47 +207,3 @@ where } None } - -fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)> -where - Eq: Fn(&T, &T) -> bool, -{ - if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { - Some((&exprs[0], &exprs[1])) - } else { - None - } -} - -fn search_same(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> -where - Hash: Fn(&T) -> u64, - Eq: Fn(&T, &T) -> bool, -{ - if let Some(expr) = search_common_cases(&exprs, &eq) { - return vec![expr]; - } - - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: FxHashMap<_, Vec<&_>> = - FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); - - for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } - } - o.get_mut().push(expr); - }, - Entry::Vacant(v) => { - v.insert(vec![expr]); - }, - } - } - - match_expr_list -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7c8cb90fe1c..459fdd70443 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -528,7 +528,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &comparison_chain::COMPARISON_CHAIN, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, - &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, @@ -659,6 +658,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::MATCH_LIKE_MATCHES_MACRO, &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, + &matches::MATCH_SAME_ARMS, &matches::MATCH_SINGLE_BINDING, &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, @@ -1204,7 +1204,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), - LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), @@ -1234,6 +1233,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), + LintId::of(&matches::MATCH_SAME_ARMS), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH_ELSE), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d93433c607f..4bdfca1a292 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1,5 +1,4 @@ use crate::consts::{constant, miri_to_const, Constant}; -use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ @@ -8,8 +7,10 @@ use crate::utils::{ snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; +use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ @@ -18,10 +19,12 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; +use rustc_span::Symbol; use std::cmp::Ordering; +use std::collections::hash_map::Entry; use std::collections::Bound; declare_clippy_lint! { @@ -475,6 +478,47 @@ declare_clippy_lint! { "a match that could be written with the matches! macro" } +declare_clippy_lint! { + /// **What it does:** Checks for `match` with identical arm bodies. + /// + /// **Why is this bad?** This is probably a copy & paste error. If arm bodies + /// are the same on purpose, you can factor them + /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). + /// + /// **Known problems:** False positive possible with order dependent `match` + /// (see issue + /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). + /// + /// **Example:** + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => bar(), // <= oops + /// } + /// ``` + /// + /// This should probably be + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => baz(), // <= fixed + /// } + /// ``` + /// + /// or if the original code was not a typo: + /// ```rust,ignore + /// match foo { + /// Bar | Baz => bar(), // <= shows the intent better + /// Quz => quz(), + /// } + /// ``` + pub MATCH_SAME_ARMS, + pedantic, + "`match` with identical arm bodies" +} + #[derive(Default)] pub struct Matches { infallible_destructuring_match_linted: bool, @@ -495,7 +539,8 @@ impl_lint_pass!(Matches => [ INFALLIBLE_DESTRUCTURING_MATCH, REST_PAT_IN_FULLY_BOUND_STRUCTS, REDUNDANT_PATTERN_MATCHING, - MATCH_LIKE_MATCHES_MACRO + MATCH_LIKE_MATCHES_MACRO, + MATCH_SAME_ARMS, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -505,7 +550,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check(cx, expr); - check_match_like_matches(cx, expr); + if !check_match_like_matches(cx, expr) { + lint_match_arms(cx, expr); + } if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); @@ -1063,32 +1110,47 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { } /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` -fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), - _ => return, + _ => false, } + } else { + false } } /// Lint a `match` or desugared `if let` for replacement by `matches!` -fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) { +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool { if_chain! { - if arms.len() == 2; + if arms.len() >= 2; if cx.typeck_results().expr_ty(expr).is_bool(); - if is_wild(&arms[1].pat); - if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); - if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); - if first != second; + if let Some((b1_arm, b0_arms)) = arms.split_last(); + if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared); + if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared); + if is_wild(&b1_arm.pat); + if b0 != b1; + let if_guard = &b0_arms[0].guard; + if if_guard.is_none() || b0_arms.len() == 1; + if b0_arms[1..].iter() + .all(|arm| { + find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && + arm.guard.is_none() + }); then { let mut applicability = Applicability::MachineApplicable; - - let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard { - format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability)) + let pat = { + use itertools::Itertools as _; + b0_arms.iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) + .join(" | ") + }; + let pat_and_guard = if let Some(Guard::If(g)) = if_guard { + format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { - format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability)) + pat }; span_lint_and_sugg( cx, @@ -1098,12 +1160,15 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr "try this", format!( "{}matches!({}, {})", - if first { "" } else { "!" }, + if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex.span, "..", &mut applicability), pat_and_guard, ), applicability, - ) + ); + true + } else { + false } } } @@ -1657,3 +1722,119 @@ fn test_overlapping() { ],) ); } + +/// Implementation of `MATCH_SAME_ARMS`. +fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { + fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { + lhs.len() == rhs.len() + && lhs + .iter() + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) + } + + if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_expr(&arm.body); + h.finish() + }; + + let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { + let min_index = usize::min(lindex, rindex); + let max_index = usize::max(lindex, rindex); + + // Arms with a guard are ignored, those can’t always be merged together + // This is also the case for arms in-between each there is an arm with a guard + (min_index..=max_index).all(|index| arms[index].guard.is_none()) && + SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && + // all patterns should have the same bindings + same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) + }; + + let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); + for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + j.body.span, + "this `match` has identical arm bodies", + |diag| { + diag.span_note(i.body.span, "same as this"); + + // Note: this does not use `span_suggestion` on purpose: + // there is no clean way + // to remove the other arm. Building a span and suggest to replace it to "" + // makes an even more confusing error message. Also in order not to make up a + // span for the whole pattern, the suggestion is only shown when there is only + // one pattern. The user should know about `|` if they are already using it… + + let lhs = snippet(cx, i.pat.span, ""); + let rhs = snippet(cx, j.pat.span, ""); + + if let PatKind::Wild = j.pat.kind { + // if the last arm is _, then i could be integrated into _ + // note that i.pat cannot be _, because that would mean that we're + // hiding all the subsequent arms, and rust won't compile + diag.span_note( + i.body.span, + &format!( + "`{}` has the same arm body as the `_` wildcard, consider removing it", + lhs + ), + ); + } else { + diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); + } + }, + ); + } + } +} + +/// Returns the list of bindings in a pattern. +fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap> { + fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { + match pat.kind { + PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), + PatKind::TupleStruct(_, pats, _) => { + for pat in pats { + bindings_impl(cx, pat, map); + } + }, + PatKind::Binding(.., ident, ref as_pat) => { + if let Entry::Vacant(v) = map.entry(ident.name) { + v.insert(cx.typeck_results().pat_ty(pat)); + } + if let Some(ref as_pat) = *as_pat { + bindings_impl(cx, as_pat, map); + } + }, + PatKind::Or(fields) | PatKind::Tuple(fields, _) => { + for pat in fields { + bindings_impl(cx, pat, map); + } + }, + PatKind::Struct(_, fields, _) => { + for pat in fields { + bindings_impl(cx, &pat.pat, map); + } + }, + PatKind::Slice(lhs, ref mid, rhs) => { + for pat in lhs { + bindings_impl(cx, pat, map); + } + if let Some(ref mid) = *mid { + bindings_impl(cx, mid, map); + } + for pat in rhs { + bindings_impl(cx, pat, map); + } + }, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), + } + } + + let mut result = FxHashMap::default(); + bindings_impl(cx, pat, &mut result); + result +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a1ecca0961a..0a8a4a5f9ae 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -27,11 +27,14 @@ pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; +use std::collections::hash_map::Entry; +use std::hash::BuildHasherDefault; use std::mem; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -1465,6 +1468,41 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> +where + Hash: Fn(&T) -> u64, + Eq: Fn(&T, &T) -> bool, +{ + if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { + return vec![(&exprs[0], &exprs[1])]; + } + + let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); + + let mut map: FxHashMap<_, Vec<&_>> = + FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + + for expr in exprs { + match map.entry(hash(expr)) { + Entry::Occupied(mut o) => { + for o in o.get() { + if eq(o, expr) { + match_expr_list.push((o, expr)); + } + } + o.get_mut().push(expr); + }, + Entry::Vacant(v) => { + v.insert(vec![expr]); + }, + } + } + + match_expr_list +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..aba436e5c02 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1304,7 +1304,7 @@ vec![ group: "pedantic", desc: "`match` with identical arm bodies", deprecation: None, - module: "copies", + module: "matches", }, Lint { name: "match_single_binding", diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index f3e19092480..7f4ebf56673 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns)] +#![allow(unreachable_patterns, dead_code)] fn main() { let x = Some(5); @@ -33,4 +33,70 @@ fn main() { _ => true, None => false, }; + + enum E { + A(u32), + B(i32), + C, + D, + }; + let x = E::A(2); + { + // lint + let _ans = matches!(x, E::A(_) | E::B(_)); + } + { + // lint + let _ans = !matches!(x, E::B(_) | E::C); + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => false, + E::C => true, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => true, + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) if a < 10 => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) => a == 10, + E::B(_) => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => true, + _ => false, + }; + } } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index fbae7c18b92..aee56dd4a5e 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns)] +#![allow(unreachable_patterns, dead_code)] fn main() { let x = Some(5); @@ -45,4 +45,78 @@ fn main() { _ => true, None => false, }; + + enum E { + A(u32), + B(i32), + C, + D, + }; + let x = E::A(2); + { + // lint + let _ans = match x { + E::A(_) => true, + E::B(_) => true, + _ => false, + }; + } + { + // lint + let _ans = match x { + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => false, + E::C => true, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => true, + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) if a < 10 => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) => a == 10, + E::B(_) => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => true, + _ => false, + }; + } } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 4668f8565a6..c52e41c7889 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -48,5 +48,27 @@ error: if let .. else expression looks like `matches!` macro LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` -error: aborting due to 5 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:58:20 + | +LL | let _ans = match x { + | ____________________^ +LL | | E::A(_) => true, +LL | | E::B(_) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:66:20 + | +LL | let _ans = match x { + | ____________________^ +LL | | E::B(_) => false, +LL | | E::C => false, +LL | | _ => true, +LL | | }; + | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` + +error: aborting due to 7 previous errors diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index e1401d2796a..06d91497242 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -119,6 +119,22 @@ fn match_same_arms() { unreachable!(); }, } + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; } fn main() {} diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 26c65f32ad7..fccaf805616 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -141,5 +141,18 @@ LL | Ok(3) => println!("ok"), | ^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 7 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_same_arms2.rs:133:16 + | +LL | let _ans = match x { + | ________________^ +LL | | E::A => false, +LL | | E::B => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `!matches!(x, E::A | E::B)` + | + = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 4c58860cbfe51049d48f4f31f76dcc18b39cfe98 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 27 Oct 2020 23:18:08 +0900 Subject: Fix suggestion to add unneeded space in `unused_unit` --- clippy_lints/src/unused_unit.rs | 11 +++++++ tests/ui/unused_unit.fixed | 31 ++++++++++++------- tests/ui/unused_unit.rs | 9 ++++++ tests/ui/unused_unit.stderr | 68 ++++++++++++++++++++++++++--------------- 4 files changed, 83 insertions(+), 36 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 7548c6afa97..b1339c3d639 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -123,6 +123,17 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { fn_source .rfind("->") .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + let mut rpos = rpos; + let chars: Vec = fn_source.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } ( #[allow(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 07f2791786d..7afc5361356 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -15,35 +15,35 @@ struct Unitter; impl Unitter { #[allow(clippy::no_effect)] - pub fn get_unit(&self, f: F, _g: G) - where G: Fn() { - let _y: &dyn Fn() = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } impl Into<()> for Unitter { #[rustfmt::skip] - fn into(self) { + fn into(self) { } } trait Trait { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() ; + G: FnMut(), + H: Fn(); } impl Trait for Unitter { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() {} + G: FnMut(), + H: Fn() {} } -fn return_unit() { } +fn return_unit() { } #[allow(clippy::needless_return)] #[allow(clippy::never_loop)] @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test(){} + +#[rustfmt::skip] +fn test2(){} + +#[rustfmt::skip] +fn test3(){} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index e2c6afb020f..96cef1ed5a5 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test()->(){} + +#[rustfmt::skip] +fn test2() ->(){} + +#[rustfmt::skip] +fn test3()-> (){} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 81e6738e6bf..c45634c2b6d 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:29 + --> $DIR/unused_unit.rs:18:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -11,28 +11,28 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:19 + --> $DIR/unused_unit.rs:19:18 | LL | where G: Fn() -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:59 + --> $DIR/unused_unit.rs:18:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:27 + --> $DIR/unused_unit.rs:20:26 | LL | let _y: &dyn Fn() -> () = &f; - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:19 + --> $DIR/unused_unit.rs:27:18 | LL | fn into(self) -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:28:9 @@ -41,46 +41,46 @@ LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:30 + --> $DIR/unused_unit.rs:33:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:35:20 + --> $DIR/unused_unit.rs:35:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:17 + --> $DIR/unused_unit.rs:36:16 | LL | H: Fn() -> (); - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:30 + --> $DIR/unused_unit.rs:40:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:42:20 + --> $DIR/unused_unit.rs:42:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:17 + --> $DIR/unused_unit.rs:43:16 | LL | H: Fn() -> () {} - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:18 + --> $DIR/unused_unit.rs:46:17 | LL | fn return_unit() -> () { () } - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:46:26 @@ -100,5 +100,23 @@ error: unneeded `()` LL | return(); | ^^ help: remove the `()` -error: aborting due to 16 previous errors +error: unneeded unit return type + --> $DIR/unused_unit.rs:75:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:78:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:81:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From a50d9e7af641d52e066b111014993b042b0be5a7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 28 Oct 2020 22:32:13 +0100 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index d10aefa2042..aa47930b520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1962,6 +1962,7 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index c5884361dff..461c6e31d3e 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -172,3 +172,12 @@ declare_deprecated_lint! { pub DROP_BOUNDS, "this lint has been uplifted to rustc and is now called `drop_bounds`" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been uplifted to rustc and is now called + /// `temporary_cstring_as_ptr`. + pub TEMPORARY_CSTRING_AS_PTR, + "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb949345945..2d374969846 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -488,6 +488,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::drop_bounds", "this lint has been uplifted to rustc and is now called `drop_bounds`", ); + store.register_removed( + "clippy::temporary_cstring_as_ptr", + "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 9e32fe36ece..56755596c97 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -9,5 +9,6 @@ #[warn(clippy::unused_label)] #[warn(clippy::regex_macro)] #[warn(clippy::drop_bounds)] +#[warn(clippy::temporary_cstring_as_ptr)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index d3400a7be09..37b726fc00f 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -66,11 +66,17 @@ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted LL | #[warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`` + --> $DIR/deprecated.rs:12:8 + | +LL | #[warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From e97602e4825fd5a01834d7b0e218703dcc82c091 Mon Sep 17 00:00:00 2001 From: henil Date: Mon, 26 Oct 2020 19:28:22 +0530 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index e84481f9b53..b1cdbab6de9 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -88,9 +88,33 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_span = Some(expr.span); - } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + match op.node { + hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { + hir::ExprKind::Lit(lit) => { + if let rustc_ast::ast::LitKind::Int(0, _) = lit.node { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } + }, + hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => { + if let hir::ExprKind::Lit(lit) = &expr.kind { + if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } + } + }, + _ => { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + }, + }, + _ => { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + }, + } + } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_span = Some(expr.span); } diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs index 7b1b64f390a..b74c93dc4a6 100644 --- a/tests/ui/integer_arithmetic.rs +++ b/tests/ui/integer_arithmetic.rs @@ -11,6 +11,8 @@ #[rustfmt::skip] fn main() { let mut i = 1i32; + let mut var1 = 0i32; + let mut var2 = -1i32; 1 + i; i * 2; 1 % @@ -32,7 +34,15 @@ fn main() { i -= 1; i *= 2; i /= 2; + i /= 0; + i /= -1; + i /= var1; + i /= var2; i %= 2; + i %= 0; + i %= -1; + i %= var1; + i %= var2; i <<= 3; i >>= 2; diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index 83e8a9cde3f..20356afc358 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -1,5 +1,19 @@ +error: this operation will panic at runtime + --> $DIR/integer_arithmetic.rs:37:5 + | +LL | i /= 0; + | ^^^^^^ attempt to divide `_` by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/integer_arithmetic.rs:42:5 + | +LL | i %= 0; + | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero + error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:14:5 + --> $DIR/integer_arithmetic.rs:16:5 | LL | 1 + i; | ^^^^^ @@ -7,125 +21,161 @@ LL | 1 + i; = note: `-D clippy::integer-arithmetic` implied by `-D warnings` error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:15:5 + --> $DIR/integer_arithmetic.rs:17:5 | LL | i * 2; | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:16:5 + --> $DIR/integer_arithmetic.rs:18:5 | LL | / 1 % LL | | i / 2; // no error, this is part of the expression in the preceding line - | |_________^ + | |_____^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:18:5 + --> $DIR/integer_arithmetic.rs:20:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:19:5 + --> $DIR/integer_arithmetic.rs:21:5 | LL | -i; | ^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:20:5 + --> $DIR/integer_arithmetic.rs:22:5 | LL | i >> 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:21:5 + --> $DIR/integer_arithmetic.rs:23:5 | LL | i << 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:31:5 + --> $DIR/integer_arithmetic.rs:33:5 | LL | i += 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:32:5 + --> $DIR/integer_arithmetic.rs:34:5 | LL | i -= 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:33:5 + --> $DIR/integer_arithmetic.rs:35:5 | LL | i *= 2; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:34:5 + --> $DIR/integer_arithmetic.rs:37:5 | -LL | i /= 2; +LL | i /= 0; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:35:5 + --> $DIR/integer_arithmetic.rs:38:11 + | +LL | i /= -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 %= 2; +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 -- cgit 1.4.1-3-g733a5 From e3de544c22feb01437a060e88249652c103cb612 Mon Sep 17 00:00:00 2001 From: Christian Nielsen Date: Thu, 29 Oct 2020 15:49:42 +0100 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index d0e7dea4a54..8e2f03d6e4e 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -85,7 +85,7 @@ declare_clippy_lint! { /// if s == "" { /// .. /// } - + /// /// if arr == [] { /// .. /// } @@ -95,7 +95,7 @@ declare_clippy_lint! { /// if s.is_empty() { /// .. /// } - + /// /// if arr.is_empty() { /// .. /// } -- cgit 1.4.1-3-g733a5 From 230d9cbe36b809d45efb37092b102bc5d3b350d8 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:39:19 +0100 Subject: Update clippy_lints/src/ref_option_ref.rs Co-authored-by: Philipp Krones --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index e8c60842e76..a914a77d48b 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { cx, REF_OPTION_REF, ty.span, - "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", + "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", "try", format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), Applicability::MaybeIncorrect, -- cgit 1.4.1-3-g733a5 From d780f61d7b3febdf0808dfef9abe80e95f2d46f8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 29 Oct 2020 19:08:54 +0100 Subject: add manual_ok_or / pr remarks --- clippy_lints/src/manual_ok_or.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index 38298eb813a..c99d2e35b94 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,4 +1,6 @@ -use crate::utils; +use crate::utils::{ + indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, PatKind, QPath}; @@ -49,18 +51,18 @@ impl LateLintPass<'_> for ManualOkOr { if args.len() == 3; let method_receiver = &args[0]; let ty = cx.typeck_results().expr_ty(method_receiver); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if is_type_diagnostic_item(cx, ty, sym!(option_type)); let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; - if utils::match_qpath(err_path, &utils::paths::RESULT_ERR); - if let Some(method_receiver_snippet) = utils::snippet_opt(cx, method_receiver.span); - if let Some(err_arg_snippet) = utils::snippet_opt(cx, err_arg.span); - if let Some(indent) = utils::indent_of(cx, scrutinee.span); + if match_qpath(err_path, &paths::RESULT_ERR); + if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span); + if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); + if let Some(indent) = indent_of(cx, scrutinee.span); then { let reindented_err_arg_snippet = - utils::reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); - utils::span_lint_and_sugg( + reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + span_lint_and_sugg( cx, MANUAL_OK_OR, scrutinee.span, @@ -80,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr { fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { if let ExprKind::Path(ref qpath) = map_expr.kind { - if utils::match_qpath(qpath, &utils::paths::RESULT_OK) { + if match_qpath(qpath, &paths::RESULT_OK) { return true; } } @@ -89,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir().body(body_id); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if utils::match_qpath(ok_path, &utils::paths::RESULT_OK); + if match_qpath(ok_path, &paths::RESULT_OK); if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind; if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res; then { param_id == ok_arg_path_id } else { false } -- cgit 1.4.1-3-g733a5 From fa0a78b130d52d4504eefbd16689b140e05fce59 Mon Sep 17 00:00:00 2001 From: henil Date: Fri, 30 Oct 2020 17:11:44 +0530 Subject: 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(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index b1cdbab6de9..9861d8cfc4e 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -90,12 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { match op.node { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { - hir::ExprKind::Lit(lit) => { - if let rustc_ast::ast::LitKind::Int(0, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_span = Some(expr.span); - } - }, + hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => { if let hir::ExprKind::Lit(lit) = &expr.kind { if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index 20356afc358..add3b6b90fa 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -75,12 +75,6 @@ error: integer arithmetic detected LL | i *= 2; | ^^^^^^ -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:37:5 - | -LL | i /= 0; - | ^^^^^^ - error: integer arithmetic detected --> $DIR/integer_arithmetic.rs:38:11 | @@ -99,12 +93,6 @@ error: integer arithmetic detected LL | i /= var2; | ^^^^^^^^^ -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:42:5 - | -LL | i %= 0; - | ^^^^^^ - error: integer arithmetic detected --> $DIR/integer_arithmetic.rs:43:11 | @@ -177,5 +165,5 @@ error: integer arithmetic detected LL | (&x + &y) | ^^^^^^^^^ -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors -- cgit 1.4.1-3-g733a5 From 7c74d870b5978fb100f8947850d0da3f38dd5c1c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 13:01:34 -0500 Subject: Fix vec_box scope error Fixes #6236 --- clippy_lints/src/types.rs | 2 +- tests/ui/vec_box_sized.fixed | 14 ++++++++++++++ tests/ui/vec_box_sized.rs | 14 ++++++++++++++ tests/ui/vec_box_sized.stderr | 8 +++++++- 4 files changed, 36 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 45f3bc3ea85..1bb81bbc9a3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -553,7 +553,7 @@ impl Types { hir_ty.span, "`Vec` is already on the heap, the boxing is unnecessary.", "try", - format!("Vec<{}>", ty_ty), + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index d0bee2460dd..4fa28b525c3 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -35,4 +35,18 @@ mod should_not_trigger { } } +mod inner_mod { + mod inner { + pub struct S; + } + + mod inner2 { + use super::inner::S; + + pub fn f() -> Vec { + vec![] + } + } +} + fn main() {} diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index 500a0ae263e..7dc735cd90b 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -35,4 +35,18 @@ mod should_not_trigger { } } +mod inner_mod { + mod inner { + pub struct S; + } + + mod inner2 { + use super::inner::S; + + pub fn f() -> Vec> { + vec![] + } + } +} + fn main() {} diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr index 29bf7069e8a..57e2f1fdf9a 100644 --- a/tests/ui/vec_box_sized.stderr +++ b/tests/ui/vec_box_sized.stderr @@ -18,5 +18,11 @@ error: `Vec` is already on the heap, the boxing is unnecessary. LL | struct B(Vec>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` -error: aborting due to 3 previous errors +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:46:23 + | +LL | pub fn f() -> Vec> { + | ^^^^^^^^^^^ help: try: `Vec` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From c0d1002d9328104808253b5cb680d0cd0ab70c1a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 16:06:27 -0500 Subject: Fix unnecessary_lazy_eval suggestion applicability Fixes #6240 --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 12 +++++++++++- tests/ui/unnecessary_lazy_eval_unfixable.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_lazy_eval_unfixable.stderr | 22 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_lazy_eval_unfixable.rs create mode 100644 tests/ui/unnecessary_lazy_eval_unfixable.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 08b3eab9b7c..aec1b7e2e46 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -32,6 +32,16 @@ pub(super) fn lint<'tcx>( } else { "unnecessary closure used to substitute value for `Result::Err`" }; + let applicability = if body + .params + .iter() + .all(|param| matches!(param.pat.kind, hir::PatKind::Wild)) + { + Applicability::MachineApplicable + } else { + // replacing the lambda may break type inference + Applicability::MaybeIncorrect + }; span_lint_and_sugg( cx, @@ -45,7 +55,7 @@ pub(super) fn lint<'tcx>( simplify_using, snippet(cx, body_expr.span, ".."), ), - Applicability::MachineApplicable, + applicability, ); } } diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs new file mode 100644 index 00000000000..2e923bc97a2 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -0,0 +1,18 @@ +#![warn(clippy::unnecessary_lazy_evaluations)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +fn main() { + // fix will break type inference + let _ = Ok(1).unwrap_or_else(|()| 2); + mod e { + pub struct E; + } + let _ = Ok(1).unwrap_or_else(|e::E| 2); + let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); +} diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr new file mode 100644 index 00000000000..581d641cbf5 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -0,0 +1,22 @@ +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13 + | +LL | let _ = Ok(1).unwrap_or_else(|()| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13 + | +LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13 + | +LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 2350ee75b2cc9c3d8cfbbb8d950012678913b92d Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 13 Sep 2020 13:00:45 +0200 Subject: single_char_push_str lint using insert_str() on single-char literals and suggest insert() changelog: single_char_push_str: lint using string.insert_str() with single char literals and suggests string.insert() with a char Fixes #6026 --- clippy_lints/src/methods/mod.rs | 25 +++++++++++++++++++-- clippy_lints/src/utils/paths.rs | 1 + tests/ui/single_char_insert_str.fixed | 18 +++++++++++++++ tests/ui/single_char_insert_str.rs | 18 +++++++++++++++ tests/ui/single_char_insert_str.stderr | 40 ++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 tests/ui/single_char_insert_str.fixed create mode 100644 tests/ui/single_char_insert_str.rs create mode 100644 tests/ui/single_char_insert_str.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..86e7c57afae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1290,8 +1290,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using `push_str` with a single-character string literal, - /// and `push` with a `char` would work fine. + /// **What it does:** Warns when using `push_str` with a single-character string literal + /// where `push` with a `char` would work fine. /// /// **Why is this bad?** It's less clear that we are pushing a single character. /// @@ -1521,6 +1521,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { lint_single_char_push_string(cx, expr, args); + } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { + lint_single_char_insert_string(cx, expr, args); } } @@ -3255,6 +3257,25 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args } } +/// lint for length-1 `str`s as argument for `insert_str` +fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let pos_arg = snippet(cx, args[1].span, ".."); + let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `insert_str()` using a single-character string literal", + "consider using `insert` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd72fdd61fd..34d5c8d2eb7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -52,6 +52,7 @@ pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entr pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed new file mode 100644 index 00000000000..c3416720ec3 --- /dev/null +++ b/tests/ui/single_char_insert_str.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.insert(0, 'R'); + string.insert(1, '\''); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert(0, '\x52'); + string.insert(0, '\u{0052}'); + let x: usize = 2; + string.insert(x, 'a'); + const Y: usize = 1; + string.insert(Y, 'a'); +} diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs new file mode 100644 index 00000000000..2295669e81d --- /dev/null +++ b/tests/ui/single_char_insert_str.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.insert_str(0, "R"); + string.insert_str(1, "'"); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert_str(0, "\x52"); + string.insert_str(0, "\u{0052}"); + let x: usize = 2; + string.insert_str(x, r##"a"##); + const Y: usize = 1; + string.insert_str(Y, r##"a"##); +} diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr new file mode 100644 index 00000000000..65dc8a69d69 --- /dev/null +++ b/tests/ui/single_char_insert_str.stderr @@ -0,0 +1,40 @@ +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:6:5 + | +LL | string.insert_str(0, "R"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:7:5 + | +LL | string.insert_str(1, "'"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:12:5 + | +LL | string.insert_str(0, "/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:13:5 + | +LL | string.insert_str(0, "/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:15:5 + | +LL | string.insert_str(x, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:17:5 + | +LL | string.insert_str(Y, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From c6412aeebc432ab49d46fe1f45a0fb5322c805d1 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 14 Sep 2020 15:31:04 +0200 Subject: handle macros returning Strings in single_char_push_str and single_char_insert_str --- clippy_lints/src/methods/mod.rs | 6 ++++-- tests/ui/single_char_insert_str.fixed | 8 ++++++++ tests/ui/single_char_insert_str.rs | 8 ++++++++ tests/ui/single_char_insert_str.stderr | 20 +++++++++++++------- tests/ui/single_char_push_str.fixed | 8 ++++++++ tests/ui/single_char_push_str.rs | 8 ++++++++ tests/ui/single_char_push_str.stderr | 10 +++++----- 7 files changed, 54 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 86e7c57afae..68a152270e0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3243,7 +3243,8 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability); let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, @@ -3261,7 +3262,8 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); let pos_arg = snippet(cx, args[1].span, ".."); let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed index c3416720ec3..fd43f913330 100644 --- a/tests/ui/single_char_insert_str.fixed +++ b/tests/ui/single_char_insert_str.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.insert(0, 'R'); @@ -15,4 +21,6 @@ fn main() { string.insert(x, 'a'); const Y: usize = 1; string.insert(Y, 'a'); + + get_string!().insert(1, '?'); } diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs index 2295669e81d..4278f7ef9fd 100644 --- a/tests/ui/single_char_insert_str.rs +++ b/tests/ui/single_char_insert_str.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.insert_str(0, "R"); @@ -15,4 +21,6 @@ fn main() { string.insert_str(x, r##"a"##); const Y: usize = 1; string.insert_str(Y, r##"a"##); + + get_string!().insert_str(1, "?"); } diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr index 65dc8a69d69..3d00c91a2ac 100644 --- a/tests/ui/single_char_insert_str.stderr +++ b/tests/ui/single_char_insert_str.stderr @@ -1,5 +1,5 @@ error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:6:5 + --> $DIR/single_char_insert_str.rs:12:5 | LL | string.insert_str(0, "R"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` @@ -7,34 +7,40 @@ LL | string.insert_str(0, "R"); = note: `-D clippy::single-char-push-str` implied by `-D warnings` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:7:5 + --> $DIR/single_char_insert_str.rs:13:5 | LL | string.insert_str(1, "'"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:12:5 + --> $DIR/single_char_insert_str.rs:18:5 | LL | string.insert_str(0, "/x52"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:13:5 + --> $DIR/single_char_insert_str.rs:19:5 | LL | string.insert_str(0, "/u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:15:5 + --> $DIR/single_char_insert_str.rs:21:5 | LL | string.insert_str(x, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:17:5 + --> $DIR/single_char_insert_str.rs:23:5 | LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` -error: aborting due to 6 previous errors +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:25:5 + | +LL | get_string!().insert_str(1, "?"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` + +error: aborting due to 7 previous errors diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index 0812c026a64..da742fe70e2 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.push('R'); @@ -12,4 +18,6 @@ fn main() { string.push('\x52'); string.push('\u{0052}'); string.push('a'); + + get_string!().push_str("ö"); } diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs index ab293bbe4ee..a8203e6503e 100644 --- a/tests/ui/single_char_push_str.rs +++ b/tests/ui/single_char_push_str.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.push_str("R"); @@ -12,4 +18,6 @@ fn main() { string.push_str("\x52"); string.push_str("\u{0052}"); string.push_str(r##"a"##); + + get_string!().push_str("ö"); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 0e9bdaa23e7..1f535589121 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -1,5 +1,5 @@ error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:6:5 + --> $DIR/single_char_push_str.rs:12:5 | LL | string.push_str("R"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` @@ -7,25 +7,25 @@ LL | string.push_str("R"); = note: `-D clippy::single-char-push-str` implied by `-D warnings` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:7:5 + --> $DIR/single_char_push_str.rs:13:5 | LL | string.push_str("'"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:12:5 + --> $DIR/single_char_push_str.rs:18:5 | LL | string.push_str("/x52"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:13:5 + --> $DIR/single_char_push_str.rs:19:5 | LL | string.push_str("/u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:14:5 + --> $DIR/single_char_push_str.rs:20:5 | LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` -- cgit 1.4.1-3-g733a5 From c1eb8ceede6538590a39721c65ee2f943ceffe40 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 24 Sep 2020 16:36:29 +0200 Subject: get_hint_if_single_char_arg: fix bug where multi-char letters are not detected properly --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/single_char_pattern.fixed | 6 +++--- tests/ui/single_char_pattern.stderr | 20 +++++++++++++++++++- tests/ui/single_char_push_str.fixed | 2 +- tests/ui/single_char_push_str.stderr | 8 +++++++- 5 files changed, 31 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 68a152270e0..5cc3c25e01d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3204,7 +3204,7 @@ fn get_hint_if_single_char_arg( if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; let string = r.as_str(); - if string.len() == 1; + if string.chars().count() == 1; then { let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 3871c4f2268..817a06199ff 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -18,9 +18,9 @@ fn main() { // // We may not want to suggest changing these anyway // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 - x.split("ß"); - x.split("ℝ"); - x.split("💣"); + x.split('ß'); + x.split('ℝ'); + x.split('💣'); // Can't use this lint for unicode code points which don't fit in a char x.split("❤️"); x.contains('x'); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index fe7211c53f8..ecaabd9155b 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -6,6 +6,24 @@ LL | x.split("x"); | = note: `-D clippy::single-char-pattern` implied by `-D warnings` +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:21:13 + | +LL | x.split("ß"); + | ^^^ help: try using a `char` instead: `'ß'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:22:13 + | +LL | x.split("ℝ"); + | ^^^ help: try using a `char` instead: `'ℝ'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:23:13 + | +LL | x.split("💣"); + | ^^^^ help: try using a `char` instead: `'💣'` + error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:26:16 | @@ -162,5 +180,5 @@ error: single-character string constant used as pattern LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` -error: aborting due to 27 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index da742fe70e2..3c550bee9a3 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -19,5 +19,5 @@ fn main() { string.push('\u{0052}'); string.push('a'); - get_string!().push_str("ö"); + get_string!().push('ö'); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 1f535589121..d6e6e635cc5 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -30,5 +30,11 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` -error: aborting due to 5 previous errors +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:22:5 + | +LL | get_string!().push_str("ö"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From d958269fe5afb26ee5026be5bc0cea459593bccf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 8 Oct 2020 00:03:26 +0200 Subject: Rename single_char_push_str to single_char_add_str --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +-- clippy_lints/src/methods/mod.rs | 16 ++++--- src/lintlist/mod.rs | 12 ++--- tests/ui/single_char_add_str.fixed | 43 ++++++++++++++++++ tests/ui/single_char_add_str.rs | 43 ++++++++++++++++++ tests/ui/single_char_add_str.stderr | 82 ++++++++++++++++++++++++++++++++++ tests/ui/single_char_insert_str.fixed | 26 ----------- tests/ui/single_char_insert_str.rs | 26 ----------- tests/ui/single_char_insert_str.stderr | 46 ------------------- tests/ui/single_char_pattern.fixed | 6 --- tests/ui/single_char_pattern.rs | 6 --- tests/ui/single_char_pattern.stderr | 58 ++++++++++++------------ tests/ui/single_char_push_str.fixed | 23 ---------- tests/ui/single_char_push_str.rs | 23 ---------- tests/ui/single_char_push_str.stderr | 40 ----------------- 16 files changed, 216 insertions(+), 242 deletions(-) create mode 100644 tests/ui/single_char_add_str.fixed create mode 100644 tests/ui/single_char_add_str.rs create mode 100644 tests/ui/single_char_add_str.stderr delete mode 100644 tests/ui/single_char_insert_str.fixed delete mode 100644 tests/ui/single_char_insert_str.rs delete mode 100644 tests/ui/single_char_insert_str.stderr delete mode 100644 tests/ui/single_char_push_str.fixed delete mode 100644 tests/ui/single_char_push_str.rs delete mode 100644 tests/ui/single_char_push_str.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..27e861b0d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1939,8 +1939,8 @@ Released 2018-09-13 [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern -[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97358e06c63..f2056c72c07 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -713,8 +713,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, + &methods::SINGLE_CHAR_ADD_STR, &methods::SINGLE_CHAR_PATTERN, - &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -1438,8 +1438,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_ADD_STR), LintId::of(&methods::SINGLE_CHAR_PATTERN), - LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1631,7 +1631,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(&methods::SINGLE_CHAR_PUSH_STR), + LintId::of(&methods::SINGLE_CHAR_ADD_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5cc3c25e01d..89a2db2b76b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1290,8 +1290,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using `push_str` with a single-character string literal - /// where `push` with a `char` would work fine. + /// **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal + /// where `push`/`insert` with a `char` would work fine. /// /// **Why is this bad?** It's less clear that we are pushing a single character. /// @@ -1300,16 +1300,18 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let mut string = String::new(); + /// string.insert_str(0, "R"); /// string.push_str("R"); /// ``` /// Could be written as /// ```rust /// let mut string = String::new(); + /// string.insert(0, 'R'); /// string.push('R'); /// ``` - pub SINGLE_CHAR_PUSH_STR, + pub SINGLE_CHAR_ADD_STR, style, - "`push_str()` used with a single-character string literal as parameter" + "`push_str()` or `insert_str()` used with a single-character string literal as parameter" } declare_clippy_lint! { @@ -1390,7 +1392,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, @@ -3248,7 +3250,7 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, expr.span, "calling `push_str()` using a single-character string literal", "consider using `push` with a character literal", @@ -3268,7 +3270,7 @@ fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ar let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( cx, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, expr.span, "calling `insert_str()` using a single-character string literal", "consider using `insert` with a character literal", diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..b22bb074238 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2147,16 +2147,16 @@ vec![ module: "non_expressive_names", }, Lint { - name: "single_char_pattern", - group: "perf", - desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", + name: "single_char_add_str", + group: "style", + desc: "`push_str()` or `insert_str()` used with a single-character string literal as parameter", deprecation: None, module: "methods", }, Lint { - name: "single_char_push_str", - group: "style", - desc: "`push_str()` used with a single-character string literal as parameter", + name: "single_char_pattern", + group: "perf", + desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", deprecation: None, module: "methods", }, diff --git a/tests/ui/single_char_add_str.fixed b/tests/ui/single_char_add_str.fixed new file mode 100644 index 00000000000..2feba4b8069 --- /dev/null +++ b/tests/ui/single_char_add_str.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::single_char_add_str)] + +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + +fn main() { + // `push_str` tests + + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); + + get_string!().push('ö'); + + // `insert_str` tests + + let mut string = String::new(); + string.insert(0, 'R'); + string.insert(1, '\''); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert(0, '\x52'); + string.insert(0, '\u{0052}'); + let x: usize = 2; + string.insert(x, 'a'); + const Y: usize = 1; + string.insert(Y, 'a'); + + get_string!().insert(1, '?'); +} diff --git a/tests/ui/single_char_add_str.rs b/tests/ui/single_char_add_str.rs new file mode 100644 index 00000000000..681b3d10345 --- /dev/null +++ b/tests/ui/single_char_add_str.rs @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::single_char_add_str)] + +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + +fn main() { + // `push_str` tests + + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); + + get_string!().push_str("ö"); + + // `insert_str` tests + + let mut string = String::new(); + string.insert_str(0, "R"); + string.insert_str(1, "'"); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert_str(0, "\x52"); + string.insert_str(0, "\u{0052}"); + let x: usize = 2; + string.insert_str(x, r##"a"##); + const Y: usize = 1; + string.insert_str(Y, r##"a"##); + + get_string!().insert_str(1, "?"); +} diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr new file mode 100644 index 00000000000..2b17279a564 --- /dev/null +++ b/tests/ui/single_char_add_str.stderr @@ -0,0 +1,82 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:14:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-add-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:15:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:20:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:21:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:22:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:24:5 + | +LL | get_string!().push_str("ö"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:29:5 + | +LL | string.insert_str(0, "R"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:30:5 + | +LL | string.insert_str(1, "'"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:35:5 + | +LL | string.insert_str(0, "/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:36:5 + | +LL | string.insert_str(0, "/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:38:5 + | +LL | string.insert_str(x, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:40:5 + | +LL | string.insert_str(Y, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:42:5 + | +LL | get_string!().insert_str(1, "?"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed deleted file mode 100644 index fd43f913330..00000000000 --- a/tests/ui/single_char_insert_str.fixed +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.insert(0, 'R'); - string.insert(1, '\''); - - string.insert(0, 'u'); - string.insert_str(2, "st"); - string.insert_str(0, ""); - string.insert(0, '\x52'); - string.insert(0, '\u{0052}'); - let x: usize = 2; - string.insert(x, 'a'); - const Y: usize = 1; - string.insert(Y, 'a'); - - get_string!().insert(1, '?'); -} diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs deleted file mode 100644 index 4278f7ef9fd..00000000000 --- a/tests/ui/single_char_insert_str.rs +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.insert_str(0, "R"); - string.insert_str(1, "'"); - - string.insert(0, 'u'); - string.insert_str(2, "st"); - string.insert_str(0, ""); - string.insert_str(0, "\x52"); - string.insert_str(0, "\u{0052}"); - let x: usize = 2; - string.insert_str(x, r##"a"##); - const Y: usize = 1; - string.insert_str(Y, r##"a"##); - - get_string!().insert_str(1, "?"); -} diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr deleted file mode 100644 index 3d00c91a2ac..00000000000 --- a/tests/ui/single_char_insert_str.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:12:5 - | -LL | string.insert_str(0, "R"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` - | - = note: `-D clippy::single-char-push-str` implied by `-D warnings` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:13:5 - | -LL | string.insert_str(1, "'"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:18:5 - | -LL | string.insert_str(0, "/x52"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:19:5 - | -LL | string.insert_str(0, "/u{0052}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:21:5 - | -LL | string.insert_str(x, r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:23:5 - | -LL | string.insert_str(Y, r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:25:5 - | -LL | get_string!().insert_str(1, "?"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` - -error: aborting due to 7 previous errors - diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 817a06199ff..d8b5f19e144 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -12,12 +12,6 @@ fn main() { let y = "x"; x.split(y); - // Not yet testing for multi-byte characters - // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` - // should have done this but produced an ICE - // - // We may not want to suggest changing these anyway - // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 x.split('ß'); x.split('ℝ'); x.split('💣'); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index 32afe339cd8..a7bc73e3756 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -12,12 +12,6 @@ fn main() { let y = "x"; x.split(y); - // Not yet testing for multi-byte characters - // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` - // should have done this but produced an ICE - // - // We may not want to suggest changing these anyway - // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 x.split("ß"); x.split("ℝ"); x.split("💣"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index ecaabd9155b..ee4e7e50efd 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -7,175 +7,175 @@ LL | x.split("x"); = note: `-D clippy::single-char-pattern` implied by `-D warnings` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:21:13 + --> $DIR/single_char_pattern.rs:15:13 | LL | x.split("ß"); | ^^^ help: try using a `char` instead: `'ß'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:22:13 + --> $DIR/single_char_pattern.rs:16:13 | LL | x.split("ℝ"); | ^^^ help: try using a `char` instead: `'ℝ'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:23:13 + --> $DIR/single_char_pattern.rs:17:13 | LL | x.split("💣"); | ^^^^ help: try using a `char` instead: `'💣'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:26:16 + --> $DIR/single_char_pattern.rs:20:16 | LL | x.contains("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:27:19 + --> $DIR/single_char_pattern.rs:21:19 | LL | x.starts_with("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:28:17 + --> $DIR/single_char_pattern.rs:22:17 | LL | x.ends_with("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:29:12 + --> $DIR/single_char_pattern.rs:23:12 | LL | x.find("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:30:13 + --> $DIR/single_char_pattern.rs:24:13 | LL | x.rfind("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:31:14 + --> $DIR/single_char_pattern.rs:25:14 | LL | x.rsplit("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:32:24 + --> $DIR/single_char_pattern.rs:26:24 | LL | x.split_terminator("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:33:25 + --> $DIR/single_char_pattern.rs:27:25 | LL | x.rsplit_terminator("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:34:17 + --> $DIR/single_char_pattern.rs:28:17 | LL | x.splitn(0, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:35:18 + --> $DIR/single_char_pattern.rs:29:18 | LL | x.rsplitn(0, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:36:15 + --> $DIR/single_char_pattern.rs:30:15 | LL | x.matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:37:16 + --> $DIR/single_char_pattern.rs:31:16 | LL | x.rmatches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:38:21 + --> $DIR/single_char_pattern.rs:32:21 | LL | x.match_indices("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:39:22 + --> $DIR/single_char_pattern.rs:33:22 | LL | x.rmatch_indices("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:40:26 + --> $DIR/single_char_pattern.rs:34:26 | LL | x.trim_start_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:41:24 + --> $DIR/single_char_pattern.rs:35:24 | LL | x.trim_end_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:43:13 + --> $DIR/single_char_pattern.rs:37:13 | LL | x.split("/n"); | ^^^^ help: try using a `char` instead: `'/n'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:44:13 + --> $DIR/single_char_pattern.rs:38:13 | LL | x.split("'"); | ^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:13 + --> $DIR/single_char_pattern.rs:39:13 | LL | x.split("/'"); | ^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:50:31 + --> $DIR/single_char_pattern.rs:44:31 | LL | x.replace(";", ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:51:19 + --> $DIR/single_char_pattern.rs:45:19 | LL | x.starts_with("/x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'/x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:58:13 + --> $DIR/single_char_pattern.rs:52:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:59:13 + --> $DIR/single_char_pattern.rs:53:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:60:13 + --> $DIR/single_char_pattern.rs:54:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:61:13 + --> $DIR/single_char_pattern.rs:55:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:62:13 + --> $DIR/single_char_pattern.rs:56:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed deleted file mode 100644 index 3c550bee9a3..00000000000 --- a/tests/ui/single_char_push_str.fixed +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.push('R'); - string.push('\''); - - string.push('u'); - string.push_str("st"); - string.push_str(""); - string.push('\x52'); - string.push('\u{0052}'); - string.push('a'); - - get_string!().push('ö'); -} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs deleted file mode 100644 index a8203e6503e..00000000000 --- a/tests/ui/single_char_push_str.rs +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.push_str("R"); - string.push_str("'"); - - string.push('u'); - string.push_str("st"); - string.push_str(""); - string.push_str("\x52"); - string.push_str("\u{0052}"); - string.push_str(r##"a"##); - - get_string!().push_str("ö"); -} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr deleted file mode 100644 index d6e6e635cc5..00000000000 --- a/tests/ui/single_char_push_str.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:12:5 - | -LL | string.push_str("R"); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` - | - = note: `-D clippy::single-char-push-str` implied by `-D warnings` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:13:5 - | -LL | string.push_str("'"); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:18:5 - | -LL | string.push_str("/x52"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:19:5 - | -LL | string.push_str("/u{0052}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:20:5 - | -LL | string.push_str(r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:22:5 - | -LL | get_string!().push_str("ö"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` - -error: aborting due to 6 previous errors - -- cgit 1.4.1-3-g733a5 From f8ac1f99efd996bea2b05c83a915a830d69e1690 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 30 Oct 2020 23:47:22 +0100 Subject: Address suggestions in PR review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 1 + clippy_lints/src/methods/mod.rs | 2 +- tests/ui/single_char_add_str.fixed | 2 ++ tests/ui/single_char_add_str.rs | 2 ++ tests/ui/single_char_add_str.stderr | 14 +++++++++++++- 6 files changed, 20 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e861b0d3e..be47eeaa073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ Current beta, release 2020-11-19 * [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) * [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044) * [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) -* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) ### Moves and Deprecations diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2056c72c07..9d9097002d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1958,6 +1958,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); + ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 89a2db2b76b..558c90249cc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3266,7 +3266,7 @@ fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ar if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet(cx, args[1].span, ".."); + let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( cx, diff --git a/tests/ui/single_char_add_str.fixed b/tests/ui/single_char_add_str.fixed index 2feba4b8069..63a6d37a9cc 100644 --- a/tests/ui/single_char_add_str.fixed +++ b/tests/ui/single_char_add_str.fixed @@ -38,6 +38,8 @@ fn main() { string.insert(x, 'a'); const Y: usize = 1; string.insert(Y, 'a'); + string.insert(Y, '"'); + string.insert(Y, '\''); get_string!().insert(1, '?'); } diff --git a/tests/ui/single_char_add_str.rs b/tests/ui/single_char_add_str.rs index 681b3d10345..a799ea7d885 100644 --- a/tests/ui/single_char_add_str.rs +++ b/tests/ui/single_char_add_str.rs @@ -38,6 +38,8 @@ fn main() { string.insert_str(x, r##"a"##); const Y: usize = 1; string.insert_str(Y, r##"a"##); + string.insert_str(Y, r##"""##); + string.insert_str(Y, r##"'"##); get_string!().insert_str(1, "?"); } diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr index 2b17279a564..55d91583ad0 100644 --- a/tests/ui/single_char_add_str.stderr +++ b/tests/ui/single_char_add_str.stderr @@ -72,11 +72,23 @@ error: calling `insert_str()` using a single-character string literal LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:41:5 + | +LL | string.insert_str(Y, r##"""##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')` + error: calling `insert_str()` using a single-character string literal --> $DIR/single_char_add_str.rs:42:5 | +LL | string.insert_str(Y, r##"'"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:44:5 + | LL | get_string!().insert_str(1, "?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 2eb248dd16f6727a2cdaa66f3f529a53bbcc54c7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 31 Oct 2020 09:34:01 +0100 Subject: Fix formatting --- clippy_lints/src/redundant_clone.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index ae3b0a03754..344ed02361d 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -518,7 +518,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { self.possible_borrower.add(borrowed.local, lhs); }, other => { - if ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty).is_continue() { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { return; } rvalue_locals(other, |rhs| { -- cgit 1.4.1-3-g733a5 From 7b203f3da6b590059c12bb01d0873b31be347928 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 31 Oct 2019 18:02:02 +0200 Subject: Implement field_reassign_with_default - Implement `field_reassign_with_default` as a `LateLintPass` - Avoid triggering `default_trait_access` on a span already linted by `field_reassigned_with_default` - Merge `default_trait_access` and `field_reassign_with_default` into `Default` - Co-authored-by: Eduardo Broto - Fixes #568 --- CHANGELOG.md | 1 + clippy_lints/src/default.rs | 304 ++++++++++++++++++++++++++++ clippy_lints/src/default_trait_access.rs | 62 ------ clippy_lints/src/lib.rs | 11 +- src/lintlist/mod.rs | 9 +- tests/ui/field_reassign_with_default.rs | 110 ++++++++++ tests/ui/field_reassign_with_default.stderr | 75 +++++++ 7 files changed, 505 insertions(+), 67 deletions(-) create mode 100644 clippy_lints/src/default.rs delete mode 100644 clippy_lints/src/default_trait_access.rs create mode 100644 tests/ui/field_reassign_with_default.rs create mode 100644 tests/ui/field_reassign_with_default.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..36b54416a19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1714,6 +1714,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file [`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map [`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs new file mode 100644 index 00000000000..612c5355338 --- /dev/null +++ b/clippy_lints/src/default.rs @@ -0,0 +1,304 @@ +use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{span_lint_and_note, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Adt, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for literal calls to `Default::default()`. + /// + /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is + /// being gotten than the generic `Default`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let s: String = Default::default(); + /// + /// // Good + /// let s = String::default(); + /// ``` + pub DEFAULT_TRAIT_ACCESS, + pedantic, + "checks for literal calls to `Default::default()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for immediate reassignment of fields initialized + /// with Default::default(). + /// + /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). + /// + /// **Known problems:** Assignments to patterns that are of tuple type are not linted. + /// + /// **Example:** + /// Bad: + /// ``` + /// # #[derive(Default)] + /// # struct A { i: i32 } + /// let mut a: A = Default::default(); + /// a.i = 42; + /// ``` + /// Use instead: + /// ``` + /// # #[derive(Default)] + /// # struct A { i: i32 } + /// let a = A { + /// i: 42, + /// .. Default::default() + /// }; + /// ``` + pub FIELD_REASSIGN_WITH_DEFAULT, + style, + "binding initialized with Default should have its fields set in the initializer" +} + +#[derive(Default)] +pub struct Default { + // Spans linted by `field_reassign_with_default`. + reassigned_linted: FxHashSet, +} + +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); + +impl LateLintPass<'_> for Default { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Avoid cases already linted by `field_reassign_with_default` + if !self.reassigned_linted.contains(&expr.span); + if let ExprKind::Call(ref path, ..) = expr.kind; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; + then { + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind() { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); + } + } + } + } + + fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + // find all binding statements like `let mut _ = T::default()` where `T::default()` is the + // `default` method of the `Default` trait, and store statement index in current block being + // checked and the name of the bound variable + let binding_statements_using_default = enumerate_bindings_using_default(cx, block); + + // start from the `let mut _ = _::default();` and look at all the following + // statements, see if they re-assign the fields of the binding + for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { + // the last statement of a block cannot trigger the lint + if stmt_idx == block.stmts.len() - 1 { + break; + } + + // find all "later statement"'s where the fields of the binding set as + // Default::default() get reassigned, unless the reassignment refers to the original binding + let mut first_assign = None; + let mut assigned_fields = Vec::new(); + let mut cancel_lint = false; + for consecutive_statement in &block.stmts[stmt_idx + 1..] { + // interrupt if the statement is a let binding (`Local`) that shadows the original + // binding + if stmt_shadows_binding(consecutive_statement, binding_name) { + break; + } + // find out if and which field was set by this `consecutive_statement` + else if let Some((field_ident, assign_rhs)) = + field_reassigned_by_stmt(consecutive_statement, binding_name) + { + // interrupt and cancel lint if assign_rhs references the original binding + if contains_name(binding_name, assign_rhs) { + cancel_lint = true; + break; + } + + // if the field was previously assigned, replace the assignment, otherwise insert the assignment + if let Some(prev) = assigned_fields + .iter_mut() + .find(|(field_name, _)| field_name == &field_ident.name) + { + *prev = (field_ident.name, assign_rhs); + } else { + assigned_fields.push((field_ident.name, assign_rhs)); + } + + // also set first instance of error for help message + if first_assign.is_none() { + first_assign = Some(consecutive_statement); + } + } + // interrupt also if no field was assigned, since we only want to look at consecutive statements + else { + break; + } + } + + // if there are incorrectly assigned fields, do a span_lint_and_note to suggest + // construction using `Ty { fields, ..Default::default() }` + if !assigned_fields.is_empty() && !cancel_lint { + // take the original assignment as span + let stmt = &block.stmts[stmt_idx]; + + if let StmtKind::Local(preceding_local) = &stmt.kind { + // filter out fields like `= Default::default()`, because the FRU already covers them + let assigned_fields = assigned_fields + .into_iter() + .filter(|(_, rhs)| !is_expr_default(rhs, cx)) + .collect::)>>(); + + // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. + let ext_with_default = !fields_of_type(binding_type) + .iter() + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + + let field_list = assigned_fields + .into_iter() + .map(|(field, rhs)| { + // extract and store the assigned value for help message + let value_snippet = snippet(cx, rhs.span, ".."); + format!("{}: {}", field, value_snippet) + }) + .collect::>() + .join(", "); + + let sugg = if ext_with_default { + if field_list.is_empty() { + format!("{}::default()", binding_type) + } else { + format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + } + } else { + format!("{} {{ {} }}", binding_type, field_list) + }; + + // span lint once per statement that binds default + span_lint_and_note( + cx, + FIELD_REASSIGN_WITH_DEFAULT, + first_assign.unwrap().span, + "field assignment outside of initializer for an instance created with Default::default()", + Some(preceding_local.span), + &format!( + "consider initializing the variable with `{}` and removing relevant reassignments", + sugg + ), + ); + self.reassigned_linted.insert(span); + } + } + } + } +} + +/// Checks if the given expression is the `default` method belonging to the `Default` trait. +fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { + if_chain! { + if let ExprKind::Call(ref fn_expr, _) = &expr.kind; + if let ExprKind::Path(qpath) = &fn_expr.kind; + if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id); + then { + // right hand side of assignment is `Default::default` + match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD) + } else { + false + } + } +} + +/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except +/// for when the pattern type is a tuple. +fn enumerate_bindings_using_default<'tcx>( + cx: &LateContext<'tcx>, + block: &Block<'tcx>, +) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { + block + .stmts + .iter() + .enumerate() + .filter_map(|(idx, stmt)| { + if_chain! { + // only take `let ...` statements + if let StmtKind::Local(ref local) = stmt.kind; + // only take bindings to identifiers + if let PatKind::Binding(_, _, ident, _) = local.pat.kind; + // that are not tuples + let ty = cx.typeck_results().pat_ty(local.pat); + if !matches!(ty.kind(), ty::Tuple(_)); + // only when assigning `... = Default::default()` + if let Some(ref expr) = local.init; + if is_expr_default(expr, cx); + then { + Some((idx, ident.name, ty, expr.span)) + } else { + None + } + } + }) + .collect() +} + +fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool { + if let StmtKind::Local(local) = &this.kind { + if let PatKind::Binding(_, _, ident, _) = local.pat.kind { + return ident.name == shadowed; + } + } + false +} + +/// Returns the reassigned field and the assigning expression (right-hand side of assign). +fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { + if_chain! { + // only take assignments + if let StmtKind::Semi(ref later_expr) = this.kind; + if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; + // only take assignments to fields where the left-hand side field is a field of + // the same binding as the previous statement + if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; + if let ExprKind::Path(ref qpath) = binding.kind; + if let QPath::Resolved(_, path) = qpath; + if let Some(second_binding_name) = path.segments.last(); + if second_binding_name.ident.name == binding_name; + then { + Some((field_ident, assign_rhs)) + } else { + None + } + } +} + +/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. +fn fields_of_type(ty: Ty<'_>) -> Vec { + if let Adt(adt, _) = ty.kind() { + if adt.is_struct() { + let variant = &adt.non_enum_variant(); + return variant.fields.iter().map(|f| f.ident).collect(); + } + } + vec![] +} diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs deleted file mode 100644 index 3048436d9a7..00000000000 --- a/clippy_lints/src/default_trait_access.rs +++ /dev/null @@ -1,62 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg}; - -declare_clippy_lint! { - /// **What it does:** Checks for literal calls to `Default::default()`. - /// - /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is - /// being gotten than the generic `Default`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // Bad - /// let s: String = Default::default(); - /// - /// // Good - /// let s = String::default(); - /// ``` - pub DEFAULT_TRAIT_ACCESS, - pedantic, - "checks for literal calls to `Default::default()`" -} - -declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]); - -impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(ref path, ..) = expr.kind; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); - // Detect and ignore ::default() because these calls do explicitly name the type. - if let QPath::Resolved(None, _path) = qpath; - then { - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind() { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - } - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..bb94b3f6cbe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -176,7 +176,7 @@ mod copies; mod copy_iterator; mod create_dir; mod dbg_macro; -mod default_trait_access; +mod default; mod dereference; mod derive; mod disallowed_method; @@ -537,7 +537,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, - &default_trait_access::DEFAULT_TRAIT_ACCESS, + &default::DEFAULT_TRAIT_ACCESS, + &default::FIELD_REASSIGN_WITH_DEFAULT, &dereference::EXPLICIT_DEREF_METHODS, &derive::DERIVE_HASH_XOR_EQ, &derive::DERIVE_ORD_XOR_PARTIAL_ORD, @@ -1049,7 +1050,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box duration_subsec::DurationSubsec); - store.register_late_pass(|| box default_trait_access::DefaultTraitAccess); store.register_late_pass(|| box indexing_slicing::IndexingSlicing); store.register_late_pass(|| box non_copy_const::NonCopyConst); store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); @@ -1100,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box exit::Exit); @@ -1212,7 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), - LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), + LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), @@ -1321,6 +1322,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), + LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), @@ -1581,6 +1583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..a3645a65182 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -359,7 +359,7 @@ vec![ group: "pedantic", desc: "checks for literal calls to `Default::default()`", deprecation: None, - module: "default_trait_access", + module: "default", }, Lint { name: "deprecated_cfg_attr", @@ -627,6 +627,13 @@ vec![ deprecation: None, module: "fallible_impl_from", }, + Lint { + name: "field_reassign_with_default", + group: "style", + desc: "binding initialized with Default should have its fields set in the initializer", + deprecation: None, + module: "default", + }, Lint { name: "filetype_is_file", group: "restriction", diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs new file mode 100644 index 00000000000..79a30c22f95 --- /dev/null +++ b/tests/ui/field_reassign_with_default.rs @@ -0,0 +1,110 @@ +#![warn(clippy::field_reassign_with_default)] + +#[derive(Default)] +struct A { + i: i32, + j: i64, +} + +struct B { + i: i32, + j: i64, +} + +/// Implements .next() that returns a different number each time. +struct SideEffect(i32); + +impl SideEffect { + fn new() -> SideEffect { + SideEffect(0) + } + fn next(&mut self) -> i32 { + self.0 += 1; + self.0 + } +} + +fn main() { + // wrong, produces first error in stderr + let mut a: A = Default::default(); + a.i = 42; + + // right + let mut a: A = Default::default(); + + // right + let a = A { + i: 42, + ..Default::default() + }; + + // right + let mut a: A = Default::default(); + if a.i == 0 { + a.j = 12; + } + + // right + let mut a: A = Default::default(); + let b = 5; + + // right + let mut b = 32; + let mut a: A = Default::default(); + b = 2; + + // right + let b: B = B { i: 42, j: 24 }; + + // right + let mut b: B = B { i: 42, j: 24 }; + b.i = 52; + + // right + let mut b = B { i: 15, j: 16 }; + let mut a: A = Default::default(); + b.i = 2; + + // wrong, produces second error in stderr + let mut a: A = Default::default(); + a.j = 43; + a.i = 42; + + // wrong, produces third error in stderr + let mut a: A = Default::default(); + a.i = 42; + a.j = 43; + a.j = 44; + + // wrong, produces fourth error in stderr + let mut a = A::default(); + a.i = 42; + + // wrong, but does not produce an error in stderr, because we can't produce a correct kind of + // suggestion with current implementation + let mut c: (i32, i32) = Default::default(); + c.0 = 42; + c.1 = 21; + + // wrong, produces the fifth error in stderr + let mut a: A = Default::default(); + a.i = Default::default(); + + // wrong, produces the sixth error in stderr + let mut a: A = Default::default(); + a.i = Default::default(); + a.j = 45; + + // right, because an assignment refers to another field + let mut x = A::default(); + x.i = 42; + x.j = 21 + x.i as i64; + + // right, we bail out if there's a reassignment to the same variable, since there is a risk of + // side-effects affecting the outcome + let mut x = A::default(); + let mut side_effect = SideEffect::new(); + x.i = side_effect.next(); + x.j = 2; + x.i = side_effect.next(); +} diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr new file mode 100644 index 00000000000..c788ebae552 --- /dev/null +++ b/tests/ui/field_reassign_with_default.stderr @@ -0,0 +1,75 @@ +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:30:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | + = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` +note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:29:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:70:5 + | +LL | a.j = 43; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:69:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:75:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:74:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:81:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:80:5 + | +LL | let mut a = A::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:91:5 + | +LL | a.i = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `A::default()` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:90:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:95:5 + | +LL | a.i = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:94:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 158bf9aa443c5ea93ff4b3def8c7220632e45442 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 31 Oct 2020 19:56:51 +0100 Subject: Fix incorrect suggestion for macro expansion in `deref_addrof` lint --- clippy_lints/src/reference.rs | 8 +++++++- tests/ui/deref_addrof.fixed | 15 ++++++++++++++- tests/ui/deref_addrof.rs | 15 ++++++++++++++- tests/ui/deref_addrof.stderr | 13 ++++++++++++- 4 files changed, 47 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 3fda00403c6..f1555ab3ec1 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -46,13 +46,19 @@ impl EarlyLintPass for DerefAddrOf { if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; + let sugg = if e.span.from_expansion() { + let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); + snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + } else { + snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() + }; span_lint_and_sugg( cx, DEREF_ADDROF, e.span, "immediately dereferencing a reference", "try this", - format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)), + sugg, applicability, ); } diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 9e5b51d6d5e..8cdec4201fe 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,4 +1,5 @@ // run-rustfix +#![warn(clippy::deref_addrof)] fn get_number() -> usize { 10 @@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize { #[allow(clippy::many_single_char_names, clippy::double_parens)] #[allow(unused_variables, unused_parens)] -#[warn(clippy::deref_addrof)] fn main() { let a = 10; let aref = &a; @@ -37,3 +37,16 @@ fn main() { let b = *aref; } + +macro_rules! m { + ($visitor: expr) => { + $visitor + }; +} + +pub struct S; +impl S { + pub fn f(&self) -> &Self { + m!(self) + } +} diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 5641a73cbc1..6433e63ca59 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,4 +1,5 @@ // run-rustfix +#![warn(clippy::deref_addrof)] fn get_number() -> usize { 10 @@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize { #[allow(clippy::many_single_char_names, clippy::double_parens)] #[allow(unused_variables, unused_parens)] -#[warn(clippy::deref_addrof)] fn main() { let a = 10; let aref = &a; @@ -37,3 +37,16 @@ fn main() { let b = **&aref; } + +macro_rules! m { + ($visitor: expr) => { + *&$visitor + }; +} + +pub struct S; +impl S { + pub fn f(&self) -> &Self { + m!(self) + } +} diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index bc51719e8a7..1fe49ad49cd 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -48,5 +48,16 @@ error: immediately dereferencing a reference LL | let b = **&aref; | ^^^^^^ help: try this: `aref` -error: aborting due to 8 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:43:9 + | +LL | *&$visitor + | ^^^^^^^^^^ help: try this: `$visitor` +... +LL | m!(self) + | -------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 343bdb33647627a6a001be039a107e7ef3362707 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 17:56:54 +0100 Subject: Give better suggestion by working on span on `deref_addrof` lint --- clippy_lints/src/reference.rs | 36 +++++++++++++++++++++++++++++++----- tests/ui/deref_addrof.fixed | 10 ++++++++++ tests/ui/deref_addrof.rs | 12 +++++++++++- tests/ui/deref_addrof.stderr | 17 ++++++++++++++--- 4 files changed, 66 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index f1555ab3ec1..8646d616735 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,9 +1,11 @@ -use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp}; +use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::BytePos; +// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -42,13 +44,37 @@ impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; - if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind; + if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); - snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + if let Ok(macro_source) = cx.sess.source_map().span_to_snippet(e.span) { + // Remove leading whitespace from the given span + // e.g: ` $visitor` turns into `$visitor` + let trim_leading_whitespaces = |span| { + if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()).map(|pos| { + span.lo() + BytePos(pos as u32) + }) + }) { + e.span.with_lo(start_no_whitespace) + } else { + span + } + }; + + let rpos = if *mutability == Mutability::Mut { + macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() + } else { + macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + }; + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + } else { + snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + } } else { snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() }; diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 8cdec4201fe..689e9d55223 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -44,9 +44,19 @@ macro_rules! m { }; } +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + $visitor + }; +} + pub struct S; impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 6433e63ca59..57effce5d12 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -40,7 +40,14 @@ fn main() { macro_rules! m { ($visitor: expr) => { - *&$visitor + *& $visitor + }; +} + +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + *& mut $visitor }; } @@ -49,4 +56,7 @@ impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 1fe49ad49cd..52c1f7d1da8 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -51,13 +51,24 @@ LL | let b = **&aref; error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:43:9 | -LL | *&$visitor - | ^^^^^^^^^^ help: try this: `$visitor` +LL | *& $visitor + | ^^^^^^^^^^^ help: try this: `$visitor` ... LL | m!(self) | -------- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:50:9 + | +LL | *& mut $visitor + | ^^^^^^^^^^^^^^^ help: try this: `$visitor` +... +LL | m_mut!(self) + | ------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From ce98468158318a47f6bab3707d348e116451ad39 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 17:59:47 +0100 Subject: Fix incorrect suggestion when from expansion in `try_err` lint --- clippy_lints/src/try_err.rs | 10 ++++++++-- tests/ui/try_err.fixed | 16 ++++++++++++++++ tests/ui/try_err.rs | 16 ++++++++++++++++ tests/ui/try_err.stderr | 21 ++++++++++++++++----- 4 files changed, 56 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 3e747ec4ad9..e6d8e7521a8 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg, }; use if_chain::if_chain; @@ -92,9 +92,13 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let origin_snippet = if err_arg.span.from_expansion() { + // println!("\n\n{:?}", in_macro(expr.span)); + // println!("{:#?}", snippet(cx, err_arg.span, "_")); + let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { + // println!("from expansion"); snippet_with_macro_callsite(cx, err_arg.span, "_") } else { + // println!("just a snippet"); snippet(cx, err_arg.span, "_") }; let suggestion = if err_ty == expr_err_ty { @@ -102,6 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; + // println!("origin_snippet: {:#?}", origin_snippet); + // println!("suggestion: {:#?}", suggestion); span_lint_and_sugg( cx, diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 9e77dcd8731..053dd45f23e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -78,12 +78,28 @@ fn nested_error() -> Result { Ok(1) } +// Bad suggestion when in macro (see #6242) +macro_rules! try_validation { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => return Err(1), + } + }}; +} + +fn calling_macro() -> Result { + try_validation!(Ok::<_, i32>(5)); + Ok(5) +} + fn main() { basic_test().unwrap(); into_test().unwrap(); negative_test().unwrap(); closure_matches_test().unwrap(); closure_into_test().unwrap(); + calling_macro().unwrap(); // We don't want to lint in external macros try_err!(); diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 41bcb0a189e..215ca6a07e6 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -78,12 +78,28 @@ fn nested_error() -> Result { Ok(1) } +// Bad suggestion when in macro (see #6242) +macro_rules! try_validation { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => Err(1)?, + } + }}; +} + +fn calling_macro() -> Result { + try_validation!(Ok::<_, i32>(5)); + Ok(5) +} + fn main() { basic_test().unwrap(); into_test().unwrap(); negative_test().unwrap(); closure_matches_test().unwrap(); closure_into_test().unwrap(); + calling_macro().unwrap(); // We don't want to lint in external macros try_err!(); diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 3f1cbc17e72..443c8d08472 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -29,28 +29,39 @@ LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:106:9 + --> $DIR/try_err.rs:86:23 + | +LL | Err(_) => Err(1)?, + | ^^^^^^^ help: try this: `return Err(1)` +... +LL | try_validation!(Ok::<_, i32>(5)); + | --------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:122:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:113:9 + --> $DIR/try_err.rs:129:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:115:9 + --> $DIR/try_err.rs:131:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:123:9 + --> $DIR/try_err.rs:139:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 22cc77a232a0ec73475ff54b5116adf9defc8667 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 10:32:55 -0600 Subject: Use const rustc sym where possible --- clippy_lints/src/attrs.rs | 22 ++++----- clippy_lints/src/booleans.rs | 5 +- clippy_lints/src/bytecount.rs | 3 +- clippy_lints/src/cognitive_complexity.rs | 6 +-- clippy_lints/src/doc.rs | 10 ++-- clippy_lints/src/explicit_write.rs | 3 +- clippy_lints/src/fallible_impl_from.rs | 8 ++-- clippy_lints/src/format.rs | 13 +++--- clippy_lints/src/functions.rs | 3 +- clippy_lints/src/get_last_with_len.rs | 3 +- clippy_lints/src/if_let_some_result.rs | 3 +- clippy_lints/src/inherent_to_string.rs | 3 +- clippy_lints/src/inline_fn_without_body.rs | 4 +- clippy_lints/src/loops.rs | 22 ++++----- clippy_lints/src/manual_non_exhaustive.rs | 10 ++-- clippy_lints/src/manual_unwrap_or.rs | 5 +- clippy_lints/src/map_clone.rs | 4 +- clippy_lints/src/map_identity.rs | 5 +- clippy_lints/src/map_unit_fn.rs | 5 +- clippy_lints/src/match_on_vec_items.rs | 3 +- clippy_lints/src/matches.rs | 9 ++-- clippy_lints/src/methods/inefficient_to_string.rs | 3 +- clippy_lints/src/methods/mod.rs | 56 +++++++++++------------ clippy_lints/src/methods/option_map_unwrap_or.rs | 4 +- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 5 +- clippy_lints/src/missing_doc.rs | 7 +-- clippy_lints/src/missing_inline.rs | 3 +- clippy_lints/src/needless_borrow.rs | 3 +- clippy_lints/src/needless_pass_by_value.rs | 10 ++-- clippy_lints/src/new_without_default.rs | 3 +- clippy_lints/src/non_expressive_names.rs | 3 +- clippy_lints/src/option_if_let_else.rs | 3 +- clippy_lints/src/panic_in_result_fn.rs | 4 +- clippy_lints/src/partialeq_ne_impl.rs | 3 +- clippy_lints/src/pass_by_ref_or_value.rs | 6 +-- clippy_lints/src/ptr.rs | 6 +-- clippy_lints/src/ptr_offset_with_cast.rs | 3 +- clippy_lints/src/question_mark.rs | 3 +- clippy_lints/src/ranges.rs | 3 +- clippy_lints/src/redundant_clone.rs | 3 +- clippy_lints/src/repeat_once.rs | 3 +- clippy_lints/src/returns.rs | 3 +- clippy_lints/src/strings.rs | 3 +- clippy_lints/src/swap.rs | 3 +- clippy_lints/src/try_err.rs | 9 ++-- clippy_lints/src/types.rs | 10 ++-- clippy_lints/src/unnecessary_sort_by.rs | 3 +- clippy_lints/src/unwrap.rs | 9 ++-- clippy_lints/src/unwrap_in_result.rs | 14 +++--- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/mod.rs | 3 +- clippy_lints/src/write.rs | 5 +- 52 files changed, 192 insertions(+), 158 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index f6eadbdef0b..a004abb58b8 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_span::symbol::{Symbol, SymbolStr}; use semver::Version; @@ -286,14 +287,14 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { }, _ => {}, } - if items.is_empty() || !attr.has_name(sym!(deprecated)) { + if items.is_empty() || !attr.has_name(sym::deprecated) { return; } for item in items { if_chain! { if let NestedMetaItem::MetaItem(mi) = &item; if let MetaItemKind::NameValue(lit) = &mi.kind; - if mi.has_name(sym!(since)); + if mi.has_name(sym::since); then { check_semver(cx, item.span(), lit); } @@ -309,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } match item.kind { ItemKind::ExternCrate(..) | ItemKind::Use(..) => { - let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym!(macro_use))); + let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym::macro_use)); for attr in item.attrs { if in_external_macro(cx.sess(), attr.span) { @@ -326,7 +327,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { match item.kind { ItemKind::Use(..) => { if is_word(lint, sym!(unused_imports)) - || is_word(lint, sym!(deprecated)) + || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) || extract_clippy_lint(lint) @@ -411,8 +412,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMet let lint_store = cx.lints(); for lint in items { if let Some(lint_name) = extract_clippy_lint(lint) { - if let CheckLintNameResult::Tool(Err((None, _))) = - lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) + if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym::clippy)) { span_lint_and_then( cx, @@ -529,10 +529,10 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut for attr in attrs { if let Some(values) = attr.meta_item_list() { - if values.len() != 1 || !attr.has_name(sym!(inline)) { + if values.len() != 1 || !attr.has_name(sym::inline) { continue; } - if is_word(&values[0], sym!(always)) { + if is_word(&values[0], sym::always) { span_lint( cx, INLINE_ALWAYS, @@ -623,12 +623,12 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { if_chain! { // check cfg_attr - if attr.has_name(sym!(cfg_attr)); + if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); if items.len() == 2; // check for `rustfmt` if let Some(feature_item) = items[0].meta_item(); - if feature_item.has_name(sym!(rustfmt)); + if feature_item.has_name(sym::rustfmt); // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); if skip_item.has_name(sym!(rustfmt_skip)) || @@ -690,7 +690,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } if_chain! { - if attr.has_name(sym!(cfg)); + if attr.has_name(sym::cfg); if let Some(list) = attr.meta_item_list(); let mismatched = find_mismatched_target_os(&list); if !mismatched.is_empty(); diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 280a2c7fe67..90bb0bd555f 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for boolean expressions that can be written more @@ -253,8 +254,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { }, ExprKind::MethodCall(path, _, args, _) if args.len() == 1 => { let type_of_receiver = cx.typeck_results().expr_ty(&args[0]); - if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type)) - && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type)) + if !is_type_diagnostic_item(cx, type_of_receiver, sym::option_type) + && !is_type_diagnostic_item(cx, type_of_receiver, sym::result_type) { return None; } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index d7d02ebf985..38a0e27c4cf 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use rustc_span::Symbol; declare_clippy_lint! { @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = filter_args[0].kind { let p = path.ident.name; - if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 { + if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { &args[0] } else { &filter_args[0] diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 14ef8c319ef..b1bc2ec29e1 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::BytePos; +use rustc_span::{sym, BytePos}; use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack}; @@ -61,7 +61,7 @@ impl CognitiveComplexity { helper.visit_expr(expr); let CCHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); - let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) { + let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) { returns } else { #[allow(clippy::integer_division)] @@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { hir_id: HirId, ) { let def_id = cx.tcx.hir().local_def_id(hir_id); - if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) { + if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { self.check(cx, kind, decl, body, span); } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 07f604cf714..edecba57e44 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -15,7 +15,7 @@ use rustc_parse::maybe_new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; -use rustc_span::{FileName, Pos}; +use rustc_span::{sym, FileName, Pos}; use std::io; use std::ops::Range; use url::Url; @@ -237,7 +237,7 @@ fn lint_for_missing_headers<'tcx>( ); } if !headers.errors { - if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { span_lint( cx, MISSING_ERRORS_DOC, @@ -255,7 +255,7 @@ fn lint_for_missing_headers<'tcx>( if let ty::Opaque(_, subs) = ret_ty.kind(); if let Some(gen) = subs.types().next(); if let ty::Generator(_, subs, _) = gen.kind(); - if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type)); + if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::result_type); then { span_lint( cx, @@ -333,7 +333,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span); spans.extend_from_slice(¤t_spans); doc.push_str(&comment); - } else if attr.has_name(sym!(doc)) { + } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check return DocHeaders { @@ -479,7 +479,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) => return false, // We found a main function ... - ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { let is_async = matches!(sig.header.asyncness, Async::Yes{..}); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 0240e80d814..f8038d06e50 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be @@ -33,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { if_chain! { // match call to unwrap if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind; - if unwrap_fun.ident.name == sym!(unwrap); + if unwrap_fun.ident.name == sym::unwrap; // match call to write_fmt if !unwrap_args.is_empty(); if let ExprKind::MethodCall(ref write_fun, _, write_args, _) = diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index a9e05fddbe7..fe817fe94f2 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()` @@ -95,8 +95,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); - if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) - || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type) { self.result.push(expr.span); } @@ -113,7 +113,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h for impl_item in impl_items { if_chain! { - if impl_item.ident.name == sym!(from); + if impl_item.ident.name == sym::from; if let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind; then { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 26da058598e..8e41e0e34da 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -10,6 +10,7 @@ use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for the use of `format!("string literal with no @@ -91,7 +92,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if pats.len() == 1; then { let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); - if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) { return None; } if let ExprKind::Lit(ref lit) = format_args.kind { @@ -186,15 +187,15 @@ fn check_unformatted(expr: &Expr<'_>) -> bool { if exprs.len() == 1; // struct `core::fmt::rt::v1::Argument` if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format)); + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); // struct `core::fmt::rt::v1::FormatSpec` if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind; - if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision)); + if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision); if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; - if last_path_segment(precision_path).ident.name == sym!(Implied); - if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width)); + if last_path_segment(precision_path).ident.name == sym::Implied; + if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width); if let ExprKind::Path(ref width_qpath) = width_field.expr.kind; - if last_path_segment(width_qpath).ident.name == sym!(Implied); + if last_path_segment(width_qpath).ident.name == sym::Implied; then { return true; } diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 9c0efef95de..8b58d1f2601 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_target::spec::abi::Abi; use rustc_typeck::hir_ty_to_ty; @@ -473,7 +474,7 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span if !in_external_macro(cx.sess(), item_span); if let hir::FnRetTy::Return(ref ty) = decl.output; if let hir::TyKind::Path(ref qpath) = ty.kind; - if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type)); + if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type); if let Some(ref args) = last_path_segment(qpath).args; if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; if let hir::TyKind::Tup(t) = err_ty.kind; diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index 48ebcf5ebcd..cdd8a42e7cd 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of @@ -55,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { // Argument 0 (the struct we're calling the method on) is a vector if let Some(struct_calling_on) = args.get(0); let struct_ty = cx.typeck_results().expr_ty(struct_calling_on); - if is_type_diagnostic_item(cx, struct_ty, sym!(vec_type)); + if is_type_diagnostic_item(cx, struct_ty, sym::vec_type); // Argument to "get" is a subtraction if let Some(get_index_arg) = args.get(1); diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 28b20cdeac3..e0a1f4c5ca4 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -4,6 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:*** Checks for unnecessary `ok()` in if let. @@ -45,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym!(result_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; then { diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 0877b44d901..b723d06a688 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -2,6 +2,7 @@ use if_chain::if_chain; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use crate::utils::{ get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, @@ -107,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if decl.inputs.len() == 1; // Check if return type is String - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::string_type); // Filters instances of to_string which are required by a trait if trait_ref_of_method(cx, impl_item.hir_id).is_none(); diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 4b605fdb366..d1c3fdc7146 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// **What it does:** Checks for `#[inline]` on trait methods without bodies @@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { for attr in attrs { - if !attr.has_name(sym!(inline)) { + if !attr.has_name(sym::inline) { continue; } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c3f75f283f4..c7d3f4e9b08 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -619,9 +619,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } let lhs_constructor = last_path_segment(qpath); - if method_path.ident.name == sym!(next) + if method_path.ident.name == sym::next && match_trait_method(cx, match_expr, &paths::ITERATOR) - && lhs_constructor.ident.name == sym!(Some) + && lhs_constructor.ident.name == sym::Some && (pat_args.is_empty() || !is_refutable(cx, &pat_args[0]) && !is_used_inside(cx, iter_expr, &arms[0].body) @@ -985,13 +985,13 @@ fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { _ => false, }; - is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) + is_slice || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { if_chain! { if let ExprKind::MethodCall(method, _, args, _) = expr.kind; - if method.ident.name == sym!(clone); + if method.ident.name == sym::clone; if args.len() == 1; if let Some(arg) = args.get(0); then { arg } else { expr } @@ -1355,7 +1355,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type); if path.ident.name.as_str() == "push"; then { return Some((self_expr, pushed_item)) @@ -1736,7 +1736,7 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: /// Checks for `for` loops over `Option`s and `Result`s. fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) { + if is_type_diagnostic_item(cx, ty, sym::option_type) { span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -1753,7 +1753,7 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { snippet(cx, arg.span, "_") ), ); - } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, ty, sym::result_type) { span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -2186,8 +2186,8 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind; - if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) + || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); if !self.check(&args[1], &args[0], expr); then { return } } @@ -2333,7 +2333,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { // will allow further borrows afterwards let ty = cx.typeck_results().expr_ty(e); is_iterable_array(ty, cx) || - is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + is_type_diagnostic_item(cx, ty, sym::vec_type) || match_type(cx, ty, &paths::LINKED_LIST) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || is_type_diagnostic_item(cx, ty, sym!(hashset_type)) || @@ -2890,7 +2890,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); then { let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 9c623821fdd..a1450b0d5fe 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -5,7 +5,7 @@ use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -83,9 +83,9 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } fn is_doc_hidden(attr: &Attribute) -> bool { - attr.has_name(sym!(doc)) + attr.has_name(sym::doc) && match attr.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym!(hidden)), + Some(l) => attr::list_contains_name(&l, sym::hidden), None => false, } } @@ -102,7 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive))); + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); let header_span = cx.sess.source_map().span_until_char(item.span, '{'); if let Some(snippet) = snippet_opt(cx, header_span); then { @@ -154,7 +154,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive))); + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); let header_span = find_header_span(cx, item, data); if let Some(snippet) = snippet_opt(cx, header_span); then { diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 22aa37e41fe..9e2c6c7f231 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -8,6 +8,7 @@ use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -97,9 +98,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) { + if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) { Some(Case::Option) - } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) { + } else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) { Some(Case::Result) } else { None diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 6d1c2ffbfbd..034cd99a9be 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if args.len() == 2; if method.ident.as_str() == "map"; let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::ITERATOR); + if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index d4c2e66ff4b..6b782385a38 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function. @@ -65,8 +66,8 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a if args.len() == 2 && method.ident.as_str() == "map"; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if match_trait_method(cx, expr, &paths::ITERATOR) - || is_type_diagnostic_item(cx, caller_ty, sym!(result_type)) - || is_type_diagnostic_item(cx, caller_ty, sym!(option_type)); + || is_type_diagnostic_item(cx, caller_ty, sym::result_type) + || is_type_diagnostic_item(cx, caller_ty, sym::option_type); then { Some(args) } else { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 076ef235b8b..e50d11a4d71 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function @@ -206,9 +207,9 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr let var_arg = &map_args[0]; let (map_type, variant, lint) = - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(option_type)) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::option_type) { ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(result_type)) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::result_type) { ("Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 331b6c6c34a..086dae9422f 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`. @@ -90,7 +91,7 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym!(vec_type)) + is_type_diagnostic_item(cx, ty, sym::vec_type) } fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4bdfca1a292..c6dca54e250 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -22,7 +22,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -662,7 +662,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp } } else { // not a block, don't lint - return; + return; }; let ty = cx.typeck_results().expr_ty(ex); @@ -840,7 +840,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); - if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) { + if is_type_diagnostic_item(cx, ex_ty, sym::result_type) { for arm in arms { if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); @@ -1509,6 +1509,7 @@ mod redundant_pattern_match { use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; + use rustc_span::sym; pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { @@ -1552,7 +1553,7 @@ mod redundant_pattern_match { if_chain! { if keyword == "while"; if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; - if method_path.ident.name == sym!(next); + if method_path.ident.name == sym::next; if match_trait_method(cx, op, &paths::ITERATOR); then { return; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 5dae7efad97..c83b6f2c329 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; /// Checks for the `INEFFICIENT_TO_STRING` lint pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { @@ -50,7 +51,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, ty, sym::string_type) { return true; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..167b8cc2422 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1677,7 +1677,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, item.span); - if item.ident.name == sym!(new); + if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); @@ -1767,7 +1767,7 @@ fn lint_or_fun_call<'tcx>( _ => (), } - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { + if is_type_diagnostic_item(cx, ty, sym::vec_type) { return; } } @@ -1864,11 +1864,11 @@ fn lint_expect_fun_call( hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, hir::ExprKind::MethodCall(method_name, _, call_args, _) => { if call_args.len() == 1 - && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref)) + && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) && { let arg_type = cx.typeck_results().expr_ty(&call_args[0]); let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) + *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type) } { &call_args[0] @@ -1886,7 +1886,7 @@ fn lint_expect_fun_call( // converted to string. fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, arg_ty, sym::string_type) { return false; } if let ty::Ref(_, ty, ..) = arg_ty.kind() { @@ -1973,9 +1973,9 @@ fn lint_expect_fun_call( } let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) { + let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) { "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) { "|_|" } else { return; @@ -2162,7 +2162,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { "" - } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { "&" } else { return; @@ -2188,14 +2188,14 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, obj_ty, sym::string_type) { lint_string_extend(cx, expr, args); } } fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym!(vec_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])); if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); @@ -2348,7 +2348,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ ); } } - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type) || matches!( &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), ty::Array(_, _) @@ -2381,7 +2381,7 @@ fn lint_iter_nth<'tcx>( let mut_str = if is_mut { "_mut" } else { "" }; let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vec_type)) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { "Vec" } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { "VecDeque" @@ -2434,7 +2434,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() { needs_ref = get_args_str.parse::().is_ok(); "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) { + } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) { needs_ref = get_args_str.parse::().is_ok(); "Vec" } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) { @@ -2520,7 +2520,7 @@ fn derefs_to_slice<'tcx>( match ty.kind() { ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), + ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), ty::Array(_, size) => size .try_eval_usize(cx.tcx, cx.param_env) .map_or(false, |size| size < 32), @@ -2530,7 +2530,7 @@ fn derefs_to_slice<'tcx>( } if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - if path.ident.name == sym!(iter) && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { + if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { Some(&args[0]) } else { None @@ -2555,9 +2555,9 @@ fn derefs_to_slice<'tcx>( fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((UNWRAP_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { Some((UNWRAP_USED, "a Result", "Err")) } else { None @@ -2607,7 +2607,7 @@ fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::E fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) { if_chain! { // lint if the caller of `ok()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym!(result_type)); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type); let result_type = cx.typeck_results().expr_ty(&ok_args[0]); if let Some(error_type) = get_error_type(cx, result_type); if has_debug_impl(error_type, cx); @@ -2637,7 +2637,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type) }, _ => false, }; @@ -2663,7 +2663,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } // lint if caller of `.map().flatten()` is an Option - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { let func_snippet = snippet(cx, map_args[1].span, ".."); let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( @@ -2687,8 +2687,8 @@ fn lint_map_unwrap_or_else<'tcx>( unwrap_args: &'tcx [hir::Expr<'_>], ) -> bool { // lint if the caller of `map()` is an `Option` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); if is_option || is_result { // Don't make a suggestion that may fail to compile due to mutably borrowing @@ -2741,8 +2741,8 @@ fn lint_map_unwrap_or_else<'tcx>( /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` @@ -3100,7 +3100,7 @@ fn lint_chars_cmp( if arg_char.len() == 1; if let hir::ExprKind::Path(ref qpath) = fun.kind; if let Some(segment) = single_segment_path(qpath); - if segment.ident.name == sym!(Some); + if segment.ident.name == sym::Some; then { let mut applicability = Applicability::MachineApplicable; let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); @@ -3375,7 +3375,7 @@ fn lint_option_as_ref_deref<'tcx>( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); - if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) { + if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { return; } @@ -3482,7 +3482,7 @@ fn lint_map_collect( if match_trait_method(cx, map_expr, &paths::ITERATOR); // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym!(result_type)); + if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); if result_t.is_unit(); @@ -3509,7 +3509,7 @@ fn lint_map_collect( /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1), + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::result_type) => substs.types().nth(1), _ => None, } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index d30b85d6a78..7763fd5f113 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -7,7 +7,7 @@ use rustc_hir::{self, HirId, Path}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{sym, Symbol}; use super::MAP_UNWRAP_OR; @@ -20,7 +20,7 @@ pub(super) fn lint<'tcx>( map_span: Span, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) { // Do not lint if the `map` argument uses identifiers in the `map` // argument that are also used in the `unwrap_or` argument diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 08b3eab9b7c..cde89983a26 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -3,6 +3,7 @@ use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::UNNECESSARY_LAZY_EVALUATIONS; @@ -14,8 +15,8 @@ pub(super) fn lint<'tcx>( args: &'tcx [hir::Expr<'_>], simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type); if is_option || is_result { if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 813f9c43948..009e3d8937e 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -14,6 +14,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Warns if there is missing doc for any documentable item @@ -105,10 +106,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { - attr.has_name(sym!(doc)) + attr.has_name(sym::doc) && match attr.meta_item_list() { None => false, - Some(l) => attr::list_contains_name(&l[..], sym!(hidden)), + Some(l) => attr::list_contains_name(&l[..], sym::hidden), } }); self.doc_hidden_stack.push(doc_hidden); @@ -128,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Enum(..) => "an enum", hir::ItemKind::Fn(..) => { // ignore main() - if it.ident.name == sym!(main) { + if it.ident.name == sym::main { let def_id = it.hir_id.owner; let def_key = cx.tcx.hir().def_key(def_id); if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) { diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 3eae45b2819..53abe6086ea 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_lint::{self, LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** it lints if an exported function, method, trait method with default impl, @@ -57,7 +58,7 @@ declare_clippy_lint! { } fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { - let has_inline = attrs.iter().any(|a| a.has_name(sym!(inline))); + let has_inline = attrs.iter().any(|a| a.has_name(sym::inline)); if !has_inline { span_lint( cx, diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index b71d5496a37..405c21d608d 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -112,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.attrs.iter().any(|a| a.has_name(sym!(automatically_derived))) { + if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.hir_id); } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7e933c674dd..5c92590f41e 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::can_type_implement_copy; @@ -204,12 +204,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let deref_span = spans_need_deref.get(&canonical_id); if_chain! { - if is_type_diagnostic_item(cx, ty, sym!(vec_type)); + if is_type_diagnostic_item(cx, ty, sym::vec_type); if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; if let Some(elem_ty) = path.segments.iter() - .find(|seg| seg.ident.name == sym!(Vec)) + .find(|seg| seg.ident.name == sym::Vec) .and_then(|ps| ps.args.as_ref()) .map(|params| params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), @@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, ty, sym::string_type) { if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { diag.span_suggestion( @@ -302,7 +302,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { /// Functions marked with these attributes must have the exact signature. fn requires_exact_signature(attrs: &[Attribute]) -> bool { attrs.iter().any(|attr| { - [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)] + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] .iter() .any(|&allow| attr.has_name(allow)) }) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 28d1322e946..68fdd0eb269 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for types with a `fn new() -> Self` method and no @@ -91,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // impl of `Default` return; } - if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) { + if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) { let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); let self_ty = cx.tcx.type_of(self_def_id); if_chain! { diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 603440c0f83..485888fa944 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -7,6 +7,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_span::symbol::{Ident, Symbol}; use std::cmp::Ordering; @@ -384,7 +385,7 @@ impl EarlyLintPass for NonExpressiveNames { } fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { - if !attrs.iter().any(|attr| attr.has_name(sym!(test))) { + if !attrs.iter().any(|attr| attr.has_name(sym::test)) { let mut visitor = SimilarNamesLocalVisitor { names: Vec::new(), cx, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index eb7624b25a3..681dbce9769 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -66,7 +67,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" - && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type)) + && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) } else { false } diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 4077aba6ef1..72dfccc1089 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. @@ -40,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { hir_id: hir::HirId, ) { if !matches!(fn_kind, FnKind::Closure(_)) - && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) + && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { lint_impl_body(cx, span, body); } diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 19d355e64ca..ceecc8dbc06 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -3,6 +3,7 @@ use if_chain::if_chain; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for manual re-implementations of `PartialEq::ne`. @@ -39,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { if trait_ref.path.res.def_id() == eq_trait; then { for impl_item in impl_items { - if impl_item.ident.name == sym!(ne) { + if impl_item.ident.name == sym::ne { span_lint_hir( cx, PARTIALEQ_NE_IMPL, diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 28816c3076d..030650c3256 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -10,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutabil use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_target::spec::Target; @@ -230,8 +230,8 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } for a in attrs { if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym!(proc_macro_derive)) - || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + if a.has_name(sym::proc_macro_derive) + || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)) { return; } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 6b1c848a946..dcb643a28ae 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::MultiSpan; +use rustc_span::{sym, MultiSpan}; use std::borrow::Cow; declare_clippy_lint! { @@ -181,7 +181,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { + if is_type_diagnostic_item(cx, ty, sym::vec_type) { let mut ty_snippet = None; if_chain! { if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; @@ -225,7 +225,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } - } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + } else if is_type_diagnostic_item(cx, ty, sym::string_type) { if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) { span_lint_and_then( cx, diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 0a2d1b5fbe6..e0996804a59 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -3,6 +3,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use std::fmt; declare_clippy_lint! { @@ -92,7 +93,7 @@ fn expr_as_ptr_offset_call<'tcx>( ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind { if is_expr_ty_raw_ptr(cx, &args[0]) { - if path_segment.ident.name == sym!(offset) { + if path_segment.ident.name == sym::offset { return Some((&args[0], &args[1], Method::Offset)); } if path_segment.ident.name == sym!(wrapping_offset) { diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index dbc676ae224..d9b280b7a85 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,6 +4,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use crate::utils::sugg::Sugg; use crate::utils::{ @@ -143,7 +144,7 @@ impl QuestionMark { fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expression); - is_type_diagnostic_item(cx, expr_ty, sym!(option_type)) + is_type_diagnostic_item(cx, expr_ty, sym::option_type) } fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index de54711d851..79e9a56af9a 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{Span, Spanned}; +use rustc_span::sym; use rustc_span::symbol::Ident; use std::cmp::Ordering; @@ -304,7 +305,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: if_chain! { // `.iter()` call if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; - if iter_path.ident.name == sym!(iter); + if iter_path.ident.name == sym::iter; // range expression in `.zip()` call: `0..x.len()` if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); if is_integer_const(cx, start, 0); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 344ed02361d..b4a9804fb25 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; +use rustc_span::sym; use std::convert::TryFrom; use std::ops::ControlFlow; @@ -115,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD) || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) - && is_type_diagnostic_item(cx, arg_ty, sym!(string_type))); + && is_type_diagnostic_item(cx, arg_ty, sym::string_type)); let from_deref = !from_borrow && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index ae601353009..d34e744eb94 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. @@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + } else if is_type_diagnostic_item(cx, ty, sym::string_type) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index a6e4252a0c8..7f4913a02cb 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -9,6 +9,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; @@ -141,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { } fn attr_is_cfg(attr: &Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) + attr.meta_item_list().is_some() && attr.has_name(sym::cfg) } fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3783bd78de2..0dd2da949c4 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -4,6 +4,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::sym; use if_chain::if_chain; @@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type)) + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::string_type) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 54b38d9f4ce..386987eb181 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -8,6 +8,7 @@ use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for manual swapping. @@ -197,7 +198,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym!(vec_type)) + || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) { return Slice::Swappable(lhs1, idx1, idx2); diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 3e747ec4ad9..6f6b6999bf0 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usages of `Err(x)?`. @@ -133,7 +134,7 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym!(result_type)); + if is_type_diagnostic_item(cx, ty, sym::result_type); let err_ty = subst.type_at(1); then { Some(err_ty) @@ -151,7 +152,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did); + if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did); let err_ty = ready_subst.type_at(1); then { @@ -170,11 +171,11 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did); + if cx.tcx.is_diagnostic_item(sym::option_type, ready_def.did); let some_ty = ready_subst.type_at(0); if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did); + if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did); let err_ty = some_subst.type_at(1); then { diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 45f3bc3ea85..c7d82da3b8b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -522,7 +522,7 @@ impl Types { ); return; // don't recurse into the type } - } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) { + } else if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) { if_chain! { // Get the _ part of Vec<_> if let Some(ref last) = last_path_segment(qpath).args; @@ -559,7 +559,7 @@ impl Types { return; // don't recurse into the type } } - } else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) { + } else if cx.tcx.is_diagnostic_item(sym::option_type, def_id) { if match_type_parameter(cx, qpath, &paths::OPTION).is_some() { span_lint( cx, @@ -1610,7 +1610,7 @@ fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { if names.is_empty() { return false; } - if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { + if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { return true; } } @@ -2777,7 +2777,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't } if match_path(ty_path, &paths::HASHMAP) { - if method.ident.name == sym!(new) { + if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashMap::default()".to_string()); } else if method.ident.name == sym!(with_capacity) { @@ -2790,7 +2790,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't ); } } else if match_path(ty_path, &paths::HASHSET) { - if method.ident.name == sym!(new) { + if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashSet::default()".to_string()); } else if method.ident.name == sym!(with_capacity) { diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 1307237dbc7..0bccfc15678 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -6,6 +6,7 @@ use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegme use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use rustc_span::symbol::Ident; declare_clippy_lint! { @@ -175,7 +176,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type)); + if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index ea4b8172c9c..f4a77e54dd1 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -11,6 +11,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail. @@ -92,11 +93,11 @@ fn collect_unwrap_info<'tcx>( invert: bool, ) -> Vec> { fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name) + is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name) } fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name) + is_type_diagnostic_item(cx, ty, sym::result_type) && ["is_ok", "is_err"].contains(&method_name) } if let ExprKind::Binary(op, left, right) = &expr.kind { @@ -168,8 +169,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { if_chain! { if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind; if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; - if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name); - let call_to_unwrap = method_name.ident.name == sym!(unwrap); + if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); + let call_to_unwrap = method_name.ident.name == sym::unwrap; if let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.ident.res == path.res); // Span contexts should not differ with the conditional branch diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 0f8797243ec..fde31029330 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` @@ -57,8 +57,8 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { // first check if it's a method or function if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::result_type) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::option_type); then { lint_impl_body(cx, impl_item.span, impl_item); } @@ -82,8 +82,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // check for `expect` if let Some(arglists) = method_chain_args(expr, &["expect"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); - if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) - || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type) { self.result.push(expr.span); } @@ -92,8 +92,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); - if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) - || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type) { self.result.push(expr.span); } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 4e4a206a583..c6194b0c6de 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -9,6 +9,7 @@ use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls @@ -106,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, a, sym!(result_type)); + if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); @@ -136,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { then { if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); - if is_type_diagnostic_item(cx, a, sym!(result_type)); + if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 0a8a4a5f9ae..85e7f055e79 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -52,6 +52,7 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; +use rustc_span::sym as rustc_sym; use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; @@ -974,7 +975,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived))) + attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived)) } /// Remove blocks around an expression. diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index d9d60fffcd7..ff414f748ef 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -10,8 +10,7 @@ use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span, Symbol}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -224,7 +223,7 @@ impl EarlyLintPass for Write { .expect("path has at least one segment") .ident .name; - if trait_name == sym!(Debug) { + if trait_name == sym::Debug { self.in_debug_impl = true; } } -- cgit 1.4.1-3-g733a5 From b2332a7357b5da7220592aea4c711609eed54ef7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 11:47:17 -0600 Subject: Change lint to use const sym --- clippy_lints/src/utils/internal_lints.rs | 4 ++-- tests/ui/match_type_on_diag_item.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 6ca72d895c8..8b59a9541a7 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -225,7 +225,7 @@ declare_clippy_lint! { /// /// Good: /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type)) + /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type) /// ``` pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, internal, @@ -724,7 +724,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { expr.span, "usage of `utils::match_type()` on a type diagnostic item", "try", - format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), + format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr index 5e5fe9e3a3e..82465dbaf6e 100644 --- a/tests/ui/match_type_on_diag_item.stderr +++ b/tests/ui/match_type_on_diag_item.stderr @@ -2,7 +2,7 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` | note: the lint level is defined here --> $DIR/match_type_on_diag_item.rs:1:9 @@ -15,19 +15,19 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:43:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:46:17 | LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From a6611de75a9c6cf8b0ccfa371491a646a743667f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 12:55:05 -0600 Subject: Include bindings as machine applicable --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index aec1b7e2e46..fe54a238aa4 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -35,7 +35,8 @@ pub(super) fn lint<'tcx>( let applicability = if body .params .iter() - .all(|param| matches!(param.pat.kind, hir::PatKind::Wild)) + // bindings are checked to be unused above + .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) { Applicability::MachineApplicable } else { -- cgit 1.4.1-3-g733a5 From 315bab0ea186d5324fba28ccd5db82ecc7996cc4 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 10 Sep 2020 20:14:23 +0200 Subject: Add `from_iter_instead_of_collect` lint implementation --- clippy_lints/src/methods/mod.rs | 59 +++++++++++++++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 1 + 2 files changed, 60 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..93b5d4e7efc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1369,6 +1369,38 @@ declare_clippy_lint! { "using `.map(_).collect::()`, which can be replaced with `try_for_each`" } +declare_clippy_lint! { + /// **What it does:** Checks for `from_iter()` function calls that implements `FromIterator` + /// trait. + /// + /// **Why is this bad?** Makes code less readable especially in method chaining. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::iter::FromIterator; + /// + /// let five_fives = std::iter::repeat(5).take(5); + /// + /// let v = Vec::from_iter(five_fives); + /// + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// ``` + /// Use instead: + /// ```rust + /// let five_fives = std::iter::repeat(5).take(5); + /// + /// let v: Vec = five_fives.collect(); + /// + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// ``` + pub FROM_ITER_INSTEAD_OF_COLLECT, + style, + "use `.collect()` instead of `::from_iter()`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1419,6 +1451,7 @@ declare_lint_pass!(Methods => [ OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, MAP_COLLECT_RESULT_UNIT, + FROM_ITER_INSTEAD_OF_COLLECT, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1505,6 +1538,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } match expr.kind { + hir::ExprKind::Call(ref func, ref args) => { + if let hir::ExprKind::Path(path) = &func.kind { + let path_segment = last_path_segment(path); + if path_segment.ident.name.as_str() == "from_iter" { + lint_from_iter(cx, expr, args); + } + } + }, hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); @@ -3831,6 +3872,24 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); } +fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let ty = cx.typeck_results().expr_ty(expr); + let id = get_trait_def_id(cx, &paths::FROM_ITERATOR_TRAIT).unwrap(); + + if implements_trait(cx, ty, id, &[]) { + // `expr` implements `FromIterator` trait + let iter_expr = snippet(cx, args[0].span, ".."); + span_lint_and_help( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "use `.collect()` instead of `::from_iter()`", + None, + &format!("consider using `{}.collect()`", iter_expr), + ); + } +} + fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 736a531eda6..3aade8ca8a2 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,6 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; +pub const FROM_ITERATOR_TRAIT: [&str; 3] = ["std", "iter", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; -- cgit 1.4.1-3-g733a5 From 9d6eedf5f2fccfc94f36473012794b2c679d3ad3 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 10 Sep 2020 20:16:28 +0200 Subject: Run `cargo dev update_lints` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 +++ src/lintlist/mod.rs | 7 +++++++ 3 files changed, 11 insertions(+) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..50f03126c7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1732,6 +1732,7 @@ Released 2018-09-13 [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..fa4b6d11623 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -693,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::FILTER_NEXT, &methods::FIND_MAP, &methods::FLAT_MAP_IDENTITY, + &methods::FROM_ITER_INSTEAD_OF_COLLECT, &methods::GET_UNWRAP, &methods::INEFFICIENT_TO_STRING, &methods::INTO_ITER_ON_REF, @@ -1422,6 +1423,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::EXPECT_FUN_CALL), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), + LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITER_CLONED_COLLECT), @@ -1620,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), + LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), LintId::of(&methods::ITER_NEXT_SLICE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..7a1ebc3d2dd 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -753,6 +753,13 @@ vec![ deprecation: None, module: "drop_forget_ref", }, + Lint { + name: "from_iter_instead_of_collect", + group: "style", + desc: "use `.collect()` instead of `::from_iter()`", + deprecation: None, + module: "methods", + }, Lint { name: "future_not_send", group: "nursery", -- cgit 1.4.1-3-g733a5 From 8a5d78b71a71317314d7dccef7c73b523f0a44c2 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Fri, 11 Sep 2020 13:29:52 +0200 Subject: Fix `from_iter_instead_of_collect` lint crashing on exprs without path segment --- clippy_lints/src/methods/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 93b5d4e7efc..c0c39dae865 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1540,8 +1540,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { match expr.kind { hir::ExprKind::Call(ref func, ref args) => { if let hir::ExprKind::Path(path) = &func.kind { - let path_segment = last_path_segment(path); - if path_segment.ident.name.as_str() == "from_iter" { + if match_qpath(path, &["from_iter"]) { lint_from_iter(cx, expr, args); } } -- cgit 1.4.1-3-g733a5 From 8906040445a6700be2aaf40ec09937b84dea5d6a Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:04:05 +0200 Subject: Improvements from PR feedback --- clippy_lints/src/methods/mod.rs | 14 ++++++++------ clippy_lints/src/utils/paths.rs | 2 +- tests/ui/from_iter_instead_of_collect.rs | 10 +++------- tests/ui/from_iter_instead_of_collect.stderr | 15 ++++++--------- tests/ui/from_iter_instead_of_collect.stdout | 0 5 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 tests/ui/from_iter_instead_of_collect.stdout (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0c39dae865..70be8909c43 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1370,10 +1370,11 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `from_iter()` function calls that implements `FromIterator` + /// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` /// trait. /// - /// **Why is this bad?** Makes code less readable especially in method chaining. + /// **Why is this bad?** It is recommended style to use collect. See + /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) /// /// **Known problems:** None. /// @@ -3873,18 +3874,19 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); - let id = get_trait_def_id(cx, &paths::FROM_ITERATOR_TRAIT).unwrap(); + let id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); if implements_trait(cx, ty, id, &[]) { // `expr` implements `FromIterator` trait let iter_expr = snippet(cx, args[0].span, ".."); - span_lint_and_help( + span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, "use `.collect()` instead of `::from_iter()`", - None, - &format!("consider using `{}.collect()`", iter_expr), + "consider using", + format!("`{}.collect()`", iter_expr), + Applicability::MaybeIncorrect ); } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 3aade8ca8a2..8afbd8930b6 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,7 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; -pub const FROM_ITERATOR_TRAIT: [&str; 3] = ["std", "iter", "FromIterator"]; +pub const FROM_ITERATOR: [&str; 3] = ["std", "iter", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 7a1bc64f4bd..9071be33c64 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -4,12 +4,8 @@ use std::collections::HashMap; use std::iter::FromIterator; fn main() { - { - let iter_expr = std::iter::repeat(5).take(5); + let iter_expr = std::iter::repeat(5).take(5); - Vec::from_iter(iter_expr); - HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - //let v: Vec = iter_expr.collect(); - let a: Vec = Vec::new(); - } + Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index cfb92dfbbfd..1bc787aa795 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,19 +1,16 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:10:9 + --> $DIR/from_iter_instead_of_collect.rs:9:5 | -LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` - = help: consider using `iter_expr.collect()` error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:11:9 + --> $DIR/from_iter_instead_of_collect.rs:10:5 | -LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `vec![5, 5, 5, 5].iter().enumerate().collect()` +LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``vec![5, 5, 5, 5].iter().enumerate().collect()`` error: aborting due to 2 previous errors diff --git a/tests/ui/from_iter_instead_of_collect.stdout b/tests/ui/from_iter_instead_of_collect.stdout deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit 1.4.1-3-g733a5 From e320dd30428aeb85577b36afa594429a41bc07f6 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:34:36 +0200 Subject: Improve: error message --- clippy_lints/src/utils/paths.rs | 2 +- tests/ui/from_iter_instead_of_collect.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 8afbd8930b6..95fe8733421 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,7 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; -pub const FROM_ITERATOR: [&str; 3] = ["std", "iter", "FromIterator"]; +pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 9071be33c64..25b87a0a903 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -5,7 +5,7 @@ use std::iter::FromIterator; fn main() { let iter_expr = std::iter::repeat(5).take(5); - Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 1bc787aa795..3263005d7ec 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,5 +1,5 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:9:5 + --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` -- cgit 1.4.1-3-g733a5 From 854f2cef06f60b37a3176b17b27c1104692f6255 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:36:49 +0200 Subject: Run `cargo dev fmt` --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70be8909c43..013ed67b8e5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3886,7 +3886,7 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< "use `.collect()` instead of `::from_iter()`", "consider using", format!("`{}.collect()`", iter_expr), - Applicability::MaybeIncorrect + Applicability::MaybeIncorrect, ); } } -- cgit 1.4.1-3-g733a5 From abdb7aeb55461f0355829be49a09ed86af066ae8 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:44:12 +0200 Subject: Remove backticks --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 013ed67b8e5..2119cb28fc3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3885,7 +3885,7 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< expr.span, "use `.collect()` instead of `::from_iter()`", "consider using", - format!("`{}.collect()`", iter_expr), + format!("{}.collect()", iter_expr), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 3263005d7ec..7f248beadbe 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -2,7 +2,7 @@ error: use `.collect()` instead of `::from_iter()` --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `iter_expr.collect()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` @@ -10,7 +10,7 @@ error: use `.collect()` instead of `::from_iter()` --> $DIR/from_iter_instead_of_collect.rs:10:5 | LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``vec![5, 5, 5, 5].iter().enumerate().collect()`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From f359fb872b405fca196f40eadd341d1d06f1fb8b Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 19:04:19 +0200 Subject: Improve error message --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2119cb28fc3..fde43f0055d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3883,8 +3883,8 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, + "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", - "consider using", format!("{}.collect()", iter_expr), Applicability::MaybeIncorrect, ); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 7f248beadbe..46bdc2f4e19 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,16 +1,16 @@ -error: use `.collect()` instead of `::from_iter()` +error: usage of `FromIterator::from_iter` --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `iter_expr.collect()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` -error: use `.collect()` instead of `::from_iter()` +error: usage of `FromIterator::from_iter` --> $DIR/from_iter_instead_of_collect.rs:10:5 | LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `vec![5, 5, 5, 5].iter().enumerate().collect()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 52d1ea3c9ad8ea97350ba7a0ca0a8e172cfcae78 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Mon, 12 Oct 2020 17:27:50 +0200 Subject: Fix: Don't show lint for types that doesn't implement Iterator --- clippy_lints/src/methods/mod.rs | 7 +++++-- tests/ui/from_iter_instead_of_collect.rs | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fde43f0055d..521b151f5e1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3874,9 +3874,12 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); - let id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); + let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if implements_trait(cx, ty, id, &[]) { + let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); + let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap(); + + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) { // `expr` implements `FromIterator` trait let iter_expr = snippet(cx, args[0].span, ".."); span_lint_and_sugg( diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 25b87a0a903..045eb3133d3 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -8,4 +8,6 @@ fn main() { Vec::from_iter(iter_expr); HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + + Vec::from_iter(vec![42u32]); } -- cgit 1.4.1-3-g733a5 From ddf23d649ae02dde7aed22ec6699a736492b7184 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Sat, 24 Oct 2020 13:53:09 +0200 Subject: Fix: Use `.collect()` instead of `::fromIterator()` --- clippy_lints/src/lifetimes.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index c8a5a9c9431..4d737b3f49b 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -16,7 +16,6 @@ use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; -use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -214,14 +213,15 @@ fn could_use_elision<'tcx>( } if allowed_lts - .intersection(&FxHashSet::from_iter( - input_visitor + .intersection( + &input_visitor .nested_elision_site_lts .iter() .chain(output_visitor.nested_elision_site_lts.iter()) .cloned() - .filter(|v| matches!(v, RefLt::Named(_))), - )) + .filter(|v| matches!(v, RefLt::Named(_))) + .collect(), + ) .next() .is_some() { -- cgit 1.4.1-3-g733a5 From f5166e81b1065983de15f883ae04a81d30f75edf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 4 Nov 2020 22:41:15 +0100 Subject: Run cargo dev fmt --- clippy_lints/src/consts.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index c8bbc9ce2b0..0035ded9356 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -7,10 +7,10 @@ use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; use rustc_lint::LateContext; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, ScalarInt}; +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_middle::mir::interpret::Scalar; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; use std::convert::TryInto; @@ -501,7 +501,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn miri_to_const(result: &ty::Const<'_>) -> Option { - use rustc_middle::mir::interpret::{ConstValue}; + use rustc_middle::mir::interpret::ConstValue; match result.val { ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { match result.ty.kind() { -- cgit 1.4.1-3-g733a5 From a4acb3164ad93d8fe444783ad588a8b5d71dacd9 Mon Sep 17 00:00:00 2001 From: Urcra Date: Wed, 4 Nov 2020 23:39:52 +0100 Subject: Fix example for cargo common data --- clippy_lints/src/cargo_common_metadata.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 76a000157df..0d294761af5 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -23,6 +23,21 @@ declare_clippy_lint! { /// [package] /// name = "clippy" /// version = "0.0.212" + /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" + /// repository = "https://github.com/rust-lang/rust-clippy" + /// readme = "README.md" + /// license = "MIT OR Apache-2.0" + /// keywords = ["clippy", "lint", "plugin"] + /// categories = ["development-tools", "development-tools::cargo-plugins"] + /// ``` + /// + /// Should include an authors field like: + /// + /// ```toml + /// # This `Cargo.toml` includes all common metadata + /// [package] + /// name = "clippy" + /// version = "0.0.212" /// authors = ["Someone "] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" -- cgit 1.4.1-3-g733a5 From bc27d1492d60ecfb0673629e28bc4bbe0b7fd886 Mon Sep 17 00:00:00 2001 From: Patrick José Pereira Date: Thu, 8 Oct 2020 00:19:56 -0300 Subject: Add string_from_utf8_as_bytes linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/strings.rs | 68 +++++++++++++++++++++++++++++-- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 ++++ tests/ui/string_from_utf8_as_bytes.fixed | 6 +++ tests/ui/string_from_utf8_as_bytes.rs | 6 +++ tests/ui/string_from_utf8_as_bytes.stderr | 10 +++++ 8 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/ui/string_from_utf8_as_bytes.fixed create mode 100644 tests/ui/string_from_utf8_as_bytes.rs create mode 100644 tests/ui/string_from_utf8_as_bytes.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..aeccf6cf83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1952,6 +1952,7 @@ Released 2018-09-13 [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign [`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..d29ba33064c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -826,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, + &strings::STRING_FROM_UTF8_AS_BYTES, &strings::STRING_LIT_AS_BYTES, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, @@ -1515,6 +1516,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), + LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1738,6 +1740,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), + LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3783bd78de2..a55df1e5502 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,5 +1,5 @@ use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -8,7 +8,10 @@ use rustc_span::source_map::Spanned; use if_chain::if_chain; use crate::utils::SpanlessEq; -use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg}; +use crate::utils::{ + get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, + span_lint_and_sugg, +}; declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without @@ -173,16 +176,75 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { } } +declare_clippy_lint! { + /// **What it does:** Check if the string is transformed to byte array and casted back to string. + /// + /// **Why is this bad?** It's unnecessary, the string can be used directly. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); + /// ``` + /// could be written as + /// ```rust + /// let _ = &"Hello World!"[6..11]; + /// ``` + pub STRING_FROM_UTF8_AS_BYTES, + complexity, + "casting string slices to byte slices and back" +} + // Max length a b"foo" string can take const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; -declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]); +declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use crate::utils::{snippet, snippet_with_applicability}; use rustc_ast::LitKind; + if_chain! { + // Find std::str::converts::from_utf8 + if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); + + // Find string::as_bytes + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind; + if let ExprKind::Index(ref left, ref right) = args.kind; + let (method_names, expressions, _) = method_calls(left, 1); + if method_names.len() == 1; + if expressions.len() == 1; + if expressions[0].len() == 1; + if method_names[0] == sym!(as_bytes); + + // Check for slicer + if let ExprKind::Struct(ref path, _, _) = right.kind; + if let QPath::LangItem(LangItem::Range, _) = path; + + then { + let mut applicability = Applicability::MachineApplicable; + let string_expression = &expressions[0][0]; + + let snippet_app = snippet_with_applicability( + cx, + string_expression.span, "..", + &mut applicability, + ); + + span_lint_and_sugg( + cx, + STRING_FROM_UTF8_AS_BYTES, + e.span, + "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", + "try", + format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")), + applicability + ) + } + } + if_chain! { if let ExprKind::MethodCall(path, _, args, _) = &e.kind; if path.ident.name == sym!(as_bytes); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd72fdd61fd..0d0ecf9ae9f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -120,6 +120,7 @@ pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..4d824cf94be 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2230,6 +2230,13 @@ vec![ deprecation: None, module: "methods", }, + Lint { + name: "string_from_utf8_as_bytes", + group: "complexity", + desc: "casting string slices to byte slices and back", + deprecation: None, + module: "strings", + }, Lint { name: "string_lit_as_bytes", group: "nursery", diff --git a/tests/ui/string_from_utf8_as_bytes.fixed b/tests/ui/string_from_utf8_as_bytes.fixed new file mode 100644 index 00000000000..6e665cdd563 --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.fixed @@ -0,0 +1,6 @@ +// run-rustfix +#![warn(clippy::string_from_utf8_as_bytes)] + +fn main() { + let _ = Some(&"Hello World!"[6..11]); +} diff --git a/tests/ui/string_from_utf8_as_bytes.rs b/tests/ui/string_from_utf8_as_bytes.rs new file mode 100644 index 00000000000..670d206d367 --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.rs @@ -0,0 +1,6 @@ +// run-rustfix +#![warn(clippy::string_from_utf8_as_bytes)] + +fn main() { + let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); +} diff --git a/tests/ui/string_from_utf8_as_bytes.stderr b/tests/ui/string_from_utf8_as_bytes.stderr new file mode 100644 index 00000000000..bf5e5d33e8f --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.stderr @@ -0,0 +1,10 @@ +error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary + --> $DIR/string_from_utf8_as_bytes.rs:5:13 + | +LL | let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&"Hello World!"[6..11])` + | + = note: `-D clippy::string-from-utf8-as-bytes` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f83762b79cf23bfa91d77d6f04ec2b87b2159b07 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 18:03:16 +0100 Subject: Skip rustfmt as it is wanted for this test --- clippy_lints/src/reference.rs | 23 ++++++++++------------- clippy_lints/src/try_err.rs | 6 ------ tests/ui/deref_addrof.fixed | 1 + tests/ui/deref_addrof.rs | 1 + tests/ui/deref_addrof.stderr | 4 ++-- 5 files changed, 14 insertions(+), 21 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 8646d616735..35a1310d68b 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,11 +1,10 @@ use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; +use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::BytePos; -// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -53,31 +52,29 @@ impl EarlyLintPass for DerefAddrOf { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` let trim_leading_whitespaces = |span| { - if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snippet_opt(cx, span).and_then(|snip| { + #[allow(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()).map(|pos| { span.lo() + BytePos(pos as u32) }) - }) { - e.span.with_lo(start_no_whitespace) - } else { - span - } + }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) }; let rpos = if *mutability == Mutability::Mut { macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() } else { - macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + macro_source.rfind('&').expect("already checked this is a reference") + "&".len() }; + #[allow(clippy::cast_possible_truncation)] let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + snippet_with_applicability(cx, span, "_", &mut applicability) } else { - snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + snippet_with_applicability(cx, e.span, "_", &mut applicability) } } else { - snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() - }; + snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability) + }.to_string(); span_lint_and_sugg( cx, DEREF_ADDROF, diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e6d8e7521a8..9c1185d30f2 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -92,13 +92,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - // println!("\n\n{:?}", in_macro(expr.span)); - // println!("{:#?}", snippet(cx, err_arg.span, "_")); let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { - // println!("from expansion"); snippet_with_macro_callsite(cx, err_arg.span, "_") } else { - // println!("just a snippet"); snippet(cx, err_arg.span, "_") }; let suggestion = if err_ty == expr_err_ty { @@ -106,8 +102,6 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; - // println!("origin_snippet: {:#?}", origin_snippet); - // println!("suggestion: {:#?}", suggestion); span_lint_and_sugg( cx, diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 689e9d55223..0795900558b 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -38,6 +38,7 @@ fn main() { let b = *aref; } +#[rustfmt::skip] macro_rules! m { ($visitor: expr) => { $visitor diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 57effce5d12..60c4318601b 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -38,6 +38,7 @@ fn main() { let b = **&aref; } +#[rustfmt::skip] macro_rules! m { ($visitor: expr) => { *& $visitor diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 52c1f7d1da8..e85b30fa56e 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -49,7 +49,7 @@ LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:43:9 + --> $DIR/deref_addrof.rs:44:9 | LL | *& $visitor | ^^^^^^^^^^^ help: try this: `$visitor` @@ -60,7 +60,7 @@ LL | m!(self) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:50:9 + --> $DIR/deref_addrof.rs:51:9 | LL | *& mut $visitor | ^^^^^^^^^^^^^^^ help: try this: `$visitor` -- cgit 1.4.1-3-g733a5 From 83e75f92079909aa07306633f26f42eccfb608e1 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 5 Nov 2020 18:00:34 +0100 Subject: Fix incorrect suggestion for `try_err` lint when `Err` arg is itself a macro --- clippy_lints/src/try_err.rs | 9 ++++++--- tests/ui/try_err.fixed | 18 ++++++++++++++++++ tests/ui/try_err.rs | 18 ++++++++++++++++++ tests/ui/try_err.stderr | 21 ++++++++++++++++----- 4 files changed, 58 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 9c1185d30f2..e1dec63a98a 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,6 +1,6 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, - span_lint_and_sugg, + differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -91,8 +91,11 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { }; let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let differing_contexts = differing_macro_contexts(expr.span, err_arg.span); - let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { + let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts { + snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_") + } else if err_arg.span.from_expansion() && !in_macro(expr.span) { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 053dd45f23e..aa43e69f79e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -88,8 +88,26 @@ macro_rules! try_validation { }}; } +macro_rules! ret_one { + () => { + 1 + }; +} + +macro_rules! try_validation_in_macro { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => return Err(ret_one!()), + } + }}; +} + fn calling_macro() -> Result { + // macro try_validation!(Ok::<_, i32>(5)); + // `Err` arg is another macro + try_validation_in_macro!(Ok::<_, i32>(5)); Ok(5) } diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 215ca6a07e6..df3a9dc5367 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -88,8 +88,26 @@ macro_rules! try_validation { }}; } +macro_rules! ret_one { + () => { + 1 + }; +} + +macro_rules! try_validation_in_macro { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => Err(ret_one!())?, + } + }}; +} + fn calling_macro() -> Result { + // macro try_validation!(Ok::<_, i32>(5)); + // `Err` arg is another macro + try_validation_in_macro!(Ok::<_, i32>(5)); Ok(5) } diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 443c8d08472..3905ed2476b 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -40,28 +40,39 @@ LL | try_validation!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:122:9 + --> $DIR/try_err.rs:101:23 + | +LL | Err(_) => Err(ret_one!())?, + | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` +... +LL | try_validation_in_macro!(Ok::<_, i32>(5)); + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:140:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:129:9 + --> $DIR/try_err.rs:147:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:131:9 + --> $DIR/try_err.rs:149:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:139:9 + --> $DIR/try_err.rs:157:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 1e4ce0fcaa2bd99ce51e1857767788cc498e62c0 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 6 Nov 2020 04:02:41 +0900 Subject: Fix `await_holding_refcell_ref` examples for clarify --- clippy_lints/src/await_holding_invalid.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index fcebb54c6c2..ca819663fde 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -65,8 +65,8 @@ declare_clippy_lint! { /// use std::cell::RefCell; /// /// async fn foo(x: &RefCell) { - /// let b = x.borrow_mut()(); - /// *ref += 1; + /// let mut y = x.borrow_mut(); + /// *y += 1; /// bar.await; /// } /// ``` @@ -77,8 +77,8 @@ declare_clippy_lint! { /// /// async fn foo(x: &RefCell) { /// { - /// let b = x.borrow_mut(); - /// *ref += 1; + /// let mut y = x.borrow_mut(); + /// *y += 1; /// } /// bar.await; /// } -- cgit 1.4.1-3-g733a5 From 1624b00bde42a674c50a03e63868e8b4d08b6b49 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 5 Nov 2020 13:56:57 +0900 Subject: Fix suggestion to add unneeded space in `manual_async` --- clippy_lints/src/manual_async_fn.rs | 17 ++++++++- tests/ui/manual_async_fn.fixed | 14 ++++++- tests/ui/manual_async_fn.rs | 20 ++++++++++ tests/ui/manual_async_fn.stderr | 74 +++++++++++++++++++++++++++++++++---- 4 files changed, 115 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 864d1ea87f5..e9d65abb443 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -69,7 +69,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = header_snip.rfind("->"); + if let Some(ret_pos) = header_snip.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec = header_snip.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); @@ -194,7 +207,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, }, _ => { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip))) + snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip))) }, } } diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 4f551690c43..5184f6fdb88 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -7,7 +7,19 @@ use std::future::Future; async fn fut() -> i32 { 42 } -async fn empty_fut() {} +#[rustfmt::skip] +async fn fut2() -> i32 { 42 } + +#[rustfmt::skip] +async fn fut3() -> i32 { 42 } + +async fn empty_fut() {} + +#[rustfmt::skip] +async fn empty_fut2() {} + +#[rustfmt::skip] +async fn empty_fut3() {} async fn core_fut() -> i32 { 42 } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index 6ed60309947..68c0e591f0b 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -9,10 +9,30 @@ fn fut() -> impl Future { async { 42 } } +#[rustfmt::skip] +fn fut2() ->impl Future { + async { 42 } +} + +#[rustfmt::skip] +fn fut3()-> impl Future { + async { 42 } +} + fn empty_fut() -> impl Future { async {} } +#[rustfmt::skip] +fn empty_fut2() ->impl Future { + async {} +} + +#[rustfmt::skip] +fn empty_fut3()-> impl Future { + async {} +} + fn core_fut() -> impl core::future::Future { async move { 42 } } diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index ccd82867427..fdd43db3255 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -15,14 +15,44 @@ LL | fn fut() -> impl Future { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:12:1 + --> $DIR/manual_async_fn.rs:13:1 + | +LL | fn fut2() ->impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn fut2() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut2() ->impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:18:1 + | +LL | fn fut3()-> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn fut3() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut3()-> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:22:1 | LL | fn empty_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and remove the return type | -LL | async fn empty_fut() { +LL | async fn empty_fut() { | ^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | @@ -30,7 +60,37 @@ LL | fn empty_fut() -> impl Future {} | ^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:16:1 + --> $DIR/manual_async_fn.rs:27:1 + | +LL | fn empty_fut2() ->impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut2() { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut2() ->impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:32:1 + | +LL | fn empty_fut3()-> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut3() { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut3()-> impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:36:1 | LL | fn core_fut() -> impl core::future::Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +105,7 @@ LL | fn core_fut() -> impl core::future::Future { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:38:5 + --> $DIR/manual_async_fn.rs:58:5 | LL | fn inh_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +125,7 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:73:1 + --> $DIR/manual_async_fn.rs:93:1 | LL | fn elided(_: &i32) -> impl Future + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +140,7 @@ LL | fn elided(_: &i32) -> impl Future + '_ { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:82:1 + --> $DIR/manual_async_fn.rs:102:1 | LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,5 +154,5 @@ help: move the body of the async block to the enclosing function LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } | ^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 5f57608604bd35bd11c1f33cbd7202250e072b54 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Fri, 6 Nov 2020 14:38:46 +0300 Subject: do not trigger map_clone in the case of &mut --- clippy_lints/src/map_clone.rs | 8 +++++--- tests/ui/map_clone.fixed | 8 ++++++++ tests/ui/map_clone.rs | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 034cd99a9be..9a00608ce39 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -80,9 +80,11 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { let obj_ty = cx.typeck_results().expr_ty(&obj[0]); - if let ty::Ref(_, ty, _) = obj_ty.kind() { - let copy = is_copy(cx, ty); - lint(cx, e.span, args[0].span, copy); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, ty); + lint(cx, e.span, args[0].span, copy); + } } else { lint_needless_cloning(cx, e.span, args[0].span); } diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 81c7f659efb..6e3a8e67e81 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -44,4 +44,12 @@ fn main() { let v = vec![&mut d]; let _: Vec = v.into_iter().map(|&mut x| x).collect(); } + + // Issue #6299 + { + let mut aa = 5; + let mut bb = 3; + let items = vec![&mut aa, &mut bb]; + let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 8ed164f0ed5..6fd395710d4 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -44,4 +44,12 @@ fn main() { let v = vec![&mut d]; let _: Vec = v.into_iter().map(|&mut x| x).collect(); } + + // Issue #6299 + { + let mut aa = 5; + let mut bb = 3; + let items = vec![&mut aa, &mut bb]; + let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + } } -- cgit 1.4.1-3-g733a5 From b7892c6a26adf6173758641d3f7deb0ae972959d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 6 Nov 2020 22:54:38 +0900 Subject: Refactor to make getting position just before RArrow a common function --- clippy_lints/src/manual_async_fn.rs | 17 ++--------------- clippy_lints/src/unused_unit.rs | 29 ++++++++--------------------- clippy_lints/src/utils/mod.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 36 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index e9d65abb443..7b3b450ef93 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use crate::utils::paths::FUTURE_FROM_GENERATOR; -use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then}; +use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -69,20 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = header_snip.rfind("->").map(|rpos| { - let mut rpos = rpos; - let chars: Vec = header_snip.chars().collect(); - while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } - } - break; - } - rpos - }); + if let Some(ret_pos) = position_before_rarrow(header_snip.clone()); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index b1339c3d639..f61fd2ecd73 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::span_lint_and_sugg; +use crate::utils::{position_before_rarrow, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -120,26 +120,13 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source - .rfind("->") - .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - let mut rpos = rpos; - let chars: Vec = fn_source.chars().collect(); - while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } - } - break; - } - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) + position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) } else { (ty.span, Applicability::MaybeIncorrect) }; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 85e7f055e79..8e4149df032 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -659,6 +659,35 @@ pub fn indent_of(cx: &T, span: Span) -> Option { snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } +/// Returns the positon just before rarrow +/// +/// ```rust,ignore +/// fn into(self) -> () {} +/// ^ +/// // in case of unformatted code +/// fn into2(self)-> () {} +/// ^ +/// fn into3(self) -> () {} +/// ^ +/// ``` +#[allow(clippy::needless_pass_by_value)] +pub fn position_before_rarrow(s: String) -> Option { + s.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec = s.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }) +} + /// Extends the span to the beginning of the spans line, incl. whitespaces. /// /// ```rust,ignore -- cgit 1.4.1-3-g733a5 From 5253595b3bb6a3cc6502bb2327590e582d1d27e9 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 6 Nov 2020 19:34:34 +0100 Subject: FROM_ITER_INSTEAD_OF_COLLECT: avoid unwrapping unconditionally Fixes #6302 --- clippy_lints/src/methods/mod.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7186656f4e1..a3fa3bdd36a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3901,21 +3901,24 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); - let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); - let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap(); + if_chain! { + if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); + if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR); - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) { - // `expr` implements `FromIterator` trait - let iter_expr = snippet(cx, args[0].span, ".."); - span_lint_and_sugg( - cx, - FROM_ITER_INSTEAD_OF_COLLECT, - expr.span, - "usage of `FromIterator::from_iter`", - "use `.collect()` instead of `::from_iter()`", - format!("{}.collect()", iter_expr), - Applicability::MaybeIncorrect, - ); + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + then { + // `expr` implements `FromIterator` trait + let iter_expr = snippet(cx, args[0].span, ".."); + span_lint_and_sugg( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "usage of `FromIterator::from_iter`", + "use `.collect()` instead of `::from_iter()`", + format!("{}.collect()", iter_expr), + Applicability::MaybeIncorrect, + ); + } } } -- cgit 1.4.1-3-g733a5 From c6a91df838b17a2366346db664072d7914ab241a Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 22 Oct 2020 23:39:25 -0700 Subject: Enable empty_loop lint for no_std crates We skip the lint if the `loop {}` is in the `#[panic_handler]` as the main recommendation we give is to panic, which obviously isn't possible in a panic handler. Signed-off-by: Joe Richey --- clippy_lints/src/loops.rs | 28 +++++++++++++--------------- clippy_lints/src/utils/mod.rs | 7 +++++++ tests/ui/empty_loop_no_std.rs | 7 ++++++- tests/ui/empty_loop_no_std.stderr | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 tests/ui/empty_loop_no_std.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 32c2562ee95..2a53210efe3 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -4,10 +4,10 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, - match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, sugg, SpanlessEq, + indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, + snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, + span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -543,17 +543,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { - // also check for empty `loop {}` statements - // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler]) - if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { - span_lint_and_help( - cx, - EMPTY_LOOP, - expr.span, - "empty `loop {}` wastes CPU cycles", - None, - "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.", - ); + // also check for empty `loop {}` statements, skipping those in #[panic_handler] + if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) { + let msg = "empty `loop {}` wastes CPU cycles"; + let help = if is_no_std_crate(cx.tcx.hir().krate()) { + "You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body." + } else { + "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body." + }; + span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); } // extract the expression from the first statement (if any) in a block diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8e4149df032..cba3a050249 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -468,6 +468,13 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool { .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id()) } +/// Returns `true` if the expression is in the program's `#[panic_handler]`. +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let parent = cx.tcx.hir().get_parent_item(e.hir_id); + let def_id = cx.tcx.hir().local_def_id(parent).to_def_id(); + Some(def_id) == cx.tcx.lang_items().panic_impl() +} + /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs index 879d1d5d916..4553d3ec505 100644 --- a/tests/ui/empty_loop_no_std.rs +++ b/tests/ui/empty_loop_no_std.rs @@ -10,13 +10,18 @@ use core::panic::PanicInfo; #[start] fn main(argc: isize, argv: *const *const u8) -> isize { + // This should trigger the lint loop {} } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { + // This should NOT trigger the lint loop {} } #[lang = "eh_personality"] -extern "C" fn eh_personality() {} +extern "C" fn eh_personality() { + // This should also trigger the lint + loop {} +} diff --git a/tests/ui/empty_loop_no_std.stderr b/tests/ui/empty_loop_no_std.stderr new file mode 100644 index 00000000000..1cb454c3bbe --- /dev/null +++ b/tests/ui/empty_loop_no_std.stderr @@ -0,0 +1,19 @@ +error: empty `loop {}` wastes CPU cycles + --> $DIR/empty_loop_no_std.rs:14:5 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + +error: empty `loop {}` wastes CPU cycles + --> $DIR/empty_loop_no_std.rs:26:5 + | +LL | loop {} + | ^^^^^^^ + | + = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 3579b7de24df6a617322ec07eae98618ef57e5ee Mon Sep 17 00:00:00 2001 From: Joseph Richey Date: Wed, 4 Nov 2020 03:27:30 -0800 Subject: Update clippy_lints/src/loops.rs Fix NITs Co-authored-by: Philipp Krones --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2a53210efe3..b246245af24 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -547,9 +547,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx.tcx.hir().krate()) { - "You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body." + "you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body" } else { - "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body." + "you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body" }; span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); } -- cgit 1.4.1-3-g733a5 From f1f780c9422f038ae78e72a99b1ca2a0d7b392bc Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sat, 7 Nov 2020 18:26:14 -0500 Subject: Add let_underscore_drop --- CHANGELOG.md | 1 + clippy_lints/src/let_underscore.rs | 56 +++++++++++++++++++++++++++++++++++-- clippy_lints/src/lib.rs | 3 ++ src/lintlist/mod.rs | 7 +++++ tests/ui/let_underscore_drop.rs | 19 +++++++++++++ tests/ui/let_underscore_drop.stderr | 27 ++++++++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/ui/let_underscore_drop.rs create mode 100644 tests/ui/let_underscore_drop.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e0770a45c53..816d25bcd93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1787,6 +1787,7 @@ Released 2018-09-13 [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index ae2f6131b5b..9c7fd634547 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; +use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for `let _ = ` @@ -58,7 +58,40 @@ declare_clippy_lint! { "non-binding let on a synchronization lock" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]); +declare_clippy_lint! { + /// **What it does:** Checks for `let _ = ` + /// where expr has a type that implements `Drop` + /// + /// **Why is this bad?** This statement immediately drops the initializer + /// expression instead of extending its lifetime to the end of the scope, which + /// is often not intended. To extend the expression's lifetime to the end of the + /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to + /// explicitly drop the expression, `std::mem::drop` conveys your intention + /// better and is less error-prone. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust,ignore + /// struct Droppable; + /// impl Drop for Droppable { + /// fn drop(&mut self) {} + /// } + /// let _ = Droppable; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _droppable = Droppable; + /// ``` + pub LET_UNDERSCORE_DROP, + correctness, + "non-binding let on a type that implements `Drop`" +} + +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::MUTEX_GUARD, @@ -84,6 +117,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); + let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait| + init_ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + implements_trait(cx, inner_ty, drop_trait, &[]) + }, + + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + ); if contains_sync_guard { span_lint_and_help( cx, @@ -94,6 +136,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" ) + } else if implements_drop { + span_lint_and_help( + cx, + LET_UNDERSCORE_DROP, + local.span, + "non-binding let on a type that implements `Drop`", + None, + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`" + ) } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( cx, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1f4bed92e69..b44e28b4596 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -622,6 +622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, &let_if_seq::USELESS_LET_IF_SEQ, + &let_underscore::LET_UNDERSCORE_DROP, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, &lifetimes::EXTRA_UNUSED_LIFETIMES, @@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1809,6 +1811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bc0a0ad2b17..8b5e6cc916f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1117,6 +1117,13 @@ vec![ deprecation: None, module: "returns", }, + Lint { + name: "let_underscore_drop", + group: "correctness", + desc: "non-binding let on a type that implements `Drop`", + deprecation: None, + module: "let_underscore", + }, Lint { name: "let_underscore_lock", group: "correctness", diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs new file mode 100644 index 00000000000..98593edb9c5 --- /dev/null +++ b/tests/ui/let_underscore_drop.rs @@ -0,0 +1,19 @@ +#![warn(clippy::let_underscore_drop)] + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn main() { + let unit = (); + let boxed = Box::new(()); + let droppable = Droppable; + let optional = Some(Droppable); + + let _ = (); + let _ = Box::new(()); + let _ = Droppable; + let _ = Some(Droppable); +} diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr new file mode 100644 index 00000000000..6dc8904c4fe --- /dev/null +++ b/tests/ui/let_underscore_drop.stderr @@ -0,0 +1,27 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:16:5 + | +LL | let _ = Box::new(()); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:17:5 + | +LL | let _ = Droppable; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:18:5 + | +LL | let _ = Some(Droppable); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From b0994064b03f1f9f8bdb8f594bdaec9dbaa0788a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 29 Oct 2020 16:21:01 -0500 Subject: Make KNOW_TYPES static --- clippy_lints/src/methods/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a3fa3bdd36a..5309253531b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1803,6 +1803,14 @@ fn lint_or_fun_call<'tcx>( or_has_args: bool, span: Span, ) { + // (path, fn_has_argument, methods, suffix) + static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ + (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + ]; + if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { if path.ident.as_str() == "len" { let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -1818,16 +1826,8 @@ fn lint_or_fun_call<'tcx>( } } - // (path, fn_has_argument, methods, suffix) - let know_types: &[(&[_], _, &[_], _)] = &[ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), - ]; - if_chain! { - if know_types.iter().any(|k| k.2.contains(&name)); + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); if is_lazyness_candidate(cx, arg); if !contains_return(&arg); @@ -1835,7 +1835,7 @@ fn lint_or_fun_call<'tcx>( let self_ty = cx.typeck_results().expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); + KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); if poss.contains(&name); -- cgit 1.4.1-3-g733a5 From 9cab08465b5d5b4bb4f5ff406b2e18ca550f7d93 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 29 Oct 2020 15:30:20 -0500 Subject: Fix or_fun_call for index operator --- clippy_lints/src/methods/mod.rs | 45 ++++++++++++++------------------- clippy_lints/src/utils/eager_or_lazy.rs | 7 ++++- tests/ui/or_fun_call.fixed | 9 +++++++ tests/ui/or_fun_call.rs | 9 +++++++ tests/ui/or_fun_call.stderr | 18 ++++++++++--- 5 files changed, 58 insertions(+), 30 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5309253531b..b6fb3d06934 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1797,11 +1797,11 @@ fn lint_or_fun_call<'tcx>( cx: &LateContext<'tcx>, name: &str, method_span: Span, - fun_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - or_has_args: bool, span: Span, + // None if lambda is required + fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ @@ -1840,10 +1840,18 @@ fn lint_or_fun_call<'tcx>( if poss.contains(&name); then { - let sugg: Cow<'_, _> = match (fn_has_arguments, !or_has_args) { - (true, _) => format!("|_| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), - (false, false) => format!("|| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), - (false, true) => snippet_with_macro_callsite(cx, fun_span, ".."), + let sugg: Cow<'_, str> = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + let snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{}| {}", l_arg, snippet).into() + } else { + snippet + } }; let span_replace_word = method_span.with_hi(span.hi()); span_lint_and_sugg( @@ -1864,28 +1872,13 @@ fn lint_or_fun_call<'tcx>( hir::ExprKind::Call(ref fun, ref or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { - check_general_case( - cx, - name, - method_span, - fun.span, - &args[0], - &args[1], - or_has_args, - expr.span, - ); + let fun_span = if or_has_args { None } else { Some(fun.span) }; + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); } }, - hir::ExprKind::MethodCall(_, span, ref or_args, _) => check_general_case( - cx, - name, - method_span, - span, - &args[0], - &args[1], - !or_args.is_empty(), - expr.span, - ), + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + }, _ => {}, } } diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 4ceea13df37..8fe5ddee1ca 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::utils::is_ctor_or_promotable_const_function; +use crate::utils::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -96,6 +96,11 @@ fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, ex let call_found = match &expr.kind { // ignore enum and struct constructors ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + ExprKind::Index(obj, _) => { + let ty = self.cx.typeck_results().expr_ty(obj); + is_type_diagnostic_item(self.cx, ty, sym!(hashmap_type)) + || match_type(self.cx, ty, &paths::BTREEMAP) + }, ExprKind::MethodCall(..) => true, _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2045ffdb5f0..20e5016bc17 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -70,6 +70,15 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; let _ = opt.ok_or(format!("{} world.", hello)); + + // index + let map = HashMap::::new(); + let _ = Some(1).unwrap_or_else(|| map[&1]); + let map = BTreeMap::::new(); + let _ = Some(1).unwrap_or_else(|| map[&1]); + // don't lint index vec + let vec = vec![1]; + let _ = Some(1).unwrap_or(vec[1]); } struct Foo(u8); diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 522f31b72d0..e7192deeebb 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -70,6 +70,15 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; let _ = opt.ok_or(format!("{} world.", hello)); + + // index + let map = HashMap::::new(); + let _ = Some(1).unwrap_or(map[&1]); + let map = BTreeMap::::new(); + let _ = Some(1).unwrap_or(map[&1]); + // don't lint index vec + let vec = vec![1]; + let _ = Some(1).unwrap_or(vec[1]); } struct Foo(u8); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..d0c4df0e008 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -78,17 +78,29 @@ error: use of `unwrap_or` followed by a function call LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:76:21 + | +LL | let _ = Some(1).unwrap_or(map[&1]); + | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:78:21 + | +LL | let _ = Some(1).unwrap_or(map[&1]); + | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:102:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:106:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From aa6bf1f90d9191cdf4c6d925f8137fa8eac37889 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius" <35515885+smoelius@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:24:55 -0500 Subject: Update clippy_lints/src/let_underscore.rs Co-authored-by: Philipp Krones --- clippy_lints/src/let_underscore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 9c7fd634547..e8e64bca535 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -141,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { cx, LET_UNDERSCORE_DROP, local.span, - "non-binding let on a type that implements `Drop`", + "non-binding `let` on a type that implements `Drop`", None, "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" -- cgit 1.4.1-3-g733a5 From 9751cba2c675c2cd46742d2a81c891d4db4deacc Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius" <35515885+smoelius@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:25:23 -0500 Subject: Update clippy_lints/src/let_underscore.rs Co-authored-by: Philipp Krones --- clippy_lints/src/let_underscore.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index e8e64bca535..fb2df9369dc 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -79,12 +79,20 @@ declare_clippy_lint! { /// impl Drop for Droppable { /// fn drop(&mut self) {} /// } - /// let _ = Droppable; + /// { + /// let _ = Droppable; + /// // ^ dropped here + /// /* more code */ + /// } /// ``` /// /// Good: /// ```rust,ignore - /// let _droppable = Droppable; + /// { + /// let _droppable = Droppable; + /// /* more code */ + /// // dropped at end of scope + /// } /// ``` pub LET_UNDERSCORE_DROP, correctness, -- cgit 1.4.1-3-g733a5 From 8211b597baff7d599df8c1fe42f4e7d15b7d3e95 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius" <35515885+smoelius@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:25:50 -0500 Subject: Update clippy_lints/src/let_underscore.rs Co-authored-by: Philipp Krones --- clippy_lints/src/let_underscore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index fb2df9369dc..6a5a77f8690 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -95,7 +95,7 @@ declare_clippy_lint! { /// } /// ``` pub LET_UNDERSCORE_DROP, - correctness, + pedantic, "non-binding let on a type that implements `Drop`" } -- cgit 1.4.1-3-g733a5 From 40d7af50ed9811e427aa65b0111bca261ecf1239 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 Nov 2020 17:33:54 -0500 Subject: Update lints --- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b44e28b4596..20b38cbb6d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1241,6 +1241,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), @@ -1384,7 +1385,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1811,7 +1811,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(&let_underscore::LET_UNDERSCORE_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8b5e6cc916f..4f1b56ed9be 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1119,7 +1119,7 @@ vec![ }, Lint { name: "let_underscore_drop", - group: "correctness", + group: "pedantic", desc: "non-binding let on a type that implements `Drop`", deprecation: None, module: "let_underscore", -- cgit 1.4.1-3-g733a5 From effcb52bbf818ca39c57f9beb7d7d63b1e47d1fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 10 Nov 2020 22:33:02 +0100 Subject: Run cargo dev fmt --- clippy_lints/src/non_expressive_names.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 6b175490cc8..5b42b61fcde 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,7 +1,5 @@ use crate::utils::{span_lint, span_lint_and_then}; -use rustc_ast::ast::{ - Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, -}; +use rustc_ast::ast::{Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind}; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; -- cgit 1.4.1-3-g733a5 From 769094410a22849c426972dd421a1937463a48e7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 09:18:16 -0500 Subject: Fix map_clone with deref and clone --- clippy_lints/src/map_clone.rs | 14 +++++++++----- tests/ui/map_clone.fixed | 7 +++++++ tests/ui/map_clone.rs | 7 +++++++ 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 9a00608ce39..3f34dd5112e 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -8,6 +8,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; +use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; @@ -75,11 +76,14 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } } }, - hir::ExprKind::MethodCall(ref method, _, ref obj, _) => { - if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone" - && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { - - let obj_ty = cx.typeck_results().expr_ty(&obj[0]); + hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { + if ident_eq(name, obj) && method.ident.name == sym::clone; + if match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT); + // no autoderefs + if !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); + then { + let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { if matches!(mutability, Mutability::Not) { let copy = is_copy(cx, ty); diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 6e3a8e67e81..504ae281fe7 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -52,4 +52,11 @@ fn main() { let items = vec![&mut aa, &mut bb]; let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); } + + // Issue #6239 deref coercion and clone deref + { + use std::cell::RefCell; + + let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 6fd395710d4..9348e6bae7a 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -52,4 +52,11 @@ fn main() { let items = vec![&mut aa, &mut bb]; let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); } + + // Issue #6239 deref coercion and clone deref + { + use std::cell::RefCell; + + let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); + } } -- cgit 1.4.1-3-g733a5 From a1cf2d334d685fa11fdc96fc98f35292254e5651 Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Mon, 5 Oct 2020 21:23:36 -0700 Subject: Added a lint as suggested in 6010 which recommends using `contains()` instead of `find()` follows by `is_some()` on strings Update clippy_lints/src/find_is_some_on_strs.rs Co-authored-by: Takayuki Nakata Update clippy_lints/src/methods/mod.rs Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 38 +++++++++++++++++++++++++++++++++++--- src/lintlist/mod.rs | 2 +- tests/ui/methods.rs | 23 +++++++++++++++++++++-- tests/ui/methods.stderr | 2 -- 4 files changed, 57 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6fb3d06934..bd04a95e4a1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -515,11 +515,11 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for an iterator search (such as `find()`, + /// **What it does:** Checks for an iterator or string search (such as `find()`, /// `position()`, or `rposition()`) followed by a call to `is_some()`. /// /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.any(_)`. + /// `_.any(_)` or `_.contains(_)`. /// /// **Known problems:** None. /// @@ -535,7 +535,7 @@ declare_clippy_lint! { /// ``` pub SEARCH_IS_SOME, complexity, - "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`" + "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`" } declare_clippy_lint! { @@ -3041,6 +3041,7 @@ fn lint_flat_map_identity<'tcx>( } /// lint searching an Iterator followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` fn lint_search_is_some<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -3094,6 +3095,37 @@ fn lint_search_is_some<'tcx>( span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); } } + // lint if `find()` is called by `String` or `&str` + else if search_method == "find" { + let is_string_or_str_slice = |e| { + let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); + if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + true + } else { + *self_ty.kind() == ty::Str + } + }; + if_chain! { + if is_string_or_str_slice(&search_args[0]); + if is_string_or_str_slice(&search_args[1]); + then { + let msg = "called `is_some()` after calling `find()` \ + on a string. This is more succinctly expressed by calling \ + `contains()`.".to_string(); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "try this", + format!("contains({})", find_arg), + applicability, + ); + } + } + } } /// Used for `lint_binary_expr_with_method_call`. diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4f1b56ed9be..69acd3d9b8b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2121,7 +2121,7 @@ vec![ Lint { name: "search_is_some", group: "complexity", - desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`", + desc: "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`", deprecation: None, module: "methods", }, diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index d93e5b114ec..92ec00a11d2 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -168,8 +168,27 @@ fn search_is_some() { x < 0 } ).is_some(); - - // Check that we don't lint if the caller is not an `Iterator`. + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // Check caller `find()` is a &`static str case + let _ = "hello world".find("world").is_some(); + let _ = "hello world".find(&s2).is_some(); + let _ = "hello world".find(&s2[2..]).is_some(); + // Check caller of `find()` is a String case + let _ = s1.find("world").is_some(); + let _ = s1.find(&s2).is_some(); + let _ = s1.find(&s2[2..]).is_some(); + // Check caller of `find()` is a slice of String case + let _ = s1[2..].find("world").is_some(); + let _ = s1[2..].find(&s2).is_some(); + let _ = s1[2..].find(&s2[2..]).is_some(); + + // Check that we don't lint if `find()` is called with + // Pattern that is not a string + let _ = s1.find(|c: char| c == 'o' || c == 'l').is_some(); + + // Check that we don't lint if the caller is not an `Iterator` or string let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.find().is_some(); let _ = foo.position().is_some(); diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 8a281c2dbd2..b2b551bd5f8 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -88,5 +88,3 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 11 previous errors - -- cgit 1.4.1-3-g733a5 From e9612f3eca5566b601dda60c999be69e50f89cac Mon Sep 17 00:00:00 2001 From: rsulli55 Date: Sun, 25 Oct 2020 18:11:38 -0700 Subject: Remove `to_string` on msg Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bd04a95e4a1..4df1b3d197f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3111,7 +3111,7 @@ fn lint_search_is_some<'tcx>( then { let msg = "called `is_some()` after calling `find()` \ on a string. This is more succinctly expressed by calling \ - `contains()`.".to_string(); + `contains()`"; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( -- cgit 1.4.1-3-g733a5 From fb74b4802eda6da49f0ea9f2bc1ef238306c1329 Mon Sep 17 00:00:00 2001 From: rsulli55 Date: Sun, 25 Oct 2020 18:11:57 -0700 Subject: Remove borrow Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4df1b3d197f..fc66bf0422f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3118,7 +3118,7 @@ fn lint_search_is_some<'tcx>( cx, SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), - &msg, + msg, "try this", format!("contains({})", find_arg), applicability, -- cgit 1.4.1-3-g733a5 From ee1b959054aaf69968d440915766e834568de8fd Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 31 Oct 2020 12:40:56 -0700 Subject: Added period back to lint `search_is_some` and ran `update-all-references.sh` --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/methods.stderr | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc66bf0422f..19e63fbddf7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3111,7 +3111,7 @@ fn lint_search_is_some<'tcx>( then { let msg = "called `is_some()` after calling `find()` \ on a string. This is more succinctly expressed by calling \ - `contains()`"; + `contains()`."; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index bf4675966df..33aba630a53 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -20,3 +20,5 @@ LL | | ).next(); | = note: `-D clippy::filter-next` implied by `-D warnings` +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From fd303132a27f0fa9bbbfb3282200f8190353574b Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 7 Nov 2020 00:21:22 -0700 Subject: Cleaned up message and suggestion for `lint_search_is_some` --- clippy_lints/src/methods/mod.rs | 14 ++++---- tests/ui/search_is_some.stderr | 11 +++++-- tests/ui/search_is_some_fixable.stderr | 60 +++++++++++++++++----------------- 3 files changed, 44 insertions(+), 41 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 19e63fbddf7..66f5aa0c6a0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3053,10 +3053,10 @@ fn lint_search_is_some<'tcx>( // lint if caller of search is an Iterator if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { let msg = format!( - "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \ - expressed by calling `any()`.", + "called `is_some()` after searching an `Iterator` with {}", search_method ); + let hint = "this is more succinctly expressed by calling `any()`"; let search_snippet = snippet(cx, search_args[1].span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -3084,7 +3084,7 @@ fn lint_search_is_some<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "try this", + "use `any()` instead", format!( "any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -3092,7 +3092,7 @@ fn lint_search_is_some<'tcx>( Applicability::MachineApplicable, ); } else { - span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); } } // lint if `find()` is called by `String` or `&str` @@ -3109,9 +3109,7 @@ fn lint_search_is_some<'tcx>( if is_string_or_str_slice(&search_args[0]); if is_string_or_str_slice(&search_args[1]); then { - let msg = "called `is_some()` after calling `find()` \ - on a string. This is more succinctly expressed by calling \ - `contains()`."; + let msg = "called `is_some()` after calling `find()` on a string"; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( @@ -3119,7 +3117,7 @@ fn lint_search_is_some<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), msg, - "try this", + "use `contains()` instead", format!("contains({})", find_arg), applicability, ); diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index a124ab1dfd4..43827a6a98d 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some.rs:13:13 | LL | let _ = v.iter().find(|&x| { @@ -9,8 +9,9 @@ LL | | ).is_some(); | |______________________________^ | = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with position --> $DIR/search_is_some.rs:19:13 | LL | let _ = v.iter().position(|&x| { @@ -19,8 +20,10 @@ LL | | x < 0 LL | | } LL | | ).is_some(); | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with rposition --> $DIR/search_is_some.rs:25:13 | LL | let _ = v.iter().rposition(|&x| { @@ -29,6 +32,8 @@ LL | | x < 0 LL | | } LL | | ).is_some(); | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` error: use of a blacklisted/placeholder name `foo` --> $DIR/search_is_some.rs:31:9 diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index 7a2c063fee8..f4c5d7a3389 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -1,94 +1,94 @@ -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:10:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:11:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:12:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:13:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with position --> $DIR/search_is_some_fixable.rs:16:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with rposition --> $DIR/search_is_some_fixable.rs:19:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:24:27 | LL | let _ = "hello world".find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:25:27 | LL | let _ = "hello world".find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:26:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:28:16 | LL | let _ = s1.find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:29:16 | LL | let _ = s1.find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:30:16 | LL | let _ = s1.find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:32:21 | LL | let _ = s1[2..].find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:33:21 | LL | let _ = s1[2..].find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:34:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 56d252c53d1d4f7822b336d086659c26a8f0bb4d Mon Sep 17 00:00:00 2001 From: rsulli55 Date: Tue, 10 Nov 2020 23:17:46 -0700 Subject: Update clippy_lints/src/methods/mod.rs Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 66f5aa0c6a0..5c1c1594f7d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3053,7 +3053,7 @@ fn lint_search_is_some<'tcx>( // lint if caller of search is an Iterator if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { let msg = format!( - "called `is_some()` after searching an `Iterator` with {}", + "called `is_some()` after searching an `Iterator` with `{}`", search_method ); let hint = "this is more succinctly expressed by calling `any()`"; -- cgit 1.4.1-3-g733a5 From 5f64867e1d5344da8ff7526308d4b63676242c0d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 11 Nov 2020 22:36:53 +0900 Subject: Fix suggestion in `manual_range_contains` when using float --- clippy_lints/src/ranges.rs | 6 ++++-- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ tests/ui/range_contains.stderr | 14 +++++++++++++- 4 files changed, 27 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 79e9a56af9a..4b514bbd42c 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -222,13 +222,14 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + let space = if lo.ends_with('.') { " " } else { "" }; span_lint_and_sugg( cx, MANUAL_RANGE_CONTAINS, span, &format!("manual `{}::contains` implementation", range_type), "use", - format!("({}{}{}).contains(&{})", lo, range_op, hi, name), + format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); } else if !combine_and && ord == Some(lord) { @@ -251,13 +252,14 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + let space = if lo.ends_with('.') { " " } else { "" }; span_lint_and_sugg( cx, MANUAL_RANGE_CONTAINS, span, &format!("manual `!{}::contains` implementation", range_type), "use", - format!("!({}{}{}).contains(&{})", lo, range_op, hi, name), + format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); } diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 632a6592a28..048874a7f82 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -38,4 +38,9 @@ fn main() { x >= 8 || x >= 12; x < 12 || 12 < x; x >= 8 || x <= 12; + + // Fix #6315 + let y = 3.; + (0. ..1.).contains(&y); + !(0. ..=1.).contains(&y); } diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 6af0d034ef6..60ad259f404 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -38,4 +38,9 @@ fn main() { x >= 8 || x >= 12; x < 12 || 12 < x; x >= 8 || x <= 12; + + // Fix #6315 + let y = 3.; + y >= 0. && y < 1.; + y < 0. || y > 1.; } diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 69b009eafc3..bc79f1bca84 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -72,5 +72,17 @@ error: manual `!RangeInclusive::contains` implementation LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` -error: aborting due to 12 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:44:5 + | +LL | y >= 0. && y < 1.; + | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:45:5 + | +LL | y < 0. || y > 1.; + | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 8f89108533690afe964799d8f514956ec8b72377 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 9 Nov 2020 22:14:11 +0900 Subject: Fix FP in indirect `needless_collect` when used multiple times --- clippy_lints/src/loops.rs | 34 +++++++++++++++++++++++++++++++++- tests/ui/needless_collect_indirect.rs | 20 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0d31e9cfc3d..143cbea5537 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2950,7 +2950,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo for ref stmt in block.stmts { if_chain! { if let StmtKind::Local( - Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } ) = stmt.kind; if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; @@ -2964,6 +2964,16 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if iter_calls.len() == 1; then { + let mut used_count_visitor = UsedCountVisitor { + cx, + id: *pat_id, + count: 0, + }; + walk_block(&mut used_count_visitor, block); + if used_count_visitor.count > 1 { + return; + } + // Suggest replacing iter_call with iter_replacement, and removing stmt let iter_call = &iter_calls[0]; span_lint_and_then( @@ -3087,6 +3097,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { } } +struct UsedCountVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + id: HirId, + count: usize, +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if same_var(self.cx, expr, self.id) { + self.count += 1; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + /// Detect the occurrences of calls to `iter` or `into_iter` for the /// given identifier fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 4f6e5357727..0918a6868ab 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -22,4 +22,24 @@ fn main() { let sample = vec![a.clone(), "b".to_string(), "c".to_string()]; let non_copy_contains = sample.into_iter().collect::>(); non_copy_contains.contains(&a); + + // Fix #5991 + let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let vec_b = vec_a.iter().collect::>(); + if vec_b.len() > 3 {} + let other_vec = vec![1, 3, 12, 4, 16, 2]; + let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::>(); + + // Fix #6297 + let sample = [1; 5]; + let multiple_indirect = sample.iter().collect::>(); + let sample2 = vec![2, 3]; + if multiple_indirect.is_empty() { + // do something + } else { + let found = sample2 + .iter() + .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0)) + .collect::>(); + } } -- cgit 1.4.1-3-g733a5 From c6b74df6807792aae1b18031017a3d154c770173 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 13 Nov 2020 14:47:23 +0900 Subject: Fix dogfood test --- clippy_lints/src/utils/ast_utils.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 9050b9b2d9a..fcf7a4b1367 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -110,8 +110,7 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { match (l, r) { (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), - (StructRest::Rest(_), StructRest::Rest(_)) => true, - (StructRest::None, StructRest::None) => true, + (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, _ => false, } } -- cgit 1.4.1-3-g733a5 From 5b8f2b6c93ea819cd6d6e02ad7e2b8b9da23fd67 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 16 Nov 2020 18:32:01 +0100 Subject: Remove `expect()` calls to avoid ICEs in `deref_addrof` lint --- clippy_lints/src/reference.rs | 48 +++++++++++++++++++++++++------------------ tests/ui/crashes/ice-6332.rs | 11 ++++++++++ 2 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 tests/ui/crashes/ice-6332.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 35a1310d68b..efe3237990d 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -60,30 +60,38 @@ impl EarlyLintPass for DerefAddrOf { }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) }; - let rpos = if *mutability == Mutability::Mut { - macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() - } else { - macro_source.rfind('&').expect("already checked this is a reference") + "&".len() + let mut generate_snippet = |pattern: &str| { + #[allow(clippy::cast_possible_truncation)] + macro_source.rfind(pattern).map(|pattern_pos| { + let rpos = pattern_pos + pattern.len(); + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability) + }) }; - #[allow(clippy::cast_possible_truncation)] - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) + + if *mutability == Mutability::Mut { + generate_snippet("mut") + } else { + generate_snippet("&") + } } else { - snippet_with_applicability(cx, e.span, "_", &mut applicability) + Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) } } else { - snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability) - }.to_string(); - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try this", - sugg, - applicability, - ); + Some(snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)) + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try this", + sugg.to_string(), + applicability, + ); + } } } } diff --git a/tests/ui/crashes/ice-6332.rs b/tests/ui/crashes/ice-6332.rs new file mode 100644 index 00000000000..9dc92aa500b --- /dev/null +++ b/tests/ui/crashes/ice-6332.rs @@ -0,0 +1,11 @@ +fn cmark_check() { + let mut link_err = false; + macro_rules! cmark_error { + ($bad:expr) => { + *$bad = true; + }; + } + cmark_error!(&mut link_err); +} + +pub fn main() {} -- cgit 1.4.1-3-g733a5 From a7ac441760ae034ff7401439b38da821f4e2df3a Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 20 Sep 2020 02:03:14 +0900 Subject: Add new lint to detect unnecessarily wrapped value --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unnecessary_wrap.rs | 177 +++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/unnecessary_wrap.fixed | 47 ++++++++++ tests/ui/unnecessary_wrap.rs | 47 ++++++++++ tests/ui/unnecessary_wrap.stderr | 34 +++++++ 7 files changed, 318 insertions(+) create mode 100644 clippy_lints/src/unnecessary_wrap.rs create mode 100644 tests/ui/unnecessary_wrap.fixed create mode 100644 tests/ui/unnecessary_wrap.rs create mode 100644 tests/ui/unnecessary_wrap.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 816d25bcd93..02b862d3196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2008,6 +2008,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 20b38cbb6d0..2d1f75391bb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,6 +323,7 @@ mod unicode; mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; +mod unnecessary_wrap; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -892,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnecessary_wrap::UNNECESSARY_WRAP, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); + store.register_late_pass(|| box unnecessary_wrap::UnnecessaryWrap); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1571,6 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1775,6 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs new file mode 100644 index 00000000000..26a57517258 --- /dev/null +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -0,0 +1,177 @@ +use crate::utils::{ + is_type_diagnostic_item, match_qpath, multispan_sugg_with_applicability, paths, return_ty, snippet, + span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for private functions that only return `Ok` or `Some`. + /// + /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. + /// + /// **Known problems:** Since this lint changes function type signature, you may need to + /// adjust some codes at callee side. + /// + /// **Example:** + /// + /// ```rust + /// pub fn get_cool_number(a: bool, b: bool) -> Option { + /// if a && b { + /// return Some(50); + /// } + /// if a { + /// Some(0) + /// } else { + /// Some(10) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn get_cool_number(a: bool, b: bool) -> i32 { + /// if a && b { + /// return 50; + /// } + /// if a { + /// 0 + /// } else { + /// 10 + /// } + /// } + /// ``` + pub UNNECESSARY_WRAP, + complexity, + "functions that only return `Ok` or `Some`" +} + +declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if_chain! { + if let FnKind::ItemFn(.., visibility, _) = fn_kind; + if visibility.node.is_pub(); + then { + return; + } + } + + if let ExprKind::Block(ref block, ..) = body.value.kind { + let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + &paths::OPTION_SOME + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + &paths::RESULT_OK + } else { + return; + }; + + let mut visitor = UnnecessaryWrapVisitor { result: Vec::new() }; + visitor.visit_block(block); + let result = visitor.result; + + if result.iter().any(|expr| { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + false + } else { + true + } + } + }) { + return; + } + + let suggs = result.iter().filter_map(|expr| { + let snippet = if let ExprKind::Call(_, ref args) = expr.kind { + Some(snippet(cx, args[0].span, "..").to_string()) + } else { + None + }; + snippet.map(|snip| (expr.span, snip)) + }); + + span_lint_and_then( + cx, + UNNECESSARY_WRAP, + span, + "this function returns unnecessarily wrapping data", + move |diag| { + multispan_sugg_with_applicability( + diag, + "factor this out to", + Applicability::MachineApplicable, + suggs, + ); + }, + ); + } + } +} + +struct UnnecessaryWrapVisitor<'tcx> { + result: Vec<&'tcx Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for UnnecessaryWrapVisitor<'tcx> { + type Map = Map<'tcx>; + + fn visit_block(&mut self, block: &'tcx Block<'tcx>) { + for stmt in block.stmts { + self.visit_stmt(stmt); + } + if let Some(expr) = block.expr { + self.visit_expr(expr) + } + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) { + match stmt.kind { + StmtKind::Semi(ref expr) => { + if let ExprKind::Ret(Some(value)) = expr.kind { + self.result.push(value); + } + }, + StmtKind::Expr(ref expr) => self.visit_expr(expr), + _ => (), + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + ExprKind::Ret(Some(value)) => self.result.push(value), + ExprKind::Call(..) | ExprKind::Path(..) => self.result.push(expr), + ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => { + self.visit_block(block); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.visit_expr(arm.body); + } + }, + _ => intravisit::walk_expr(self, expr), + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 69acd3d9b8b..4a0cdc5d82f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2608,6 +2608,13 @@ vec![ deprecation: None, module: "unwrap", }, + Lint { + name: "unnecessary_wrap", + group: "complexity", + desc: "functions that only return `Ok` or `Some`", + deprecation: None, + module: "unnecessary_wrap", + }, Lint { name: "unneeded_field_pattern", group: "restriction", diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed new file mode 100644 index 00000000000..1657a3173db --- /dev/null +++ b/tests/ui/unnecessary_wrap.fixed @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::unnecessary_wrap)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// public fns should not be linted +pub fn func2(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func4() -> Option { + 1 +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true); +} diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs new file mode 100644 index 00000000000..edf41dad790 --- /dev/null +++ b/tests/ui/unnecessary_wrap.rs @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::unnecessary_wrap)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// public fns should not be linted +pub fn func2(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func4() -> Option { + Some(1) +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true); +} diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr new file mode 100644 index 00000000000..8473bd81839 --- /dev/null +++ b/tests/ui/unnecessary_wrap.stderr @@ -0,0 +1,34 @@ +error: this function unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:8:1 + | +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` +help: factor this out to + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:39:1 + | +LL | / fn func4() -> Option { +LL | | Some(1) + | | ------- help: factor this out to: `1` +LL | | } + | |_^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 750c118b347af938383c5bff53040480e0974071 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 20 Sep 2020 18:22:01 +0900 Subject: Add suggestion on type signatures --- clippy_lints/src/unnecessary_wrap.rs | 31 ++++++++++++++++++++++--------- tests/ui/unnecessary_wrap.fixed | 18 +++++++++++++++++- tests/ui/unnecessary_wrap.rs | 16 ++++++++++++++++ tests/ui/unnecessary_wrap.stderr | 35 +++++++++++++++++++++++++++-------- 4 files changed, 82 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 26a57517258..7b586c1df0c 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; +use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -100,14 +100,27 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { return; } - let suggs = result.iter().filter_map(|expr| { - let snippet = if let ExprKind::Call(_, ref args) = expr.kind { - Some(snippet(cx, args[0].span, "..").to_string()) - } else { - None - }; - snippet.map(|snip| (expr.span, snip)) - }); + let suggs = result + .iter() + .filter_map(|expr| { + let snippet = if let ExprKind::Call(_, ref args) = expr.kind { + Some(snippet(cx, args[0].span, "..").to_string()) + } else { + None + }; + snippet.map(|snip| (expr.span, snip)) + }) + .chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // first outermost inner type is needed + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }); span_lint_and_then( cx, diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed index 1657a3173db..749bb95c417 100644 --- a/tests/ui/unnecessary_wrap.fixed +++ b/tests/ui/unnecessary_wrap.fixed @@ -1,8 +1,10 @@ // run-rustfix + #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] +#![allow(dead_code)] // should be linted fn func1(a: bool, b: bool) -> Option { @@ -37,7 +39,21 @@ fn func3(a: bool) -> Option { // should be linted fn func4() -> Option { - 1 + Some(1) +} + +// should be linted +fn func5() -> Result { + Ok(1) +} + +// should not be linted +fn func6(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } } fn main() { diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index edf41dad790..749bb95c417 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -1,8 +1,10 @@ // run-rustfix + #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] +#![allow(dead_code)] // should be linted fn func1(a: bool, b: bool) -> Option { @@ -40,6 +42,20 @@ fn func4() -> Option { Some(1) } +// should be linted +fn func5() -> Result { + Ok(1) +} + +// should not be linted +fn func6(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 8473bd81839..511d085c82f 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,5 +1,5 @@ -error: this function unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:8:1 +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:10:1 | LL | / fn func1(a: bool, b: bool) -> Option { LL | | if a && b { @@ -13,22 +13,41 @@ LL | | } = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` help: factor this out to | +LL | fn func1(a: bool, b: bool) -> i32 { +LL | if a && b { LL | return 42; LL | } LL | if a { LL | Some(-1); -LL | 2 -LL | } else { ... -error: this function unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:39:1 +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:41:1 | LL | / fn func4() -> Option { LL | | Some(1) - | | ------- help: factor this out to: `1` LL | | } | |_^ + | +help: factor this out to + | +LL | fn func4() -> i32 { +LL | 1 + | + +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:46:1 + | +LL | / fn func5() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: factor this out to + | +LL | fn func5() -> i32 { +LL | 1 + | -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 0335b8d6a747672c0a4ff6cb14b12eced3848325 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 21 Sep 2020 00:06:25 +0900 Subject: Fix lint example --- clippy_lints/src/unnecessary_wrap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 7b586c1df0c..c5d0d510079 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// pub fn get_cool_number(a: bool, b: bool) -> Option { + /// fn get_cool_number(a: bool, b: bool) -> Option { /// if a && b { /// return Some(50); /// } @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// pub fn get_cool_number(a: bool, b: bool) -> i32 { + /// fn get_cool_number(a: bool, b: bool) -> i32 { /// if a && b { /// return 50; /// } -- cgit 1.4.1-3-g733a5 From ebdd4e2c723c6902851a050ec8bdc7b966dc2c64 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 02:53:55 +0900 Subject: Refactor code according to reivews --- clippy_lints/src/unnecessary_wrap.rs | 224 ++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 83 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index c5d0d510079..53ade7baec7 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -4,7 +4,7 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; @@ -71,57 +71,31 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } - if let ExprKind::Block(ref block, ..) = body.value.kind { - let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - &paths::OPTION_SOME - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - &paths::RESULT_OK - } else { - return; - }; - - let mut visitor = UnnecessaryWrapVisitor { result: Vec::new() }; - visitor.visit_block(block); - let result = visitor.result; - - if result.iter().any(|expr| { - if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, path); - if args.len() == 1; - then { - false - } else { - true - } + let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + &paths::OPTION_SOME + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + &paths::RESULT_OK + } else { + return; + }; + + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { + if_chain! { + if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); + true + } else { + false } - }) { - return; } + }); - let suggs = result - .iter() - .filter_map(|expr| { - let snippet = if let ExprKind::Call(_, ref args) = expr.kind { - Some(snippet(cx, args[0].span, "..").to_string()) - } else { - None - }; - snippet.map(|snip| (expr.span, snip)) - }) - .chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // first outermost inner type is needed - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }); - + if can_sugg { span_lint_and_then( cx, UNNECESSARY_WRAP, @@ -132,7 +106,17 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { diag, "factor this out to", Applicability::MachineApplicable, - suggs, + suggs.into_iter().chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }), ); }, ); @@ -140,51 +124,125 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } -struct UnnecessaryWrapVisitor<'tcx> { - result: Vec<&'tcx Expr<'tcx>>, -} +// code below is copied from `bind_instead_of_map` + +fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } -impl<'tcx> Visitor<'tcx> for UnnecessaryWrapVisitor<'tcx> { - type Map = Map<'tcx>; + 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, + } + } + } - fn visit_block(&mut self, block: &'tcx Block<'tcx>) { - for stmt in block.stmts { - self.visit_stmt(stmt); + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val } - if let Some(expr) = block.expr { - self.visit_expr(expr) + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val } } - fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) { - match stmt.kind { - StmtKind::Semi(ref expr) => { - if let ExprKind::Ret(Some(value)) = expr.kind { - self.result.push(value); - } - }, - StmtKind::Expr(ref expr) => self.visit_expr(expr), - _ => (), + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; } } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - ExprKind::Ret(Some(value)) => self.result.push(value), - ExprKind::Call(..) | ExprKind::Path(..) => self.result.push(expr), - ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => { - self.visit_block(block); - }, - ExprKind::Match(_, arms, _) => { - for arm in arms { - self.visit_expr(arm.body); + impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + ExprKind::Block(..) => intravisit::walk_expr(self, expr), + ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), } - }, - _ => intravisit::walk_expr(self, expr), + } } } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found } -- cgit 1.4.1-3-g733a5 From 3ed89026231a17beed9e93b055ea1b6192997a68 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 03:06:52 +0900 Subject: Fix typo Co-authored-by: Philipp Krones --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 53ade7baec7..e5ef7cde789 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. /// /// **Known problems:** Since this lint changes function type signature, you may need to - /// adjust some codes at callee side. + /// adjust some code at callee side. /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From c77585672720285db5180b2ee4207c7ee9b51072 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 03:42:58 +0900 Subject: Call `diag.multipart_suggestion` instead --- clippy_lints/src/unnecessary_wrap.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index e5ef7cde789..d581912284f 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_qpath, multispan_sugg_with_applicability, paths, return_ty, snippet, + is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, }; use if_chain::if_chain; @@ -102,10 +102,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span, "this function returns unnecessarily wrapping data", move |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "factor this out to", - Applicability::MachineApplicable, suggs.into_iter().chain({ let inner_ty = return_ty(cx, hir_id) .walk() @@ -116,7 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { _ => None, }); inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }), + }).collect(), + Applicability::MachineApplicable, ); }, ); -- cgit 1.4.1-3-g733a5 From a433d4690e04c8c8eceb1f59c78fded1f04cf6f0 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 03:48:15 +0900 Subject: Run rustfmt --- clippy_lints/src/unnecessary_wrap.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index d581912284f..1c8a08172a3 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,7 +1,4 @@ -use crate::utils::{ - is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, - span_lint_and_then, -}; +use crate::utils::{is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor}; @@ -104,17 +101,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { move |diag| { diag.multipart_suggestion( "factor this out to", - suggs.into_iter().chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }).collect(), + suggs + .into_iter() + .chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }) + .collect(), Applicability::MachineApplicable, ); }, -- cgit 1.4.1-3-g733a5 From cdb72df6f9f9d6946b0614b01e70bc9c46edfe89 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 26 Sep 2020 15:39:39 +0900 Subject: Split lint suggestion into two --- clippy_lints/src/unnecessary_wrap.rs | 41 +++++++++++++++++++----------------- tests/ui/unnecessary_wrap.stderr | 18 ++++++++++++---- 2 files changed, 36 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 1c8a08172a3..3ddf921a04b 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -68,10 +68,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } - let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - &paths::OPTION_SOME + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + ("Option", &paths::OPTION_SOME) } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - &paths::RESULT_OK + ("Result", &paths::RESULT_OK) } else { return; }; @@ -98,23 +98,26 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { UNNECESSARY_WRAP, span, "this function returns unnecessarily wrapping data", - move |diag| { + |diag| { + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.for_each(|inner_ty| { + diag.span_suggestion( + fn_decl.output.span(), + format!("remove `{}` from the return type...", return_type).as_str(), + inner_ty, + Applicability::MachineApplicable, + ); + }); diag.multipart_suggestion( - "factor this out to", - suggs - .into_iter() - .chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }) - .collect(), + "...and change the returning expressions", + suggs, Applicability::MachineApplicable, ); }, diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index cd05104f490..7833ee4b213 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -11,14 +11,18 @@ LL | | } | |_^ | = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` -help: factor this out to +help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { -LL | if a && b { + | ^^^ +help: ...and change the returning expressions + | LL | return 42; LL | } LL | if a { LL | Some(-1); +LL | 2 +LL | } else { ... error: this function returns unnecessarily wrapping data @@ -29,9 +33,12 @@ LL | | Some(1) LL | | } | |_^ | -help: factor this out to +help: remove `Option` from the return type... | LL | fn func4() -> i32 { + | ^^^ +help: ...and change the returning expressions + | LL | 1 | @@ -43,9 +50,12 @@ LL | | Ok(1) LL | | } | |_^ | -help: factor this out to +help: remove `Result` from the return type... | LL | fn func6() -> i32 { + | ^^^ +help: ...and change the returning expressions + | LL | 1 | -- cgit 1.4.1-3-g733a5 From df0d565e597ad9c436fadfb8e11a16a92bcb4114 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 2 Oct 2020 18:21:17 +0900 Subject: Move `find_all_ret_expressions` into `utils` --- clippy_lints/src/methods/bind_instead_of_map.rs | 125 +--------------------- clippy_lints/src/unnecessary_wrap.rs | 132 ++---------------------- clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/visitors.rs | 125 ++++++++++++++++++++++ 4 files changed, 133 insertions(+), 250 deletions(-) create mode 100644 clippy_lints/src/utils/visitors.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index ae37942e55a..540a1484a85 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,14 +1,12 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use crate::utils::{ in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, + snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; use rustc_span::Span; pub(crate) struct OptionAndThenSome; @@ -193,124 +191,3 @@ pub(crate) trait BindInsteadOfMap { } } } - -/// returns `true` if expr contains match expr desugared from try -fn contains_try(expr: &hir::Expr<'_>) -> bool { - struct TryFinder { - found: bool, - } - - impl<'hir> intravisit::Visitor<'hir> for TryFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - if self.found { - return; - } - match expr.kind { - hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, - _ => intravisit::walk_expr(self, expr), - } - } - } - - let mut visitor = TryFinder { found: false }; - visitor.visit_expr(expr); - visitor.found -} - -fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool -where - F: FnMut(&'hir hir::Expr<'hir>) -> bool, -{ - struct RetFinder { - in_stmt: bool, - failed: bool, - cb: F, - } - - struct WithStmtGuarg<'a, F> { - val: &'a mut RetFinder, - prev_in_stmt: bool, - } - - impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { - let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { - val: self, - prev_in_stmt, - } - } - } - - impl std::ops::Deref for WithStmtGuarg<'_, F> { - type Target = RetFinder; - - fn deref(&self) -> &Self::Target { - self.val - } - } - - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.val - } - } - - impl Drop for WithStmtGuarg<'_, F> { - fn drop(&mut self) { - self.val.in_stmt = self.prev_in_stmt; - } - } - - impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { - intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { - if self.failed { - return; - } - if self.in_stmt { - match expr.kind { - hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), - _ => intravisit::walk_expr(self, expr), - } - } else { - match expr.kind { - hir::ExprKind::Match(cond, arms, _) => { - self.inside_stmt(true).visit_expr(cond); - for arm in arms { - self.visit_expr(arm.body); - } - }, - hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), - hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), - _ => self.failed |= !(self.cb)(expr), - } - } - } - } - - !contains_try(expr) && { - let mut ret_finder = RetFinder { - in_stmt: false, - failed: false, - cb: callback, - }; - ret_finder.visit_expr(expr); - !ret_finder.failed - } -} diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 3ddf921a04b..de13301381e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,10 +1,13 @@ -use crate::utils::{is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then}; +use crate::utils::{ + is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -125,126 +128,3 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } } - -// code below is copied from `bind_instead_of_map` - -fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool -where - F: FnMut(&'hir Expr<'hir>) -> bool, -{ - struct RetFinder { - in_stmt: bool, - failed: bool, - cb: F, - } - - struct WithStmtGuarg<'a, F> { - val: &'a mut RetFinder, - prev_in_stmt: bool, - } - - impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { - let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { - val: self, - prev_in_stmt, - } - } - } - - impl std::ops::Deref for WithStmtGuarg<'_, F> { - type Target = RetFinder; - - fn deref(&self) -> &Self::Target { - self.val - } - } - - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.val - } - } - - impl Drop for WithStmtGuarg<'_, F> { - fn drop(&mut self) { - self.val.in_stmt = self.prev_in_stmt; - } - } - - impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) { - intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) - } - - fn visit_expr(&mut self, expr: &'hir Expr<'_>) { - if self.failed { - return; - } - if self.in_stmt { - match expr.kind { - ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), - _ => intravisit::walk_expr(self, expr), - } - } else { - match expr.kind { - ExprKind::Match(cond, arms, _) => { - self.inside_stmt(true).visit_expr(cond); - for arm in arms { - self.visit_expr(arm.body); - } - }, - ExprKind::Block(..) => intravisit::walk_expr(self, expr), - ExprKind::Ret(Some(expr)) => self.visit_expr(expr), - _ => self.failed |= !(self.cb)(expr), - } - } - } - } - - !contains_try(expr) && { - let mut ret_finder = RetFinder { - in_stmt: false, - failed: false, - cb: callback, - }; - ret_finder.visit_expr(expr); - !ret_finder.failed - } -} - -/// returns `true` if expr contains match expr desugared from try -fn contains_try(expr: &Expr<'_>) -> bool { - struct TryFinder { - found: bool, - } - - impl<'hir> intravisit::Visitor<'hir> for TryFinder { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { - if self.found { - return; - } - match expr.kind { - ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true, - _ => intravisit::walk_expr(self, expr), - } - } - } - - let mut visitor = TryFinder { found: false }; - visitor.visit_expr(expr); - visitor.found -} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 253b7823bd9..bd62e67f80e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -21,6 +21,7 @@ pub mod ptr; pub mod qualify_min_const_fn; pub mod sugg; pub mod usage; +pub mod visitors; pub use self::attrs::*; pub use self::diagnostics::*; diff --git a/clippy_lints/src/utils/visitors.rs b/clippy_lints/src/utils/visitors.rs new file mode 100644 index 00000000000..b0837b6c43e --- /dev/null +++ b/clippy_lints/src/utils/visitors.rs @@ -0,0 +1,125 @@ +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &hir::Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } + } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir hir::Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + hir::ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), + hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} -- cgit 1.4.1-3-g733a5 From eec7f5c1113d3d49dd7f42462ea25d0b36b4b49b Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:21:04 +0900 Subject: Update clippy_lints/src/unnecessary_wrap.rs Co-authored-by: Philipp Krones --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index de13301381e..28762b8d265 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - "this function returns unnecessarily wrapping data", + format!("this function's return value is unnecessarily wrapped by `{}`, return_type)", |diag| { let inner_ty = return_ty(cx, hir_id) .walk() -- cgit 1.4.1-3-g733a5 From 1bdac8712868e1418dcb13e817254620a8c01158 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:27:47 +0900 Subject: Improve lint message --- clippy_lints/src/unnecessary_wrap.rs | 2 +- tests/ui/unnecessary_wrap.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 28762b8d265..db51ee681ef 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - format!("this function's return value is unnecessarily wrapped by `{}`, return_type)", + format!("this function's return value is unnecessarily wrapped by `{}`", return_type).as_str(), |diag| { let inner_ty = return_ty(cx, hir_id) .walk() diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index a3481330e99..958bc998022 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,4 +1,4 @@ -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:8:1 | LL | / fn func1(a: bool, b: bool) -> Option { @@ -25,7 +25,7 @@ LL | 2 LL | } else { ... -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:21:1 | LL | / fn func2(a: bool, b: bool) -> Option { @@ -51,7 +51,7 @@ LL | } else { LL | 30 | -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:51:1 | LL | / fn func5() -> Option { @@ -68,7 +68,7 @@ help: ...and change the returning expressions LL | 1 | -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Result` --> $DIR/unnecessary_wrap.rs:61:1 | LL | / fn func7() -> Result { -- cgit 1.4.1-3-g733a5 From 8392bc7946a212a85bc5a411f5321a1f76d5ccf6 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:37:27 +0900 Subject: Run `cargo dev fmt` --- clippy_lints/src/unnecessary_wrap.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index db51ee681ef..fc1a33fc6cd 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - format!("this function's return value is unnecessarily wrapped by `{}`", return_type).as_str(), + format!( + "this function's return value is unnecessarily wrapped by `{}`", + return_type + ) + .as_str(), |diag| { let inner_ty = return_ty(cx, hir_id) .walk() -- cgit 1.4.1-3-g733a5 From 2f85aa736e85544803ff6d3e6b13e44c0729bbdd Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 16:30:46 +0900 Subject: Make lint skip closures --- clippy_lints/src/unnecessary_wrap.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index fc1a33fc6cd..a20d1f82e9e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -63,12 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { - if_chain! { - if let FnKind::ItemFn(.., visibility, _) = fn_kind; - if visibility.node.is_pub(); - then { - return; - } + match fn_kind { + FnKind::ItemFn(.., visibility, _) if visibility.node.is_pub() => return, + FnKind::Closure(..) => return, + _ => (), } let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { -- cgit 1.4.1-3-g733a5 From 12474c62ff89ab122c2e6881b00680913fdf1d35 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 16:53:18 +0900 Subject: Add support for methods --- clippy_lints/src/unnecessary_wrap.rs | 6 +++++- tests/ui/unnecessary_wrap.rs | 14 ++++++++++++++ tests/ui/unnecessary_wrap.stderr | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index a20d1f82e9e..365e9c9984e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -64,7 +64,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { hir_id: HirId, ) { match fn_kind { - FnKind::ItemFn(.., visibility, _) if visibility.node.is_pub() => return, + FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { + if visibility.node.is_pub() { + return; + } + }, FnKind::Closure(..) => return, _ => (), } diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 6037f5807d3..0ced20b7a56 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -76,6 +76,20 @@ fn func9(a: bool) -> Result { Err(()) } +struct A; + +impl A { + // should not be linted + pub fn func10() -> Option { + Some(1) + } + + // should be linted + fn func11() -> Option { + Some(1) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 958bc998022..dbc5748c29e 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -85,5 +85,22 @@ help: ...and change the returning expressions LL | 1 | -error: aborting due to 4 previous errors +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wrap.rs:88:5 + | +LL | / fn func11() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func11() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From c5447eb3c167025fcc1b842fabd7bb43a2eb1e9e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:17:45 +0900 Subject: Make lint skip macros --- clippy_lints/src/unnecessary_wrap.rs | 3 ++- tests/ui/unnecessary_wrap.rs | 9 +++++++-- tests/ui/unnecessary_wrap.stderr | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 365e9c9984e..7ec523293c8 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; @@ -84,6 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { + if !in_macro(ret_expr.span); if let ExprKind::Call(ref func, ref args) = ret_expr.kind; if let ExprKind::Path(ref qpath) = func.kind; if match_qpath(qpath, path); diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 0ced20b7a56..618c452065b 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -76,16 +76,21 @@ fn func9(a: bool) -> Result { Err(()) } +// should not be linted +fn func10() -> Option<()> { + unimplemented!() +} + struct A; impl A { // should not be linted - pub fn func10() -> Option { + pub fn func11() -> Option { Some(1) } // should be linted - fn func11() -> Option { + fn func12() -> Option { Some(1) } } diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index dbc5748c29e..5f21b74bc76 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -86,16 +86,16 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:88:5 + --> $DIR/unnecessary_wrap.rs:93:5 | -LL | / fn func11() -> Option { +LL | / fn func12() -> Option { LL | | Some(1) LL | | } | |_____^ | help: remove `Option` from the return type... | -LL | fn func11() -> i32 { +LL | fn func12() -> i32 { | ^^^ help: ...and change the returning expressions | -- cgit 1.4.1-3-g733a5 From c7692cf7493facefd520a98bf86aa1f4a031f435 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:27:08 +0900 Subject: Skip function with no exprs contained --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 7ec523293c8..28cc20f0007 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } }); - if can_sugg { + if can_sugg && !suggs.is_empty() { span_lint_and_then( cx, UNNECESSARY_WRAP, -- cgit 1.4.1-3-g733a5 From e998d61fe6a1407f73504a2fee16df2f9167a37c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 18:36:37 +0900 Subject: Downgrade applicability to MaybeIncorrect --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 28cc20f0007..6434b3033dd 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { fn_decl.output.span(), format!("remove `{}` from the return type...", return_type).as_str(), inner_ty, - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); }); diag.multipart_suggestion( -- cgit 1.4.1-3-g733a5 From 9d96311d7354d99b6f3b3956405b7defac81461c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 18:46:16 +0900 Subject: Remove wildcard use --- clippy_lints/src/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 6434b3033dd..7a9cf627059 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -5,7 +5,7 @@ use crate::utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::*; +use rustc_hir::{Body, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -- cgit 1.4.1-3-g733a5 From 532d205218c7aa28c1718f9e9088eeced6e2c5ac Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 19:24:11 +0900 Subject: Skip functions in PartialOrd --- clippy_lints/src/unnecessary_wrap.rs | 12 ++++++++++-- clippy_lints/src/utils/paths.rs | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 7a9cf627059..ec6c823a4ec 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,6 +1,6 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, + in_macro, is_type_diagnostic_item, match_path, match_qpath, paths, return_ty, snippet, span_lint_and_then, + trait_ref_of_method, visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -63,6 +63,14 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { + if_chain! { + if let Some(trait_ref) = trait_ref_of_method(cx, hir_id); + if match_path(trait_ref.path, &paths::PARTIAL_ORD); + then { + return; + } + } + match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2be5ff93f86..97e01f445ff 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -81,6 +81,7 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; +pub const PARTIAL_ORD: [&str; 3] = ["std", "cmp", "PartialOrd"]; pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; -- cgit 1.4.1-3-g733a5 From bf46f78ca7e15631f08253c14975896030ca24a8 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 19:35:59 +0900 Subject: Fix clippy error --- clippy_lints/src/redundant_clone.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index b4a9804fb25..c4ca7fcdecc 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -320,11 +320,11 @@ fn find_stmt_assigns_to<'tcx>( match (by_ref, &*rvalue) { (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => { - base_local_and_movability(cx, mir, *place) + Some(base_local_and_movability(cx, mir, *place)) }, (false, mir::Rvalue::Ref(_, _, place)) => { if let [mir::ProjectionElem::Deref] = place.as_ref().projection { - base_local_and_movability(cx, mir, *place) + Some(base_local_and_movability(cx, mir, *place)) } else { None } @@ -341,7 +341,7 @@ fn base_local_and_movability<'tcx>( cx: &LateContext<'tcx>, mir: &mir::Body<'tcx>, place: mir::Place<'tcx>, -) -> Option<(mir::Local, CannotMoveOut)> { +) -> (mir::Local, CannotMoveOut) { use rustc_middle::mir::PlaceRef; // Dereference. You cannot move things out from a borrowed value. @@ -362,7 +362,7 @@ fn base_local_and_movability<'tcx>( && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty); } - Some((local, deref || field || slice)) + (local, deref || field || slice) } struct LocalUseVisitor { -- cgit 1.4.1-3-g733a5 From 4e5c02e8980d16feeee953f112f940c598180ddc Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 19:25:54 +0900 Subject: Ignore trait implementations --- clippy_lints/src/unnecessary_wrap.rs | 20 +++++++++----------- clippy_lints/src/utils/paths.rs | 1 - tests/ui/unnecessary_wrap.rs | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index ec6c823a4ec..2960ffc5352 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,11 +1,11 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_path, match_qpath, paths, return_ty, snippet, span_lint_and_then, - trait_ref_of_method, visitors::find_all_ret_expressions, + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -63,14 +63,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { - if_chain! { - if let Some(trait_ref) = trait_ref_of_method(cx, hir_id); - if match_path(trait_ref.path, &paths::PARTIAL_ORD); - then { - return; - } - } - match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { @@ -81,6 +73,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { _ => (), } + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + return; + } + } + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { ("Option", &paths::OPTION_SOME) } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 97e01f445ff..2be5ff93f86 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -81,7 +81,6 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; -pub const PARTIAL_ORD: [&str; 3] = ["std", "cmp", "PartialOrd"]; pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 618c452065b..11208690428 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -95,6 +95,20 @@ impl A { } } +trait B { + // trait impls are not linted + fn func13() -> Option { + Some(1) + } +} + +impl A for B { + // trait impls are not linted + fn func13() -> Option { + Some(0) + } +} + fn main() { // method calls are not linted func1(true, true); -- cgit 1.4.1-3-g733a5 From c7445d7f2c3eece2b9056d05ea92fb1d1112b3ca Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 18 Nov 2020 01:01:22 +0900 Subject: Pluralize lint name --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +-- clippy_lints/src/unnecessary_wrap.rs | 143 ------------------------------ clippy_lints/src/unnecessary_wraps.rs | 143 ++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 4 +- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- tests/ui/doc_errors.rs | 2 +- tests/ui/drop_ref.rs | 2 +- tests/ui/forget_ref.rs | 2 +- tests/ui/let_underscore_must_use.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_err.rs | 2 +- tests/ui/map_flatten.fixed | 2 +- tests/ui/map_flatten.rs | 2 +- tests/ui/needless_lifetimes.rs | 2 +- tests/ui/option_map_unit_fn_fixable.fixed | 2 +- tests/ui/option_map_unit_fn_fixable.rs | 2 +- tests/ui/option_option.rs | 2 +- tests/ui/or_fun_call.fixed | 2 +- tests/ui/or_fun_call.rs | 2 +- tests/ui/panic_in_result_fn.rs | 2 +- tests/ui/question_mark.fixed | 2 +- tests/ui/question_mark.rs | 2 +- tests/ui/redundant_pattern_matching.fixed | 2 +- tests/ui/redundant_pattern_matching.rs | 2 +- tests/ui/result_unit_error.rs | 2 +- tests/ui/try_err.fixed | 2 +- tests/ui/try_err.rs | 2 +- tests/ui/unit_arg.rs | 2 +- tests/ui/unnecessary_clone.rs | 2 +- tests/ui/unnecessary_wrap.rs | 116 ------------------------ tests/ui/unnecessary_wrap.stderr | 106 ---------------------- tests/ui/unnecessary_wraps.rs | 116 ++++++++++++++++++++++++ tests/ui/unnecessary_wraps.stderr | 106 ++++++++++++++++++++++ tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- tests/ui/wildcard_imports.fixed | 2 +- tests/ui/wildcard_imports.rs | 2 +- 39 files changed, 403 insertions(+), 403 deletions(-) delete mode 100644 clippy_lints/src/unnecessary_wrap.rs create mode 100644 clippy_lints/src/unnecessary_wraps.rs delete mode 100644 tests/ui/unnecessary_wrap.rs delete mode 100644 tests/ui/unnecessary_wrap.stderr create mode 100644 tests/ui/unnecessary_wraps.rs create mode 100644 tests/ui/unnecessary_wraps.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b862d3196..64f67680b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2008,7 +2008,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap -[`unnecessary_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wrap +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2d1f75391bb..f0c1cb8d6e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,7 +323,7 @@ mod unicode; mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; -mod unnecessary_wrap; +mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -893,7 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, - &unnecessary_wrap::UNNECESSARY_WRAP, + &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, @@ -1066,7 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(|| box unnecessary_wrap::UnnecessaryWrap); + store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1574,7 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1779,7 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs deleted file mode 100644 index 2960ffc5352..00000000000 --- a/clippy_lints/src/unnecessary_wrap.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::utils::{ - in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, -}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for private functions that only return `Ok` or `Some`. - /// - /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. - /// - /// **Known problems:** Since this lint changes function type signature, you may need to - /// adjust some code at callee side. - /// - /// **Example:** - /// - /// ```rust - /// fn get_cool_number(a: bool, b: bool) -> Option { - /// if a && b { - /// return Some(50); - /// } - /// if a { - /// Some(0) - /// } else { - /// Some(10) - /// } - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn get_cool_number(a: bool, b: bool) -> i32 { - /// if a && b { - /// return 50; - /// } - /// if a { - /// 0 - /// } else { - /// 10 - /// } - /// } - /// ``` - pub UNNECESSARY_WRAP, - complexity, - "functions that only return `Ok` or `Some`" -} - -declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]); - -impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - fn_kind: FnKind<'tcx>, - fn_decl: &FnDecl<'tcx>, - body: &Body<'tcx>, - span: Span, - hir_id: HirId, - ) { - match fn_kind { - FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { - if visibility.node.is_pub() { - return; - } - }, - FnKind::Closure(..) => return, - _ => (), - } - - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { - return; - } - } - - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - ("Result", &paths::RESULT_OK) - } else { - return; - }; - - let mut suggs = Vec::new(); - let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { - if_chain! { - if !in_macro(ret_expr.span); - if let ExprKind::Call(ref func, ref args) = ret_expr.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, path); - if args.len() == 1; - then { - suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); - true - } else { - false - } - } - }); - - if can_sugg && !suggs.is_empty() { - span_lint_and_then( - cx, - UNNECESSARY_WRAP, - span, - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type - ) - .as_str(), - |diag| { - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.for_each(|inner_ty| { - diag.span_suggestion( - fn_decl.output.span(), - format!("remove `{}` from the return type...", return_type).as_str(), - inner_ty, - Applicability::MaybeIncorrect, - ); - }); - diag.multipart_suggestion( - "...and change the returning expressions", - suggs, - Applicability::MachineApplicable, - ); - }, - ); - } - } -} diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs new file mode 100644 index 00000000000..25ecc7a82f1 --- /dev/null +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -0,0 +1,143 @@ +use crate::utils::{ + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for private functions that only return `Ok` or `Some`. + /// + /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. + /// + /// **Known problems:** Since this lint changes function type signature, you may need to + /// adjust some code at callee side. + /// + /// **Example:** + /// + /// ```rust + /// fn get_cool_number(a: bool, b: bool) -> Option { + /// if a && b { + /// return Some(50); + /// } + /// if a { + /// Some(0) + /// } else { + /// Some(10) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn get_cool_number(a: bool, b: bool) -> i32 { + /// if a && b { + /// return 50; + /// } + /// if a { + /// 0 + /// } else { + /// 10 + /// } + /// } + /// ``` + pub UNNECESSARY_WRAPS, + complexity, + "functions that only return `Ok` or `Some`" +} + +declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + match fn_kind { + FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { + if visibility.node.is_pub() { + return; + } + }, + FnKind::Closure(..) => return, + _ => (), + } + + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + return; + } + } + + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + ("Option", &paths::OPTION_SOME) + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + ("Result", &paths::RESULT_OK) + } else { + return; + }; + + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { + if_chain! { + if !in_macro(ret_expr.span); + if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); + true + } else { + false + } + } + }); + + if can_sugg && !suggs.is_empty() { + span_lint_and_then( + cx, + UNNECESSARY_WRAPS, + span, + format!( + "this function's return value is unnecessarily wrapped by `{}`", + return_type + ) + .as_str(), + |diag| { + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.for_each(|inner_ty| { + diag.span_suggestion( + fn_decl.output.span(), + format!("remove `{}` from the return type...", return_type).as_str(), + inner_ty, + Applicability::MaybeIncorrect, + ); + }); + diag.multipart_suggestion( + "...and change the returning expressions", + suggs, + Applicability::MachineApplicable, + ); + }, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4a0cdc5d82f..a2edd6cd0bd 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2609,11 +2609,11 @@ vec![ module: "unwrap", }, Lint { - name: "unnecessary_wrap", + name: "unnecessary_wraps", group: "complexity", desc: "functions that only return `Ok` or `Some`", deprecation: None, - module: "unnecessary_wrap", + module: "unnecessary_wraps", }, Lint { name: "unneeded_field_pattern", diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 78ec1727fc1..6f12d36d777 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ #![warn(clippy::derive_ord_xor_partial_ord)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::cmp::Ordering; diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 77df7f176f0..c77a74a58f2 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,7 +1,7 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] #![allow(clippy::result_unit_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::io; diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index ba12e763821..e1a15c609fd 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,7 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::mem::drop; diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index a9c2a92ce6b..c49e6756a6c 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::mem::forget; diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index e3800cda1b1..a842e872a37 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -1,5 +1,5 @@ #![warn(clippy::let_underscore_must_use)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] // Debug implementations can fire this lint, // so we shouldn't lint external macros diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 740b2f66728..81d903c15d3 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wrap)] +#![allow(unused_variables, clippy::unnecessary_wraps)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 6750662c58c..16105d379c3 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wrap)] +#![allow(unused_variables, clippy::unnecessary_wraps)] fn option_unwrap_or() { // int case diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 231562507a8..05b9949f102 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,5 +1,5 @@ #![warn(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index b4a51837b2f..773b5914439 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -4,7 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index e83cc46bda2..578bd877267 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -4,7 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index e5973bbef8d..44972c8c639 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_lifetimes)] -#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wrap)] +#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps)] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index de2e9155906..7d29445e66c 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -2,7 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index f0887c8a4bc..b6f834f686f 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -2,7 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 557d29dff67..6859ba8e5bb 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,5 +1,5 @@ #![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn input(_: Option>) {} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index e1735bc88f5..2a63318c8c7 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index a6abd2e8b34..026ef437caa 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs index be4e85d05a7..3d3c19a1be5 100644 --- a/tests/ui/panic_in_result_fn.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] struct A; diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index df3c98a0aa5..0b5746cb522 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(unreachable_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn some_func(a: Option) -> Option { a?; diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 62b3e96b65a..0f0825c9334 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(unreachable_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn some_func(a: Option) -> Option { if a.is_none() { diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 2e7ba8bc04d..aa20512296a 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -7,7 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index ea0e18f5970..d76f9c288ff 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -7,7 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, deprecated )] diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index 1b4a702377e..5e57c752b5a 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[warn(clippy::result_unit_err)] #[allow(unused)] diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 19458ddf68e..652b611208b 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index ab6a32dbe4d..6bd479657b7 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 6bf704c09e0..9ad16d36509 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -4,7 +4,7 @@ unused_must_use, unused_variables, clippy::unused_unit, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, clippy::or_fun_call )] diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 3b1a6cf57c6..6770a7fac90 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wrap)] +#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs deleted file mode 100644 index 58178e2e04b..00000000000 --- a/tests/ui/unnecessary_wrap.rs +++ /dev/null @@ -1,116 +0,0 @@ -#![warn(clippy::unnecessary_wrap)] -#![allow(clippy::no_effect)] -#![allow(clippy::needless_return)] -#![allow(clippy::if_same_then_else)] -#![allow(dead_code)] - -// should be linted -fn func1(a: bool, b: bool) -> Option { - if a && b { - return Some(42); - } - if a { - Some(-1); - Some(2) - } else { - return Some(1337); - } -} - -// should be linted -fn func2(a: bool, b: bool) -> Option { - if a && b { - return Some(10); - } - if a { - Some(20) - } else { - Some(30) - } -} - -// public fns should not be linted -pub fn func3(a: bool) -> Option { - if a { - Some(1) - } else { - Some(1) - } -} - -// should not be linted -fn func4(a: bool) -> Option { - if a { - Some(1) - } else { - None - } -} - -// should be linted -fn func5() -> Option { - Some(1) -} - -// should not be linted -fn func6() -> Option { - None -} - -// should be linted -fn func7() -> Result { - Ok(1) -} - -// should not be linted -fn func8(a: bool) -> Result { - if a { - Ok(1) - } else { - Err(()) - } -} - -// should not be linted -fn func9(a: bool) -> Result { - Err(()) -} - -// should not be linted -fn func10() -> Option<()> { - unimplemented!() -} - -struct A; - -impl A { - // should not be linted - pub fn func11() -> Option { - Some(1) - } - - // should be linted - fn func12() -> Option { - Some(1) - } -} - -trait B { - // trait impls are not linted - fn func13() -> Option { - Some(1) - } -} - -impl B for A { - // trait impls are not linted - fn func13() -> Option { - Some(0) - } -} - -fn main() { - // method calls are not linted - func1(true, true); - func2(true, true); -} diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr deleted file mode 100644 index 5f21b74bc76..00000000000 --- a/tests/ui/unnecessary_wrap.stderr +++ /dev/null @@ -1,106 +0,0 @@ -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:8:1 - | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(42); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | - = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` -help: remove `Option` from the return type... - | -LL | fn func1(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 42; -LL | } -LL | if a { -LL | Some(-1); -LL | 2 -LL | } else { - ... - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:21:1 - | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(10); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func2(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 10; -LL | } -LL | if a { -LL | 20 -LL | } else { -LL | 30 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:51:1 - | -LL | / fn func5() -> Option { -LL | | Some(1) -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func5() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wrap.rs:61:1 - | -LL | / fn func7() -> Result { -LL | | Ok(1) -LL | | } - | |_^ - | -help: remove `Result` from the return type... - | -LL | fn func7() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:93:5 - | -LL | / fn func12() -> Option { -LL | | Some(1) -LL | | } - | |_____^ - | -help: remove `Option` from the return type... - | -LL | fn func12() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: aborting due to 5 previous errors - diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs new file mode 100644 index 00000000000..a53dec8f91a --- /dev/null +++ b/tests/ui/unnecessary_wraps.rs @@ -0,0 +1,116 @@ +#![warn(clippy::unnecessary_wraps)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] +#![allow(dead_code)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// should be linted +fn func2(a: bool, b: bool) -> Option { + if a && b { + return Some(10); + } + if a { + Some(20) + } else { + Some(30) + } +} + +// public fns should not be linted +pub fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func4(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func5() -> Option { + Some(1) +} + +// should not be linted +fn func6() -> Option { + None +} + +// should be linted +fn func7() -> Result { + Ok(1) +} + +// should not be linted +fn func8(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } +} + +// should not be linted +fn func9(a: bool) -> Result { + Err(()) +} + +// should not be linted +fn func10() -> Option<()> { + unimplemented!() +} + +struct A; + +impl A { + // should not be linted + pub fn func11() -> Option { + Some(1) + } + + // should be linted + fn func12() -> Option { + Some(1) + } +} + +trait B { + // trait impls are not linted + fn func13() -> Option { + Some(1) + } +} + +impl B for A { + // trait impls are not linted + fn func13() -> Option { + Some(0) + } +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true, true); +} diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr new file mode 100644 index 00000000000..410f054b8ef --- /dev/null +++ b/tests/ui/unnecessary_wraps.stderr @@ -0,0 +1,106 @@ +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:8:1 + | +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` +help: remove `Option` from the return type... + | +LL | fn func1(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:21:1 + | +LL | / fn func2(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(10); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func2(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 10; +LL | } +LL | if a { +LL | 20 +LL | } else { +LL | 30 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:51:1 + | +LL | / fn func5() -> Option { +LL | | Some(1) +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func5() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Result` + --> $DIR/unnecessary_wraps.rs:61:1 + | +LL | / fn func7() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: remove `Result` from the return type... + | +LL | fn func7() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:93:5 + | +LL | / fn func12() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func12() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 45ee367d649..03977de9455 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,7 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn test_generic(val: T) -> T { let _ = val; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index e5bdffed20d..f6e094c1661 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,7 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn test_generic(val: T) -> T { let _ = T::from(val); diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 14007e5f251..ee9c9045fff 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -4,7 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 0e8631ca704..efaa8f9ef66 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -4,7 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #![warn(unused_imports)] extern crate wildcard_imports_helper; -- cgit 1.4.1-3-g733a5 From bf2d31d0533ee03a2c1e91f74780ea779e8b330c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 17 Nov 2020 18:08:12 +0100 Subject: Run cargo dev fmt --- clippy_lints/src/mut_key.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 9cceecc785a..11044e0c2fb 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -89,11 +89,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir:: for (hir_ty, ty) in decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) { check_ty(cx, hir_ty.span, ty); } - check_ty( - cx, - decl.output.span(), - cx.tcx.erase_late_bound_regions(fn_sig.output()), - ); + check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); } // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased -- cgit 1.4.1-3-g733a5 From 5a8396887714fb75f44eae2a3775b1b2a12f38ae Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Tue, 17 Nov 2020 22:34:39 +0100 Subject: Change `redundant_pattern_matching` to also lint `std::task::Poll` Suggest using utility methods `is_pending` and `is_ready`. --- clippy_lints/src/matches.rs | 33 ++++- clippy_lints/src/utils/paths.rs | 2 + tests/ui/redundant_pattern_matching.fixed | 110 ---------------- tests/ui/redundant_pattern_matching.rs | 128 ------------------ tests/ui/redundant_pattern_matching.stderr | 154 ---------------------- tests/ui/redundant_pattern_matching_option.fixed | 8 +- tests/ui/redundant_pattern_matching_option.rs | 8 +- tests/ui/redundant_pattern_matching_option.stderr | 38 +++--- tests/ui/redundant_pattern_matching_poll.fixed | 73 ++++++++++ tests/ui/redundant_pattern_matching_poll.rs | 88 +++++++++++++ tests/ui/redundant_pattern_matching_poll.stderr | 128 ++++++++++++++++++ tests/ui/redundant_pattern_matching_result.fixed | 109 +++++++++++++++ tests/ui/redundant_pattern_matching_result.rs | 127 ++++++++++++++++++ tests/ui/redundant_pattern_matching_result.stderr | 154 ++++++++++++++++++++++ 14 files changed, 732 insertions(+), 428 deletions(-) delete mode 100644 tests/ui/redundant_pattern_matching.fixed delete mode 100644 tests/ui/redundant_pattern_matching.rs delete mode 100644 tests/ui/redundant_pattern_matching.stderr create mode 100644 tests/ui/redundant_pattern_matching_poll.fixed create mode 100644 tests/ui/redundant_pattern_matching_poll.rs create mode 100644 tests/ui/redundant_pattern_matching_poll.stderr create mode 100644 tests/ui/redundant_pattern_matching_result.fixed create mode 100644 tests/ui/redundant_pattern_matching_result.rs create mode 100644 tests/ui/redundant_pattern_matching_result.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c6dca54e250..af59917e801 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -411,8 +411,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result` or - /// `Option` + /// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or + /// `std::task::Poll` /// /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function @@ -422,10 +422,13 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// # use std::task::Poll; /// if let Ok(_) = Ok::(42) {} /// if let Err(_) = Err::(42) {} /// if let None = None::<()> {} /// if let Some(_) = Some(42) {} + /// if let Poll::Pending = Poll::Pending::<()> {} + /// if let Poll::Ready(_) = Poll::Ready(42) {} /// match Ok::(42) { /// Ok(_) => true, /// Err(_) => false, @@ -435,10 +438,13 @@ declare_clippy_lint! { /// The more idiomatic use would be: /// /// ```rust + /// # use std::task::Poll; /// if Ok::(42).is_ok() {} /// if Err::(42).is_err() {} /// if None::<()>.is_none() {} /// if Some(42).is_some() {} + /// if Poll::Pending::<()>.is_pending() {} + /// if Poll::Ready(42).is_ready() {} /// Ok::(42).is_ok(); /// ``` pub REDUNDANT_PATTERN_MATCHING, @@ -1538,6 +1544,8 @@ mod redundant_pattern_match { "is_err()" } else if match_qpath(path, &paths::OPTION_SOME) { "is_some()" + } else if match_qpath(path, &paths::POLL_READY) { + "is_ready()" } else { return; } @@ -1545,7 +1553,15 @@ mod redundant_pattern_match { return; } }, - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", + PatKind::Path(ref path) => { + if match_qpath(path, &paths::OPTION_NONE) { + "is_none()" + } else if match_qpath(path, &paths::POLL_PENDING) { + "is_pending()" + } else { + return; + } + }, _ => return, }; @@ -1628,6 +1644,17 @@ mod redundant_pattern_match { "is_some()", "is_none()", ) + .or_else(|| { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::POLL_READY, + &paths::POLL_PENDING, + "is_ready()", + "is_pending()", + ) + }) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2be5ff93f86..e20f146f145 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -86,6 +86,8 @@ pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; +pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; +pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed deleted file mode 100644 index aa20512296a..00000000000 --- a/tests/ui/redundant_pattern_matching.fixed +++ /dev/null @@ -1,110 +0,0 @@ -// 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, - clippy::unnecessary_wraps, - deprecated -)] - -fn main() { - let result: Result = Err(5); - if result.is_ok() {} - - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - while Ok::(10).is_ok() {} - - while Ok::(10).is_err() {} - - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - if let Ok(x) = Ok::(42) { - println!("{}", x); - } - - Ok::(42).is_ok(); - - Ok::(42).is_err(); - - Err::(42).is_err(); - - Err::(42).is_ok(); - - let _ = if Ok::(4).is_ok() { true } else { false }; - - issue5504(); - issue6067(); - issue6065(); - - let _ = if gen_res().is_ok() { - 1 - } else if gen_res().is_err() { - 2 - } else { - 3 - }; -} - -fn gen_res() -> Result<(), ()> { - Ok(()) -} - -macro_rules! m { - () => { - Some(42u32) - }; -} - -fn issue5504() { - fn result_opt() -> Result, i32> { - Err(42) - } - - fn try_result_opt() -> Result { - while r#try!(result_opt()).is_some() {} - if r#try!(result_opt()).is_some() {} - Ok(42) - } - - try_result_opt(); - - if m!().is_some() {} - 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. -const fn issue6067() { - 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(); -} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs deleted file mode 100644 index d76f9c288ff..00000000000 --- a/tests/ui/redundant_pattern_matching.rs +++ /dev/null @@ -1,128 +0,0 @@ -// 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, - clippy::unnecessary_wraps, - deprecated -)] - -fn main() { - let result: Result = Err(5); - if let Ok(_) = &result {} - - if let Ok(_) = Ok::(42) {} - - if let Err(_) = Err::(42) {} - - while let Ok(_) = Ok::(10) {} - - while let Err(_) = Ok::(10) {} - - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - if let Ok(x) = Ok::(42) { - println!("{}", x); - } - - match Ok::(42) { - Ok(_) => true, - Err(_) => false, - }; - - match Ok::(42) { - Ok(_) => false, - Err(_) => true, - }; - - match Err::(42) { - Ok(_) => false, - Err(_) => true, - }; - - match Err::(42) { - Ok(_) => true, - Err(_) => false, - }; - - let _ = if let Ok(_) = Ok::(4) { true } else { false }; - - issue5504(); - issue6067(); - issue6065(); - - let _ = if let Ok(_) = gen_res() { - 1 - } else if let Err(_) = gen_res() { - 2 - } else { - 3 - }; -} - -fn gen_res() -> Result<(), ()> { - Ok(()) -} - -macro_rules! m { - () => { - Some(42u32) - }; -} - -fn issue5504() { - fn result_opt() -> Result, i32> { - Err(42) - } - - fn try_result_opt() -> Result { - while let Some(_) = r#try!(result_opt()) {} - if let Some(_) = r#try!(result_opt()) {} - Ok(42) - } - - try_result_opt(); - - if let Some(_) = m!() {} - 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. -const fn issue6067() { - 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, - }; -} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr deleted file mode 100644 index aeb309f5ba1..00000000000 --- a/tests/ui/redundant_pattern_matching.stderr +++ /dev/null @@ -1,154 +0,0 @@ -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:16:12 - | -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:18:12 - | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:20:12 - | -LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:22:15 - | -LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:24:15 - | -LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:34:5 - | -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.rs:39:5 - | -LL | / match Ok::(42) { -LL | | Ok(_) => false, -LL | | Err(_) => true, -LL | | }; - | |_____^ help: try this: `Ok::(42).is_err()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:44:5 - | -LL | / match Err::(42) { -LL | | Ok(_) => false, -LL | | Err(_) => true, -LL | | }; - | |_____^ help: try this: `Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 - | -LL | / match Err::(42) { -LL | | Ok(_) => true, -LL | | Err(_) => false, -LL | | }; - | |_____^ help: try this: `Err::(42).is_ok()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:54:20 - | -LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; - | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:60:20 - | -LL | let _ = if let Ok(_) = gen_res() { - | -------^^^^^------------ help: try this: `if gen_res().is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:62:19 - | -LL | } else if let Err(_) = gen_res() { - | -------^^^^^^------------ help: try this: `if gen_res().is_err()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:85:19 - | -LL | while let Some(_) = r#try!(result_opt()) {} - | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:86:16 - | -LL | if let Some(_) = r#try!(result_opt()) {} - | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:92:12 - | -LL | if let Some(_) = m!() {} - | -------^^^^^^^------- help: try this: `if m!().is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:15 - | -LL | while let Some(_) = m!() {} - | ----------^^^^^^^------- help: try this: `while m!().is_some()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:111:12 - | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:113:12 - | -LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:115:15 - | -LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:117:15 - | -LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:119: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.rs:124:5 - | -LL | / match Err::(42) { -LL | | Ok(_) => false, -LL | | Err(_) => true, -LL | | }; - | |_____^ help: try this: `Err::(42).is_err()` - -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 index 499b975b2bb..bc369dd2491 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,13 +2,7 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - deprecated -)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 2a98435e790..d7616a72913 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,13 +2,7 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - deprecated -)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index eebb3448491..7ddfbe503a2 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:14:12 + --> $DIR/redundant_pattern_matching_option.rs:8:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` @@ -7,43 +7,43 @@ LL | if let None = None::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:16:12 + --> $DIR/redundant_pattern_matching_option.rs:10:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:18:12 + --> $DIR/redundant_pattern_matching_option.rs:12:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:24:15 + --> $DIR/redundant_pattern_matching_option.rs:18:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:26:15 + --> $DIR/redundant_pattern_matching_option.rs:20:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:28:15 + --> $DIR/redundant_pattern_matching_option.rs:22:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:31:15 + --> $DIR/redundant_pattern_matching_option.rs:25:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:39:5 + --> $DIR/redundant_pattern_matching_option.rs:33:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:44:5 + --> $DIR/redundant_pattern_matching_option.rs:38:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:49:13 + --> $DIR/redundant_pattern_matching_option.rs:43:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,49 +71,49 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:55:20 + --> $DIR/redundant_pattern_matching_option.rs:49:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:60:20 + --> $DIR/redundant_pattern_matching_option.rs:54:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:62:19 + --> $DIR/redundant_pattern_matching_option.rs:56:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:83:12 + --> $DIR/redundant_pattern_matching_option.rs:77:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:85:12 + --> $DIR/redundant_pattern_matching_option.rs:79:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:87:15 + --> $DIR/redundant_pattern_matching_option.rs:81:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:89:15 + --> $DIR/redundant_pattern_matching_option.rs:83:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:91:5 + --> $DIR/redundant_pattern_matching_option.rs:85:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:96:5 + --> $DIR/redundant_pattern_matching_option.rs:90:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed new file mode 100644 index 00000000000..564c427f063 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::task::Poll::{self, Pending, Ready}; + +fn main() { + if Pending::<()>.is_pending() {} + + if Ready(42).is_ready() {} + + if Ready(42).is_ready() { + foo(); + } else { + bar(); + } + + while Ready(42).is_ready() {} + + while Ready(42).is_pending() {} + + while Pending::<()>.is_pending() {} + + if Pending::.is_pending() {} + + if Ready(42).is_ready() {} + + Ready(42).is_ready(); + + Pending::<()>.is_pending(); + + let _ = Pending::<()>.is_pending(); + + let poll = Ready(false); + let x = if poll.is_ready() { true } else { false }; + takes_poll(x); + + poll_const(); + + let _ = if gen_poll().is_ready() { + 1 + } else if gen_poll().is_pending() { + 2 + } else { + 3 + }; +} + +fn gen_poll() -> Poll<()> { + Pending +} + +fn takes_poll(_: bool) {} + +fn foo() {} + +fn bar() {} + +const fn poll_const() { + if Ready(42).is_ready() {} + + if Pending::<()>.is_pending() {} + + while Ready(42).is_ready() {} + + while Pending::<()>.is_pending() {} + + Ready(42).is_ready(); + + Pending::<()>.is_pending(); +} diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs new file mode 100644 index 00000000000..d453d4184af --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -0,0 +1,88 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::task::Poll::{self, Pending, Ready}; + +fn main() { + if let Pending = Pending::<()> {} + + if let Ready(_) = Ready(42) {} + + if let Ready(_) = Ready(42) { + foo(); + } else { + bar(); + } + + while let Ready(_) = Ready(42) {} + + while let Pending = Ready(42) {} + + while let Pending = Pending::<()> {} + + if Pending::.is_pending() {} + + if Ready(42).is_ready() {} + + match Ready(42) { + Ready(_) => true, + Pending => false, + }; + + match Pending::<()> { + Ready(_) => false, + Pending => true, + }; + + let _ = match Pending::<()> { + Ready(_) => false, + Pending => true, + }; + + let poll = Ready(false); + let x = if let Ready(_) = poll { true } else { false }; + takes_poll(x); + + poll_const(); + + let _ = if let Ready(_) = gen_poll() { + 1 + } else if let Pending = gen_poll() { + 2 + } else { + 3 + }; +} + +fn gen_poll() -> Poll<()> { + Pending +} + +fn takes_poll(_: bool) {} + +fn foo() {} + +fn bar() {} + +const fn poll_const() { + if let Ready(_) = Ready(42) {} + + if let Pending = Pending::<()> {} + + while let Ready(_) = Ready(42) {} + + while let Pending = Pending::<()> {} + + match Ready(42) { + Ready(_) => true, + Pending => false, + }; + + match Pending::<()> { + Ready(_) => false, + Pending => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr new file mode 100644 index 00000000000..42e5d6f41fe --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -0,0 +1,128 @@ +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:10:12 + | +LL | if let Pending = Pending::<()> {} + | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:12:12 + | +LL | if let Ready(_) = Ready(42) {} + | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:14:12 + | +LL | if let Ready(_) = Ready(42) { + | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:20:15 + | +LL | while let Ready(_) = Ready(42) {} + | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:22:15 + | +LL | while let Pending = Ready(42) {} + | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:24:15 + | +LL | while let Pending = Pending::<()> {} + | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:30:5 + | +LL | / match Ready(42) { +LL | | Ready(_) => true, +LL | | Pending => false, +LL | | }; + | |_____^ help: try this: `Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:35:5 + | +LL | / match Pending::<()> { +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:40:13 + | +LL | let _ = match Pending::<()> { + | _____________^ +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:46:20 + | +LL | let x = if let Ready(_) = poll { true } else { false }; + | -------^^^^^^^^------- help: try this: `if poll.is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:51:20 + | +LL | let _ = if let Ready(_) = gen_poll() { + | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:53:19 + | +LL | } else if let Pending = gen_poll() { + | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:71:12 + | +LL | if let Ready(_) = Ready(42) {} + | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:73:12 + | +LL | if let Pending = Pending::<()> {} + | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:75:15 + | +LL | while let Ready(_) = Ready(42) {} + | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:77:15 + | +LL | while let Pending = Pending::<()> {} + | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:79:5 + | +LL | / match Ready(42) { +LL | | Ready(_) => true, +LL | | Pending => false, +LL | | }; + | |_____^ help: try this: `Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:84:5 + | +LL | / match Pending::<()> { +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed new file mode 100644 index 00000000000..e94c5704b48 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -0,0 +1,109 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::unnecessary_wraps, + deprecated +)] + +fn main() { + let result: Result = Err(5); + if result.is_ok() {} + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + while Ok::(10).is_ok() {} + + while Ok::(10).is_err() {} + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if let Ok(x) = Ok::(42) { + println!("{}", x); + } + + Ok::(42).is_ok(); + + Ok::(42).is_err(); + + Err::(42).is_err(); + + Err::(42).is_ok(); + + let _ = if Ok::(4).is_ok() { true } else { false }; + + issue5504(); + issue6067(); + issue6065(); + + let _ = if gen_res().is_ok() { + 1 + } else if gen_res().is_err() { + 2 + } else { + 3 + }; +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result, i32> { + Err(42) + } + + fn try_result_opt() -> Result { + while r#try!(result_opt()).is_some() {} + if r#try!(result_opt()).is_some() {} + Ok(42) + } + + try_result_opt(); + + if m!().is_some() {} + 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. +const fn issue6067() { + 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(); +} diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs new file mode 100644 index 00000000000..5d175294232 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -0,0 +1,127 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::unnecessary_wraps, + deprecated +)] + +fn main() { + let result: Result = Err(5); + if let Ok(_) = &result {} + + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if let Ok(x) = Ok::(42) { + println!("{}", x); + } + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Ok::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => true, + Err(_) => false, + }; + + let _ = if let Ok(_) = Ok::(4) { true } else { false }; + + issue5504(); + issue6067(); + issue6065(); + + let _ = if let Ok(_) = gen_res() { + 1 + } else if let Err(_) = gen_res() { + 2 + } else { + 3 + }; +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result, i32> { + Err(42) + } + + fn try_result_opt() -> Result { + while let Some(_) = r#try!(result_opt()) {} + if let Some(_) = r#try!(result_opt()) {} + Ok(42) + } + + try_result_opt(); + + if let Some(_) = m!() {} + 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. +const fn issue6067() { + 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, + }; +} diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr new file mode 100644 index 00000000000..d6a46babb77 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -0,0 +1,154 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:15:12 + | +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_result.rs:17:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:19:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:21:15 + | +LL | while let Ok(_) = Ok::(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:23:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:33: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_result.rs:38:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:43:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:48:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Err::(42).is_ok()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:53:20 + | +LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; + | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:59:20 + | +LL | let _ = if let Ok(_) = gen_res() { + | -------^^^^^------------ help: try this: `if gen_res().is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:61:19 + | +LL | } else if let Err(_) = gen_res() { + | -------^^^^^^------------ help: try this: `if gen_res().is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:84:19 + | +LL | while let Some(_) = r#try!(result_opt()) {} + | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:85:16 + | +LL | if let Some(_) = r#try!(result_opt()) {} + | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:91:12 + | +LL | if let Some(_) = m!() {} + | -------^^^^^^^------- help: try this: `if m!().is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:92:15 + | +LL | while let Some(_) = m!() {} + | ----------^^^^^^^------- help: try this: `while m!().is_some()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:110:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:112:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:114:15 + | +LL | while let Ok(_) = Ok::(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:116:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:118: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_result.rs:123:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: aborting due to 22 previous errors + -- cgit 1.4.1-3-g733a5 From 0502ac2a87458620ae83e5260fd13149a558e090 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 18 Nov 2020 08:33:25 +0900 Subject: Improve doc about `map_clone` --- clippy_lints/src/map_clone.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 3f34dd5112e..220240acb7a 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -14,8 +14,9 @@ use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; declare_clippy_lint! { - /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests - /// `iterator.cloned()` instead + /// **What it does:** Checks for usage of `map(|x| x.clone())` or + /// dereferencing closures for `Copy` types, on `Iterator` or `Option`, + /// and suggests `cloned()` or `copied()` instead /// /// **Why is this bad?** Readability, this can be written more concisely /// -- cgit 1.4.1-3-g733a5 From dd4e471b3fb701312c2a3fabfba0011f239d4760 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 20 Nov 2020 09:37:47 +0100 Subject: Properly deprecate panic_params lint --- clippy_lints/src/deprecated_lints.rs | 5 +++++ clippy_lints/src/lib.rs | 4 ++++ src/lintlist/mod.rs | 7 ------- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +++++++- 5 files changed, 17 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 461c6e31d3e..1c3285ed701 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -181,3 +181,8 @@ declare_deprecated_lint! { pub TEMPORARY_CSTRING_AS_PTR, "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" } + +declare_deprecated_lint! { + pub PANIC_PARAMS, + "this lint has been uplifted to rustc and is now called `panic_fmt`" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19bf67d80c4..ff5d4846995 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -495,6 +495,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::temporary_cstring_as_ptr", "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", ); + store.register_removed( + "clippy::panic_params", + "this lint has been uplifted to rustc and is now called `panic_fmt`", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 702f9d86de6..213e5758659 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1831,13 +1831,6 @@ vec![ deprecation: None, module: "panic_in_result_fn", }, - Lint { - name: "panic_params", - group: "style", - desc: "missing parameters in `panic!` calls", - deprecation: None, - module: "panic_unimplemented", - }, Lint { name: "panicking_unwrap", group: "correctness", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 56755596c97..4cbc5630d75 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -10,5 +10,6 @@ #[warn(clippy::regex_macro)] #[warn(clippy::drop_bounds)] #[warn(clippy::temporary_cstring_as_ptr)] +#[warn(clippy::panic_params)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 37b726fc00f..a348d01d734 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -72,11 +72,17 @@ error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has LL | #[warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt`` + --> $DIR/deprecated.rs:13:8 + | +LL | #[warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 5ee0a400fc5104a0fb9be5c85c48d79cf1af8dce Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 20 Nov 2020 10:06:26 +0100 Subject: Fix dogfood errors --- clippy_lints/src/panic_unimplemented.rs | 2 +- clippy_lints/src/utils/ast_utils.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 8b10d071647..31b03ecd101 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -73,7 +73,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(_) = match_panic_call(cx, expr) { + if match_panic_call(cx, expr).is_some() { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { span_lint( diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 9050b9b2d9a..fcf7a4b1367 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -110,8 +110,7 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { match (l, r) { (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), - (StructRest::Rest(_), StructRest::Rest(_)) => true, - (StructRest::None, StructRest::None) => true, + (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, _ => false, } } -- cgit 1.4.1-3-g733a5 From 8b21241fd57376ab35e8ae75b3f57b7c601cdcdb Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Fri, 20 Nov 2020 11:35:15 -0500 Subject: Revert "Convert the await holding lints to correctness" This reverts commit d8c6bce4407b1c99ed961f75a093ffe767818069. --- clippy_lints/src/await_holding_invalid.rs | 4 ++-- clippy_lints/src/lib.rs | 6 ++---- src/lintlist/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index ca819663fde..58892024ce2 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_LOCK, - correctness, + pedantic, "Inside an async function, holding a MutexGuard while calling await" } @@ -84,7 +84,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_REFCELL_REF, - correctness, + pedantic, "Inside an async function, holding a RefCell ref while calling await" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 36016988b6b..7e8cbd00c22 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1224,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), @@ -1327,8 +1329,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), @@ -1793,8 +1793,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&booleans::LOGIC_BUG), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c5a31fac254..79b24b83680 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -62,14 +62,14 @@ vec![ }, Lint { name: "await_holding_lock", - group: "correctness", + group: "pedantic", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", - group: "correctness", + group: "pedantic", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, module: "await_holding_invalid", -- cgit 1.4.1-3-g733a5 From 9b910e19ce9bcd27c6dad220aba38121dc85138e Mon Sep 17 00:00:00 2001 From: oliver <16816606+o752d@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:44:47 +0000 Subject: a typo typo --- clippy_lints/src/assertions_on_constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a52f0997d43..62c73dbac48 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -129,7 +129,7 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) if let ExprKind::Block(ref block, _) = arms[0].body.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; - // inner block is optional. unwarp it if it exists, or use the expression as is otherwise. + // inner block is optional. unwrap it if it exists, or use the expression as is otherwise. if let Some(begin_panic_call) = match block_expr.kind { ExprKind::Block(ref inner_block, _) => &inner_block.expr, _ => &block.expr, -- cgit 1.4.1-3-g733a5 From 034244f1081ee65ce9a258e99cf39baa5d692bca Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sat, 21 Nov 2020 15:00:03 -0500 Subject: Small grammar, punctuation, and code style improvements to docs --- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/regex.rs | 4 ++-- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/useless_conversion.rs | 6 +++--- src/lintlist/mod.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8e2f03d6e4e..8842901d90b 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -69,7 +69,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for comparing to an empty slice such as "" or [],` + /// **What it does:** Checks for comparing to an empty slice such as `""` or `[]`, /// and suggests using `.is_empty()` where applicable. /// /// **Why is this bad?** Some structures can answer `.is_empty()` much faster diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ff0a3532465..fa174404365 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1351,7 +1351,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).collect::()`. + /// **What it does:** Checks for usage of `_.map(_).collect::()`. /// /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic. /// diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 95594e38c9e..d06ab143482 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -11,7 +11,7 @@ use std::convert::TryFrom; declare_clippy_lint! { /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation - /// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct + /// (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct /// regex syntax. /// /// **Why is this bad?** This will lead to a runtime panic. @@ -29,7 +29,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) - /// creation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`). + /// creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`). /// /// **Why is this bad?** Matching the regex can likely be replaced by `==` or /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str` diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index ade5fff5ffc..2501635e7ef 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// let mut twins = vec!((1,1), (2,2)); + /// let mut twins = vec!((1, 1), (2, 2)); /// twins.sort_by_key(|x| { x.1; }); /// ``` pub UNIT_RETURN_EXPECTING_ORD, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index c6194b0c6de..99c38e8f176 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -12,8 +12,8 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { - /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls - /// that useless converts to the same type as caller. + /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls + /// that uselessly convert to the same type. /// /// **Why is this bad?** Redundant code. /// @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` that perform useless conversions to the same type" } #[derive(Default)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 79b24b83680..1d906d20ad4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2751,7 +2751,7 @@ vec![ Lint { name: "useless_conversion", group: "complexity", - desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type", + desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type", deprecation: None, module: "useless_conversion", }, -- cgit 1.4.1-3-g733a5 From 445466e567c192bb81ccc366743793ffb079da5d Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" <193874+carols10cents@users.noreply.github.com> Date: Sun, 22 Nov 2020 10:12:41 -0500 Subject: Apply suggestions from code review to change "that" to "which" Co-authored-by: oliver <16816606+o752d@users.noreply.github.com> --- clippy_lints/src/useless_conversion.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 99c38e8f176..efa9c3fab4a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -13,7 +13,7 @@ use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls - /// that uselessly convert to the same type. + /// which uselessly convert to the same type. /// /// **Why is this bad?** Redundant code. /// @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` that perform useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type" } #[derive(Default)] -- cgit 1.4.1-3-g733a5 From a8c47440b4ac59239f11838cfc7426b2adef3685 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 10 Nov 2020 09:19:30 -0600 Subject: Use article_and_description for missing docs --- clippy_lints/src/missing_doc.rs | 64 ++++++++++++++++--------------- tests/ui/missing-doc-crate-missing.stderr | 2 +- tests/ui/missing-doc-impl.stderr | 12 +++--- 3 files changed, 40 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 009e3d8937e..f2c0ab1222c 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -2,7 +2,7 @@ // *rustc*'s // [`missing_doc`]. // -// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246 +// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // use crate::utils::span_lint; @@ -70,7 +70,14 @@ impl MissingDoc { } } - fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { + fn check_missing_docs_attrs( + &self, + cx: &LateContext<'_>, + attrs: &[ast::Attribute], + sp: Span, + article: &'static str, + desc: &'static str, + ) { // If we're building a test harness, then warning about // documentation is probably not really relevant right now. if cx.sess().opts.test { @@ -94,7 +101,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {}", desc), + &format!("missing documentation for {} {}", article, desc), ); } } @@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate"); + self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - let desc = match it.kind { - hir::ItemKind::Const(..) => "a constant", - hir::ItemKind::Enum(..) => "an enum", + match it.kind { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { @@ -136,16 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { return; } } - "a function" }, - hir::ItemKind::Mod(..) => "a module", - hir::ItemKind::Static(..) => "a static", - hir::ItemKind::Struct(..) => "a struct", - hir::ItemKind::Trait(..) => "a trait", - hir::ItemKind::TraitAlias(..) => "a trait alias", - hir::ItemKind::TyAlias(..) => "a type alias", - hir::ItemKind::Union(..) => "a union", - hir::ItemKind::OpaqueTy(..) => "an existential type", + hir::ItemKind::Const(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::OpaqueTy(..) => {}, hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod(..) | hir::ItemKind::GlobalAsm(..) @@ -153,17 +159,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Use(..) => return, }; - self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc); + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { - let desc = match trait_item.kind { - hir::TraitItemKind::Const(..) => "an associated constant", - hir::TraitItemKind::Fn(..) => "a trait method", - hir::TraitItemKind::Type(..) => "an associated type", - }; + let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc); + self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { @@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { }, } - let desc = match impl_item.kind { - hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::TyAlias(_) => "an associated type", - }; - self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc); } fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field"); + self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field"); } } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant"); + self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant"); } } diff --git a/tests/ui/missing-doc-crate-missing.stderr b/tests/ui/missing-doc-crate-missing.stderr index da46f988636..d56c5cc4c3a 100644 --- a/tests/ui/missing-doc-crate-missing.stderr +++ b/tests/ui/missing-doc-crate-missing.stderr @@ -1,4 +1,4 @@ -error: missing documentation for crate +error: missing documentation for the crate --> $DIR/missing-doc-crate-missing.rs:1:1 | LL | / #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing-doc-impl.stderr b/tests/ui/missing-doc-impl.stderr index 9656a39abce..7e10404ca00 100644 --- a/tests/ui/missing-doc-impl.stderr +++ b/tests/ui/missing-doc-impl.stderr @@ -51,13 +51,13 @@ LL | | fn foo_with_impl(&self) {} LL | | } | |_^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:39:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:40:5 | LL | fn foo_with_impl(&self) {} @@ -75,25 +75,25 @@ error: missing documentation for an associated type LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:62:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:63:5 | LL | fn bar() {} | ^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:67:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:70:5 | LL | fn foo2() {} -- cgit 1.4.1-3-g733a5 From a39a93faeb4969fac62e896138cdf72377fa5502 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 22 Nov 2020 19:21:01 -0600 Subject: Disable unnecessary_cast for cfg-dependant types --- clippy_lints/src/types.rs | 10 +++++++++- tests/ui/unnecessary_cast.rs | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f0e10e374e1..840adbbc57a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -8,6 +8,7 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -1632,7 +1633,14 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if expr.span.from_expansion() { return; } - if let ExprKind::Cast(ref ex, _) = expr.kind { + if let ExprKind::Cast(ref ex, cast_to) = expr.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { + if let Res::Def(_, def_id) = path.res { + if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { + return; + } + } + } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); if let Some(lit) = get_numeric_literal(ex) { diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index df9b227eeb3..e8f2fb46665 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -20,4 +20,7 @@ fn main() { foo!(a, i32); foo!(b, f32); foo!(c, f64); + + // do not lint cast to cfg-dependant type + 1 as std::os::raw::c_char; } -- cgit 1.4.1-3-g733a5 From dc075b426675e61cc46089046cf41d85edd79aaa Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 25 Nov 2020 02:01:05 +0100 Subject: Change `redundant_pattern_matching` to also lint `std::net::IpAddr` Suggest using utility methods `is_ipv4` and `is_ipv6`. --- clippy_lints/src/matches.rs | 25 ++++- clippy_lints/src/utils/paths.rs | 2 + tests/ui/redundant_pattern_matching_ipaddr.fixed | 73 ++++++++++++ tests/ui/redundant_pattern_matching_ipaddr.rs | 91 +++++++++++++++ tests/ui/redundant_pattern_matching_ipaddr.stderr | 130 ++++++++++++++++++++++ tests/ui/redundant_pattern_matching_option.fixed | 5 +- tests/ui/redundant_pattern_matching_option.rs | 5 +- tests/ui/redundant_pattern_matching_option.stderr | 18 +-- tests/ui/redundant_pattern_matching_poll.fixed | 5 +- tests/ui/redundant_pattern_matching_poll.rs | 5 +- tests/ui/redundant_pattern_matching_poll.stderr | 18 +-- 11 files changed, 341 insertions(+), 36 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.fixed create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.rs create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e801..a14b63faf78 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -411,8 +411,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or - /// `std::task::Poll` + /// **What it does:** Lint for redundant pattern matching over `Result`, `Option`, + /// `std::task::Poll` or `std::net::IpAddr` /// /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function @@ -423,12 +423,15 @@ declare_clippy_lint! { /// /// ```rust /// # use std::task::Poll; + /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// if let Ok(_) = Ok::(42) {} /// if let Err(_) = Err::(42) {} /// if let None = None::<()> {} /// if let Some(_) = Some(42) {} /// if let Poll::Pending = Poll::Pending::<()> {} /// if let Poll::Ready(_) = Poll::Ready(42) {} + /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {} + /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {} /// match Ok::(42) { /// Ok(_) => true, /// Err(_) => false, @@ -439,12 +442,15 @@ declare_clippy_lint! { /// /// ```rust /// # use std::task::Poll; + /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// if Ok::(42).is_ok() {} /// if Err::(42).is_err() {} /// if None::<()>.is_none() {} /// if Some(42).is_some() {} /// if Poll::Pending::<()>.is_pending() {} /// if Poll::Ready(42).is_ready() {} + /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {} /// Ok::(42).is_ok(); /// ``` pub REDUNDANT_PATTERN_MATCHING, @@ -1546,6 +1552,10 @@ mod redundant_pattern_match { "is_some()" } else if match_qpath(path, &paths::POLL_READY) { "is_ready()" + } else if match_qpath(path, &paths::IPADDR_V4) { + "is_ipv4()" + } else if match_qpath(path, &paths::IPADDR_V6) { + "is_ipv6()" } else { return; } @@ -1626,6 +1636,17 @@ mod redundant_pattern_match { "is_ok()", "is_err()", ) + .or_else(|| { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::IPADDR_V4, + &paths::IPADDR_V6, + "is_ipv4()", + "is_ipv6()", + ) + }) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 829e9a2989c..61aeabb7ba7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -58,6 +58,8 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; +pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; +pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed new file mode 100644 index 00000000000..acc8de5f41e --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::net::{ + IpAddr::{self, V4, V6}, + Ipv4Addr, Ipv6Addr, +}; + +fn main() { + let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); + if ipaddr.is_ipv4() {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) { + println!("{}", ipaddr); + } + + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + + V4(Ipv4Addr::LOCALHOST).is_ipv6(); + + V6(Ipv6Addr::LOCALHOST).is_ipv6(); + + V6(Ipv6Addr::LOCALHOST).is_ipv4(); + + let _ = if V4(Ipv4Addr::LOCALHOST).is_ipv4() { + true + } else { + false + }; + + ipaddr_const(); + + let _ = if gen_ipaddr().is_ipv4() { + 1 + } else if gen_ipaddr().is_ipv6() { + 2 + } else { + 3 + }; +} + +fn gen_ipaddr() -> IpAddr { + V4(Ipv4Addr::LOCALHOST) +} + +const fn ipaddr_const() { + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + + V6(Ipv6Addr::LOCALHOST).is_ipv6(); +} diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs new file mode 100644 index 00000000000..678d91ce93a --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -0,0 +1,91 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::net::{ + IpAddr::{self, V4, V6}, + Ipv4Addr, Ipv6Addr, +}; + +fn main() { + let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); + if let V4(_) = &ipaddr {} + + if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) { + println!("{}", ipaddr); + } + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { + true + } else { + false + }; + + ipaddr_const(); + + let _ = if let V4(_) = gen_ipaddr() { + 1 + } else if let V6(_) = gen_ipaddr() { + 2 + } else { + 3 + }; +} + +fn gen_ipaddr() -> IpAddr { + V4(Ipv4Addr::LOCALHOST) +} + +const fn ipaddr_const() { + if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr new file mode 100644 index 00000000000..caf458cd862 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -0,0 +1,130 @@ +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12 + | +LL | if let V4(_) = &ipaddr {} + | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + | +LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + | +LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20 + | +LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20 + | +LL | let _ = if let V4(_) = gen_ipaddr() { + | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19 + | +LL | } else if let V6(_) = gen_ipaddr() { + | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 + | +LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 + | +LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index bc369dd2491..66f580a0a68 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -37,8 +37,7 @@ fn main() { let _ = None::<()>.is_none(); let opt = Some(false); - let x = if opt.is_some() { true } else { false }; - takes_bool(x); + let _ = if opt.is_some() { true } else { false }; issue6067(); @@ -55,8 +54,6 @@ fn gen_opt() -> Option<()> { None } -fn takes_bool(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index d7616a72913..f18b27b8b95 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -46,8 +46,7 @@ fn main() { }; let opt = Some(false); - let x = if let Some(_) = opt { true } else { false }; - takes_bool(x); + let _ = if let Some(_) = opt { true } else { false }; issue6067(); @@ -64,8 +63,6 @@ fn gen_opt() -> Option<()> { None } -fn takes_bool(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 7ddfbe503a2..58482a0ab70 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -73,47 +73,47 @@ LL | | }; error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:49:20 | -LL | let x = if let Some(_) = opt { true } else { false }; +LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:54:20 + --> $DIR/redundant_pattern_matching_option.rs:53:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:56:19 + --> $DIR/redundant_pattern_matching_option.rs:55:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:77:12 + --> $DIR/redundant_pattern_matching_option.rs:74:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:79:12 + --> $DIR/redundant_pattern_matching_option.rs:76:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:81:15 + --> $DIR/redundant_pattern_matching_option.rs:78:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:83:15 + --> $DIR/redundant_pattern_matching_option.rs:80:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:85:5 + --> $DIR/redundant_pattern_matching_option.rs:82:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:90:5 + --> $DIR/redundant_pattern_matching_option.rs:87:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed index 564c427f063..465aa80dac2 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -34,8 +34,7 @@ fn main() { let _ = Pending::<()>.is_pending(); let poll = Ready(false); - let x = if poll.is_ready() { true } else { false }; - takes_poll(x); + let _ = if poll.is_ready() { true } else { false }; poll_const(); @@ -52,8 +51,6 @@ fn gen_poll() -> Poll<()> { Pending } -fn takes_poll(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index d453d4184af..7891ff353b1 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -43,8 +43,7 @@ fn main() { }; let poll = Ready(false); - let x = if let Ready(_) = poll { true } else { false }; - takes_poll(x); + let _ = if let Ready(_) = poll { true } else { false }; poll_const(); @@ -61,8 +60,6 @@ fn gen_poll() -> Poll<()> { Pending } -fn takes_poll(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 42e5d6f41fe..5ffc6c47c90 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -67,47 +67,47 @@ LL | | }; error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:46:20 | -LL | let x = if let Ready(_) = poll { true } else { false }; +LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try this: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:51:20 + --> $DIR/redundant_pattern_matching_poll.rs:50:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:53:19 + --> $DIR/redundant_pattern_matching_poll.rs:52:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:71:12 + --> $DIR/redundant_pattern_matching_poll.rs:68:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:73:12 + --> $DIR/redundant_pattern_matching_poll.rs:70:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:75:15 + --> $DIR/redundant_pattern_matching_poll.rs:72:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:77:15 + --> $DIR/redundant_pattern_matching_poll.rs:74:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:79:5 + --> $DIR/redundant_pattern_matching_poll.rs:76:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -116,7 +116,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:84:5 + --> $DIR/redundant_pattern_matching_poll.rs:81:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, -- cgit 1.4.1-3-g733a5 From aaa43250455c19ae54c821518c42219f6460038c Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 21 Oct 2020 18:17:34 +0530 Subject: add support for minimum supported rust version. add configuration option for minimum supported rust version add msrv attribute to some lints listed in #6097 add tests --- clippy_lints/src/lib.rs | 23 ++++++-- clippy_lints/src/manual_non_exhaustive.rs | 33 ++++++++++- clippy_lints/src/manual_strip.rs | 37 ++++++++++-- clippy_lints/src/matches.rs | 39 +++++++++++-- clippy_lints/src/methods/mod.rs | 51 +++++++++++++--- clippy_lints/src/utils/attrs.rs | 17 ++++++ clippy_lints/src/utils/conf.rs | 2 + clippy_lints/src/utils/mod.rs | 49 ++++++++++++++++ tests/ui-toml/invalid_min_rust_version/clippy.toml | 1 + .../invalid_min_rust_version.rs | 3 + .../invalid_min_rust_version.stderr | 4 ++ tests/ui-toml/min_rust_version/clippy.toml | 1 + tests/ui-toml/min_rust_version/min_rust_version.rs | 68 ++++++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/min_rust_version_attr.rs | 51 ++++++++++++++++ tests/ui/min_rust_version_invalid_attr.rs | 4 ++ tests/ui/min_rust_version_invalid_attr.stderr | 8 +++ tests/ui/min_rust_version_no_patch.rs | 14 +++++ tests/ui/min_rust_version_outer_attr.rs | 4 ++ tests/ui/min_rust_version_outer_attr.stderr | 8 +++ 20 files changed, 390 insertions(+), 29 deletions(-) create mode 100644 tests/ui-toml/invalid_min_rust_version/clippy.toml create mode 100644 tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs create mode 100644 tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr create mode 100644 tests/ui-toml/min_rust_version/clippy.toml create mode 100644 tests/ui-toml/min_rust_version/min_rust_version.rs create mode 100644 tests/ui/min_rust_version_attr.rs create mode 100644 tests/ui/min_rust_version_invalid_attr.rs create mode 100644 tests/ui/min_rust_version_invalid_attr.stderr create mode 100644 tests/ui/min_rust_version_no_patch.rs create mode 100644 tests/ui/min_rust_version_outer_attr.rs create mode 100644 tests/ui/min_rust_version_outer_attr.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e8cbd00c22..866dae110cc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -44,6 +44,7 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate rustc_typeck; +use crate::utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; @@ -933,7 +934,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -969,7 +969,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box methods::Methods); + + let parsed_msrv = conf.msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); + None + }) + }); + + let msrv = parsed_msrv.clone(); + store.register_late_pass(move || box methods::Methods::new(msrv.clone())); + let msrv = parsed_msrv.clone(); + store.register_late_pass(move || box matches::Matches::new(msrv.clone())); + let msrv = parsed_msrv.clone(); + store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone())); + let msrv = parsed_msrv; + store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone())); + store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -983,7 +999,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); - store.register_late_pass(|| box matches::Matches::default()); store.register_late_pass(|| box minmax::MinMaxPass); store.register_late_pass(|| box open_options::OpenOptions); store.register_late_pass(|| box zero_div_zero::ZeroDiv); @@ -1144,7 +1159,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); - store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); @@ -1166,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index a1450b0d5fe..4762ba16ac7 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,11 +1,20 @@ -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::{get_inner_attr, meets_msrv, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use semver::{Version, VersionReq}; + +const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version { + major: 1, + minor: 40, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -55,10 +64,26 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +#[derive(Clone)] +pub struct ManualNonExhaustive { + msrv: Option, +} + +impl ManualNonExhaustive { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustive { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) { + return; + } + match &item.kind { ItemKind::Enum(def, _) => { check_manual_non_exhaustive_enum(cx, item, &def.variants); @@ -73,6 +98,8 @@ impl EarlyLintPass for ManualNonExhaustive { _ => {}, } } + + extract_msrv_attr!(EarlyContext); } fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 4afb0ab3bad..446641ca114 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,21 +1,31 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, + eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; +use semver::{Version, VersionReq}; + +const MANUAL_STRIP_MSRV: Version = Version { + major: 1, + minor: 45, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; declare_clippy_lint! { /// **What it does:** @@ -51,7 +61,18 @@ declare_clippy_lint! { "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing" } -declare_lint_pass!(ManualStrip => [MANUAL_STRIP]); +pub struct ManualStrip { + msrv: Option, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualStrip => [MANUAL_STRIP]); #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum StripKind { @@ -61,6 +82,10 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + return; + } + if_chain! { if let Some((cond, then, _)) = higher::if_block(&expr); if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; @@ -114,6 +139,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } } } + + extract_msrv_attr!(LateContext); } // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e801..5c38abbd9c6 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet, - snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, + is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, + remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; @@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::{sym, Symbol}; +use semver::{Version, VersionReq}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -527,9 +528,20 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { + msrv: Option, infallible_destructuring_match_linted: bool, } +impl Matches { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + ..Matches::default() + } + } +} + impl_lint_pass!(Matches => [ SINGLE_MATCH, MATCH_REF_PATS, @@ -549,6 +561,14 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); +const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version { + major: 1, + minor: 42, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; + impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { @@ -556,7 +576,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check(cx, expr); - if !check_match_like_matches(cx, expr) { + + if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) { + if !check_match_like_matches(cx, expr) { + lint_match_arms(cx, expr); + } + } else { lint_match_arms(cx, expr); } @@ -640,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } } + + extract_msrv_attr!(LateContext); } #[rustfmt::skip] diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa174404365..6b478986067 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -12,6 +12,7 @@ use std::iter; use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -20,7 +21,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -28,12 +29,14 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, - match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, - single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + walk_ptrs_ty_depth, SpanlessEq, }; +use semver::{Version, VersionReq}; declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. @@ -1404,7 +1407,18 @@ declare_clippy_lint! { "use `.collect()` instead of `::from_iter()`" } -declare_lint_pass!(Methods => [ +pub struct Methods { + msrv: Option, +} + +impl Methods { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, @@ -1531,8 +1545,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { check_pointer_offset(cx, expr, arg_lists[0]) }, ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), - ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), - ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["map", "as_ref"] => { + lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) + }, + ["map", "as_mut"] => { + lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) + }, ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), @@ -1738,6 +1756,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + extract_msrv_attr!(LateContext); } /// Checks for the `OR_FUN_CALL` lint. @@ -3453,6 +3473,14 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { ); } +const OPTION_AS_REF_DEREF_MSRV: Version = Version { + major: 1, + minor: 40, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; + /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s fn lint_option_as_ref_deref<'tcx>( cx: &LateContext<'tcx>, @@ -3460,7 +3488,12 @@ fn lint_option_as_ref_deref<'tcx>( as_ref_args: &[hir::Expr<'_>], map_args: &[hir::Expr<'_>], is_mut: bool, + msrv: Option<&VersionReq>, ) { + if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + return; + } + let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index e6d41341a55..aed6ee5dc57 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -21,6 +21,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ DeprecationStatus::Replaced("cognitive_complexity"), ), ("dump", DeprecationStatus::None), + ("msrv", DeprecationStatus::None), ]; pub struct LimitStack { @@ -123,6 +124,22 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } +pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { + let mut unique_attr = None; + for attr in get_attr(sess, attrs, name) { + match attr.style { + ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), + ast::AttrStyle::Inner => { + sess.span_err(attr.span, &format!("`{}` is defined multiple times", name)); + }, + ast::AttrStyle::Outer => { + sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); + }, + } + } + unique_attr +} + /// Return true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0ac8fff69f0..fc6304118d9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,6 +106,8 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { + /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports + (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e9c71e23a67..f8e88512048 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -51,6 +51,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym as rustc_sym; @@ -58,10 +59,58 @@ use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; +use semver::{Version, VersionReq}; use smallvec::SmallVec; use crate::consts::{constant, Constant}; +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = VersionReq::parse(msrv) { + return Some(version); + } else if let Some(sess) = sess { + if let Some(span) = span { + sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv)); + } + } + None +} + +pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { + msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) +} + +#[macro_export] +macro_rules! extract_msrv_attr { + (LateContext) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::LateContext<'tcx>, attrs: &'tcx [Attribute]) { + match get_inner_attr(cx.sess(), attrs, "msrv") { + Some(msrv_attr) => { + if let Some(msrv) = msrv_attr.value_str() { + self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess()), Some(msrv_attr.span)); + } else { + cx.sess().span_err(msrv_attr.span, "bad clippy attribute"); + } + }, + _ => (), + } + } + }; + (EarlyContext) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::EarlyContext<'tcx>, attrs: &'tcx [Attribute]) { + match get_inner_attr(cx.sess, attrs, "msrv") { + Some(msrv_attr) => { + if let Some(msrv) = msrv_attr.value_str() { + self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess), Some(msrv_attr.span)); + } else { + cx.sess.span_err(msrv_attr.span, "bad clippy attribute"); + } + }, + _ => (), + } + } + }; +} + /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). #[must_use] diff --git a/tests/ui-toml/invalid_min_rust_version/clippy.toml b/tests/ui-toml/invalid_min_rust_version/clippy.toml new file mode 100644 index 00000000000..088b12b2dac --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "invalid.version" diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs new file mode 100644 index 00000000000..2ebf28645e5 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs @@ -0,0 +1,3 @@ +#![allow(clippy::redundant_clone)] + +fn main() {} diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr new file mode 100644 index 00000000000..e9d8fd2e0f5 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version + +error: aborting due to previous error + diff --git a/tests/ui-toml/min_rust_version/clippy.toml b/tests/ui-toml/min_rust_version/clippy.toml new file mode 100644 index 00000000000..8e17d8074c4 --- /dev/null +++ b/tests/ui-toml/min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "1.0.0" diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs new file mode 100644 index 00000000000..bc41efa42a1 --- /dev/null +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -0,0 +1,68 @@ +#![allow(clippy::redundant_clone)] +#![warn(clippy::manual_non_exhaustive)] + +use std::ops::Deref; + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // user forgot to remove the marker + #[non_exhaustive] + enum Ep { + A, + B, + #[doc(hidden)] + _C, + } +} + +fn option_as_ref_deref() { + let mut opt = Some(String::from("123")); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); +} + +fn match_like_matches() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn match_same_arms() { + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; +} + +fn match_same_arms2() { + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; +} + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + option_as_ref_deref(); + match_like_matches(); + match_same_arms(); + match_same_arms2(); + manual_strip_msrv(); +} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index a58e7e918e2..af3d9ecf6e8 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs new file mode 100644 index 00000000000..8ed483a3ac6 --- /dev/null +++ b/tests/ui/min_rust_version_attr.rs @@ -0,0 +1,51 @@ +#![allow(clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.0.0"] + +use std::ops::Deref; + +fn option_as_ref_deref() { + let mut opt = Some(String::from("123")); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); +} + +fn match_like_matches() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn match_same_arms() { + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; +} + +fn match_same_arms2() { + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; +} + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + option_as_ref_deref(); + match_like_matches(); + match_same_arms(); + match_same_arms2(); + manual_strip_msrv(); +} diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs new file mode 100644 index 00000000000..f20841891a7 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] +#![clippy::msrv = "invalid.version"] + +fn main() {} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr new file mode 100644 index 00000000000..6ff88ca56f8 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -0,0 +1,8 @@ +error: `invalid.version` is not a valid Rust version + --> $DIR/min_rust_version_invalid_attr.rs:2:1 + | +LL | #![clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs new file mode 100644 index 00000000000..515fe8f95e9 --- /dev/null +++ b/tests/ui/min_rust_version_no_patch.rs @@ -0,0 +1,14 @@ +#![allow(clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "^1.0"] + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + manual_strip_msrv() +} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs new file mode 100644 index 00000000000..551948bd72e --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] + +#[clippy::msrv = "invalid.version"] +fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr new file mode 100644 index 00000000000..579ee7a87d2 --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.stderr @@ -0,0 +1,8 @@ +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_outer_attr.rs:3:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From b2e2c0806ec2296bf7d98e0db5cc9c39a55f23ac Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 25 Nov 2020 11:50:13 +0100 Subject: Improve extract_msrv_attr! situation --- clippy_lints/src/manual_non_exhaustive.rs | 2 +- clippy_lints/src/manual_strip.rs | 5 ++--- clippy_lints/src/matches.rs | 10 +++++----- clippy_lints/src/methods/mod.rs | 12 +++++------- clippy_lints/src/utils/mod.rs | 30 +++++++++++++----------------- 5 files changed, 26 insertions(+), 33 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 4762ba16ac7..703e6feeca5 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_inner_attr, meets_msrv, snippet_opt, span_lint_and_then}; +use crate::utils::{meets_msrv, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; use rustc_attr as attr; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 446641ca114..e17e3adb94f 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,12 +1,11 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, - span_lint_and_then, + eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, LitKind}; +use rustc_ast::ast::LitKind; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::BinOpKind; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 5c38abbd9c6..d695af4de21 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, - is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, - remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, - span_lint_and_sugg, span_lint_and_then, + expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, + is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, + snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, LitKind}; +use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6b478986067..50dd760432d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -12,7 +12,6 @@ use std::iter; use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; -use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -29,12 +28,11 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher, - implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, - method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, + is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, + match_trait_method, match_type, match_var, meets_msrv, method_calls, method_chain_args, paths, remove_blocks, + return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; use semver::{Version, VersionReq}; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index f8e88512048..6f89e51279a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -79,30 +79,26 @@ pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) } -#[macro_export] macro_rules! extract_msrv_attr { (LateContext) => { - fn enter_lint_attrs(&mut self, cx: &rustc_lint::LateContext<'tcx>, attrs: &'tcx [Attribute]) { - match get_inner_attr(cx.sess(), attrs, "msrv") { - Some(msrv_attr) => { - if let Some(msrv) = msrv_attr.value_str() { - self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess()), Some(msrv_attr.span)); - } else { - cx.sess().span_err(msrv_attr.span, "bad clippy attribute"); - } - }, - _ => (), - } - } + extract_msrv_attr!(@LateContext, ()); }; (EarlyContext) => { - fn enter_lint_attrs(&mut self, cx: &rustc_lint::EarlyContext<'tcx>, attrs: &'tcx [Attribute]) { - match get_inner_attr(cx.sess, attrs, "msrv") { + extract_msrv_attr!(@EarlyContext); + }; + (@$context:ident$(, $call:tt)?) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) { + use $crate::utils::get_unique_inner_attr; + match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") { Some(msrv_attr) => { if let Some(msrv) = msrv_attr.value_str() { - self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess), Some(msrv_attr.span)); + self.msrv = $crate::utils::parse_msrv( + &msrv.to_string(), + Some(cx.sess$($call)?), + Some(msrv_attr.span), + ); } else { - cx.sess.span_err(msrv_attr.span, "bad clippy attribute"); + cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute"); } }, _ => (), -- cgit 1.4.1-3-g733a5 From 93f922a85843b2c04e47ea30c109f0bd305e039e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 25 Nov 2020 12:19:13 +0100 Subject: Add note where the first definition of msrv attr is --- clippy_lints/src/utils/attrs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index aed6ee5dc57..24052a243af 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -130,7 +130,9 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s match attr.style { ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), ast::AttrStyle::Inner => { - sess.span_err(attr.span, &format!("`{}` is defined multiple times", name)); + sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name)) + .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") + .emit(); }, ast::AttrStyle::Outer => { sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); -- cgit 1.4.1-3-g733a5 From 2345ef5b1f89d9cac7a6697de08c36bfb771fe12 Mon Sep 17 00:00:00 2001 From: PunitLodha Date: Sat, 14 Nov 2020 19:21:33 +0530 Subject: added lints str_to_string and string_to_string --- clippy_lints/src/deprecated_lints.rs | 20 ------- clippy_lints/src/lib.rs | 22 +++----- clippy_lints/src/strings.rs | 100 ++++++++++++++++++++++++++++++++++- src/lintlist/mod.rs | 14 +++++ tests/ui/deprecated.rs | 2 - tests/ui/deprecated.stderr | 46 ++++++---------- tests/ui/deprecated_old.rs | 2 - tests/ui/deprecated_old.stderr | 30 ++++------- tests/ui/str_to_string.rs | 7 +++ tests/ui/str_to_string.stderr | 19 +++++++ tests/ui/string_to_string.rs | 7 +++ tests/ui/string_to_string.stderr | 11 ++++ 12 files changed, 189 insertions(+), 91 deletions(-) create mode 100644 tests/ui/str_to_string.rs create mode 100644 tests/ui/str_to_string.stderr create mode 100644 tests/ui/string_to_string.rs create mode 100644 tests/ui/string_to_string.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 1c3285ed701..bec0c9f93a0 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -51,26 +51,6 @@ declare_deprecated_lint! { "`Vec::as_mut_slice` has been stabilized in 1.7" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This used to check for `.to_string()` method calls on values - /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be - /// specialized to be as efficient as `to_owned`. - pub STR_TO_STRING, - "using `str::to_string` is common even today and specialization will likely happen soon" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This used to check for `.to_string()` method calls on values - /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be - /// specialized to be as efficient as `clone`. - pub STRING_TO_STRING, - "using `string::to_string` is common even today and specialization will likely happen soon" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 866dae110cc..67a3a3fcf48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -441,14 +441,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7", ); - store.register_removed( - "clippy::str_to_string", - "using `str::to_string` is common even today and specialization will likely happen soon", - ); - store.register_removed( - "clippy::string_to_string", - "using `string::to_string` is common even today and specialization will likely happen soon", - ); store.register_removed( "clippy::misaligned_transmute", "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", @@ -840,6 +832,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &strings::STRING_ADD_ASSIGN, &strings::STRING_FROM_UTF8_AS_BYTES, &strings::STRING_LIT_AS_BYTES, + &strings::STRING_TO_STRING, + &strings::STR_TO_STRING, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &swap::ALMOST_SWAPPED, @@ -1186,6 +1180,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); + store.register_late_pass(|| box strings::StrToString); + store.register_late_pass(|| box strings::StringToString); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1228,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&strings::STRING_TO_STRING), + LintId::of(&strings::STR_TO_STRING), LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), @@ -1943,14 +1941,6 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { "unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7", ); - store.register_removed( - "str_to_string", - "using `str::to_string` is common even today and specialization will likely happen soon", - ); - store.register_removed( - "string_to_string", - "using `string::to_string` is common even today and specialization will likely happen soon", - ); store.register_removed( "misaligned_transmute", "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ede37624f71..42c45be3b45 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -2,6 +2,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use rustc_span::sym; @@ -11,7 +12,7 @@ use if_chain::if_chain; use crate::utils::SpanlessEq; use crate::utils::{ get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, - span_lint_and_sugg, + span_lint_and_help, span_lint_and_sugg, }; declare_clippy_lint! { @@ -289,3 +290,100 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } } } + +declare_clippy_lint! { + /// **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`. + /// + /// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string. + /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better + /// expressed with `.to_owned()`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let _ = "str".to_string(); + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// let _ = "str".to_owned(); + /// ``` + pub STR_TO_STRING, + restriction, + "using `to_string()` on a `&str`, which should be `to_owned()`" +} + +declare_lint_pass!(StrToString => [STR_TO_STRING]); + +impl LateLintPass<'_> for StrToString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if path.ident.name == sym!(to_string); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, ty, ..) = ty.kind(); + if *ty.kind() == ty::Str; + then { + span_lint_and_help( + cx, + STR_TO_STRING, + expr.span, + "`to_string()` called on a `&str`", + None, + "consider using `.to_owned()`", + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** This lint checks for `.to_string()` method calls on values of type `String`. + /// + /// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string. + /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`. + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let msg = String::from("Hello World"); + /// let _ = msg.to_string(); + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// let msg = String::from("Hello World"); + /// let _ = msg.clone(); + /// ``` + pub STRING_TO_STRING, + restriction, + "using `to_string()` on a `String`, which should be `clone()`" +} + +declare_lint_pass!(StringToString => [STRING_TO_STRING]); + +impl LateLintPass<'_> for StringToString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if path.ident.name == sym!(to_string); + let ty = cx.typeck_results().expr_ty(&args[0]); + if is_type_diagnostic_item(cx, ty, sym!(string_type)); + then { + span_lint_and_help( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + None, + "consider using `.clone()`", + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1d906d20ad4..a104f687bdf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2237,6 +2237,13 @@ vec![ deprecation: None, module: "stable_sort_primitive", }, + Lint { + name: "str_to_string", + group: "restriction", + desc: "using `to_string()` on a `&str`, which should be `to_owned()`", + deprecation: None, + module: "strings", + }, Lint { name: "string_add", group: "restriction", @@ -2272,6 +2279,13 @@ vec![ deprecation: None, module: "strings", }, + Lint { + name: "string_to_string", + group: "restriction", + desc: "using `to_string()` on a `String`, which should be `clone()`", + deprecation: None, + module: "strings", + }, Lint { name: "struct_excessive_bools", group: "pedantic", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 4cbc5630d75..e1ee8dbca2c 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -1,5 +1,3 @@ -#[warn(clippy::str_to_string)] -#[warn(clippy::string_to_string)] #[warn(clippy::unstable_as_slice)] #[warn(clippy::unstable_as_mut_slice)] #[warn(clippy::misaligned_transmute)] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index a348d01d734..edbb891afe0 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,88 +1,76 @@ -error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated.rs:1:8 - | -LL | #[warn(clippy::str_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - -error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated.rs:2:8 - | -LL | #[warn(clippy::string_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` - --> $DIR/deprecated.rs:3:8 + --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` - --> $DIR/deprecated.rs:4:8 + --> $DIR/deprecated.rs:2:8 | LL | #[warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` - --> $DIR/deprecated.rs:5:8 + --> $DIR/deprecated.rs:3:8 | LL | #[warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint` - --> $DIR/deprecated.rs:6:8 + --> $DIR/deprecated.rs:4:8 | LL | #[warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value`` - --> $DIR/deprecated.rs:7:8 + --> $DIR/deprecated.rs:5:8 | LL | #[warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter`` - --> $DIR/deprecated.rs:8:8 + --> $DIR/deprecated.rs:6:8 | LL | #[warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels`` - --> $DIR/deprecated.rs:9:8 + --> $DIR/deprecated.rs:7:8 | LL | #[warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018` - --> $DIR/deprecated.rs:10:8 + --> $DIR/deprecated.rs:8:8 | LL | #[warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted to rustc and is now called `drop_bounds`` - --> $DIR/deprecated.rs:11:8 + --> $DIR/deprecated.rs:9:8 | LL | #[warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`` - --> $DIR/deprecated.rs:12:8 + --> $DIR/deprecated.rs:10:8 | LL | #[warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt`` - --> $DIR/deprecated.rs:13:8 + --> $DIR/deprecated.rs:11:8 | LL | #[warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` +error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` --> $DIR/deprecated.rs:1:8 | -LL | #[warn(clippy::str_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/deprecated_old.rs b/tests/ui/deprecated_old.rs index 2e5c5b7ead1..e89dca4fcfd 100644 --- a/tests/ui/deprecated_old.rs +++ b/tests/ui/deprecated_old.rs @@ -1,5 +1,3 @@ -#[warn(str_to_string)] -#[warn(string_to_string)] #[warn(unstable_as_slice)] #[warn(unstable_as_mut_slice)] #[warn(misaligned_transmute)] diff --git a/tests/ui/deprecated_old.stderr b/tests/ui/deprecated_old.stderr index ff3e9e8fcf3..2fe1facf0c7 100644 --- a/tests/ui/deprecated_old.stderr +++ b/tests/ui/deprecated_old.stderr @@ -1,40 +1,28 @@ -error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated_old.rs:1:8 - | -LL | #[warn(str_to_string)] - | ^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - -error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated_old.rs:2:8 - | -LL | #[warn(string_to_string)] - | ^^^^^^^^^^^^^^^^ - error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` - --> $DIR/deprecated_old.rs:3:8 + --> $DIR/deprecated_old.rs:1:8 | LL | #[warn(unstable_as_slice)] | ^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` - --> $DIR/deprecated_old.rs:4:8 + --> $DIR/deprecated_old.rs:2:8 | LL | #[warn(unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^ error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` - --> $DIR/deprecated_old.rs:5:8 + --> $DIR/deprecated_old.rs:3:8 | LL | #[warn(misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^ -error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` +error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` --> $DIR/deprecated_old.rs:1:8 | -LL | #[warn(str_to_string)] - | ^^^^^^^^^^^^^ +LL | #[warn(unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/str_to_string.rs b/tests/ui/str_to_string.rs new file mode 100644 index 00000000000..08f73402518 --- /dev/null +++ b/tests/ui/str_to_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::str_to_string)] + +fn main() { + let hello = "hello world".to_string(); + let msg = &hello[..]; + msg.to_string(); +} diff --git a/tests/ui/str_to_string.stderr b/tests/ui/str_to_string.stderr new file mode 100644 index 00000000000..b1f73eda5d2 --- /dev/null +++ b/tests/ui/str_to_string.stderr @@ -0,0 +1,19 @@ +error: `to_string()` called on a `&str` + --> $DIR/str_to_string.rs:4:17 + | +LL | let hello = "hello world".to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::str-to-string` implied by `-D warnings` + = help: consider using `.to_owned()` + +error: `to_string()` called on a `&str` + --> $DIR/str_to_string.rs:6:5 + | +LL | msg.to_string(); + | ^^^^^^^^^^^^^^^ + | + = help: consider using `.to_owned()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs new file mode 100644 index 00000000000..4c66855f709 --- /dev/null +++ b/tests/ui/string_to_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::string_to_string)] +#![allow(clippy::redundant_clone)] + +fn main() { + let mut message = String::from("Hello"); + let mut v = message.to_string(); +} diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr new file mode 100644 index 00000000000..1ebd17999bd --- /dev/null +++ b/tests/ui/string_to_string.stderr @@ -0,0 +1,11 @@ +error: `to_string()` called on a `String` + --> $DIR/string_to_string.rs:6:17 + | +LL | let mut v = message.to_string(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::string-to-string` implied by `-D warnings` + = help: consider using `.clone()` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 3b53de6b36081646315c0721638c4318c28b6982 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 21 Nov 2020 21:08:32 -0300 Subject: Fix rust-lang/rust#79255 - Incorrect try suggestion for unnecessary float literal cast ending in dot --- clippy_lints/src/types.rs | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 2 ++ tests/ui/unnecessary_cast_fixable.rs | 2 ++ tests/ui/unnecessary_cast_fixable.stderr | 32 ++++++++++++++++++++++---------- 4 files changed, 27 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f0e10e374e1..f3cad66cbf3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1711,7 +1711,7 @@ fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &st expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", literal_str, cast_to), + format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 350da4965d1..7fbce58a82f 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -11,6 +11,8 @@ fn main() { let _ = -100_f32; let _ = -100_f64; let _ = -100_f64; + 100_f32; + 100_f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index ad2fb2e6289..a71363ea4d2 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -11,6 +11,8 @@ fn main() { let _ = -100 as f32; let _ = -100 as f64; let _ = -100_i32 as f64; + 100. as f32; + 100. as f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 5a210fc8909..3695a8f819c 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -36,59 +36,71 @@ error: casting integer literal to `f64` is unnecessary LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:14:5 + | +LL | 100. as f32; + | ^^^^^^^^^^^ help: try: `100_f32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:15:5 + | +LL | 100. as f64; + | ^^^^^^^^^^^ help: try: `100_f64` + error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:25:5 + --> $DIR/unnecessary_cast_fixable.rs:27:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:30:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:31:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:31:5 + --> $DIR/unnecessary_cast_fixable.rs:33:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:32:5 + --> $DIR/unnecessary_cast_fixable.rs:34:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:36:13 + --> $DIR/unnecessary_cast_fixable.rs:38:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:37:13 + --> $DIR/unnecessary_cast_fixable.rs:39:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From cb6a654b75ca095aee9ebbe5cf05fc48df5b9b50 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Thu, 26 Nov 2020 20:07:50 +0000 Subject: Added known problem to comparison_chain docs --- clippy_lints/src/comparison_chain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 99f161a0510..ae1143b2c50 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -12,7 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get /// repetitive /// - /// **Known problems:** None. + /// **Known problems:** The match statement may be slower due to the compiler + /// not inlining the call to cmp. See issue #5354 /// /// **Example:** /// ```rust,ignore -- cgit 1.4.1-3-g733a5 From bd235707acd095fdd2b079d2992923d0d732a474 Mon Sep 17 00:00:00 2001 From: "kai.giebeler" Date: Fri, 27 Nov 2020 01:42:37 +0100 Subject: add WebGL to doc_valid_idents --- clippy_lints/src/utils/conf.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index fc6304118d9..95f5d33fb23 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -127,6 +127,7 @@ define_Conf! { "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", + "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", -- cgit 1.4.1-3-g733a5 From e91d15f42d761c1a2f161a2b65deee06a7f472ab Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 27 Nov 2020 10:32:44 +0900 Subject: cargo dev fmt --- clippy_lints/src/attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 15505fd79f4..3edbe723922 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -5,7 +5,6 @@ use crate::utils::{ span_lint_and_sugg, span_lint_and_then, without_block_comments, }; use if_chain::if_chain; -use rustc_span::lev_distance::find_best_match_for_name; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ @@ -15,6 +14,7 @@ use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext, use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::{Symbol, SymbolStr}; -- cgit 1.4.1-3-g733a5 From 82a7068007a1490b43a2eb4e70e0f70de384a9ae Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Sat, 21 Nov 2020 12:28:53 +0100 Subject: Trigger modulo_one lint also on -1. --- clippy_lints/src/misc.rs | 30 ++++++++++++++++++-------- tests/ui/modulo_one.rs | 11 +++++++++- tests/ui/modulo_one.stderr | 54 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 308e92057b7..f16feb9b1ba 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, SpanlessEq, + span_lint_and_then, span_lint_hir_and_then, SpanlessEq, unsext, }; declare_clippy_lint! { @@ -139,12 +139,14 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for getting the remainder of a division by one. + /// **What it does:** Checks for getting the remainder of a division by one or minus + /// one. /// - /// **Why is this bad?** The result can only ever be zero. No one will write - /// such code deliberately, unless trying to win an Underhanded Rust - /// Contest. Even for that contest, it's probably a bad idea. Use something more - /// underhanded. + /// **Why is this bad?** The result for a divisor of one can only ever be zero; for + /// minus one it can cause panic/overflow (if the left operand is the minimal value of + /// the respective integer type) or results in zero. No one will write such code + /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that + /// contest, it's probably a bad idea. Use something more underhanded. /// /// **Known problems:** None. /// @@ -152,10 +154,11 @@ declare_clippy_lint! { /// ```rust /// # let x = 1; /// let a = x % 1; + /// let a = x % -1; /// ``` pub MODULO_ONE, correctness, - "taking a number modulo 1, which always returns 0" + "taking a number modulo +/-1, which can either panic/overflow or always returns 0" } declare_clippy_lint! { @@ -429,8 +432,17 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); - } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { - span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } else if op == BinOpKind::Rem { + if is_integer_const(cx, right, 1) { + span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } + + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { + if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { + span_lint(cx, MODULO_ONE, expr.span, + "any number modulo -1 will panic/overflow or result in 0"); + } + }; } }, _ => {}, diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index cc8c8e7cdae..678a312f66e 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -2,13 +2,22 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation)] static STATIC_ONE: usize = 2 - 1; +static STATIC_NEG_ONE: i64 = 1 - 2; fn main() { 10 % 1; + 10 % -1; 10 % 2; + i32::MIN % (-1); // also caught by rustc const ONE: u32 = 1 * 1; + const NEG_ONE: i64 = 1 - 2; + const INT_MIN: i64 = i64::MIN; 2 % ONE; - 5 % STATIC_ONE; + 5 % STATIC_ONE; // NOT caught by lint + 2 % NEG_ONE; + 5 % STATIC_NEG_ONE; // NOT caught by lint + INT_MIN % NEG_ONE; // also caught by rustc + INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc } diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 6bee68360b6..2b2c6997338 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -1,13 +1,45 @@ +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:11:5 + | +LL | i32::MIN % (-1); // also caught by rustc + | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow + | + = note: `#[deny(arithmetic_overflow)]` on by default + +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:21:5 + | +LL | INT_MIN % NEG_ONE; // also caught by rustc + | ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow + +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:22:5 + | +LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc + | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow + error: any number modulo 1 will be 0 - --> $DIR/modulo_one.rs:7:5 + --> $DIR/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ | = note: `-D clippy::modulo-one` implied by `-D warnings` +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:9:5 + | +LL | 10 % -1; + | ^^^^^^^ + +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:11:5 + | +LL | i32::MIN % (-1); // also caught by rustc + | ^^^^^^^^^^^^^^^ + error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:10:22 + --> $DIR/modulo_one.rs:13:22 | LL | const ONE: u32 = 1 * 1; | ^^^^^ @@ -15,16 +47,28 @@ LL | const ONE: u32 = 1 * 1; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:10:22 + --> $DIR/modulo_one.rs:13:22 | LL | const ONE: u32 = 1 * 1; | ^^^^^ error: any number modulo 1 will be 0 - --> $DIR/modulo_one.rs:12:5 + --> $DIR/modulo_one.rs:17:5 | LL | 2 % ONE; | ^^^^^^^ -error: aborting due to 4 previous errors +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:19:5 + | +LL | 2 % NEG_ONE; + | ^^^^^^^^^^^ + +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:21:5 + | +LL | INT_MIN % NEG_ONE; // also caught by rustc + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From e42a18f02a45ffc12016a71bb4a210aa62af9a24 Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Sat, 21 Nov 2020 12:38:21 +0100 Subject: Run `cargo dev fmt`. --- clippy_lints/src/misc.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index f16feb9b1ba..b527b2cc1cb 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, SpanlessEq, unsext, + span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -439,8 +439,12 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint(cx, MODULO_ONE, expr.span, - "any number modulo -1 will panic/overflow or result in 0"); + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); } }; } -- cgit 1.4.1-3-g733a5 From b8226320735bc8e3f699cc177be638433ed396d9 Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Mon, 23 Nov 2020 10:18:27 +0100 Subject: Factor out `check_binary` from function `check_expr`. --- clippy_lints/src/misc.rs | 140 ++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 67 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b527b2cc1cb..0512d74c7b1 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -381,73 +381,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { return; }, ExprKind::Binary(ref cmp, ref left, ref right) => { - let op = cmp.node; - if op.is_comparison() { - check_nan(cx, left, expr); - check_nan(cx, right, expr); - check_to_owned(cx, left, right, true); - check_to_owned(cx, right, left, false); - } - if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { - if is_allowed(cx, left) || is_allowed(cx, right) { - return; - } - - // Allow comparing the results of signum() - if is_signum(cx, left) && is_signum(cx, right) { - return; - } - - if let Some(name) = get_item_name(cx, expr) { - let name = name.as_str(); - if name == "eq" - || name == "ne" - || name == "is_nan" - || name.starts_with("eq_") - || name.ends_with("_eq") - { - return; - } - } - let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); - let (lint, msg) = get_lint_and_message( - is_named_constant(cx, left) || is_named_constant(cx, right), - is_comparing_arrays, - ); - span_lint_and_then(cx, lint, expr.span, msg, |diag| { - let lhs = Sugg::hir(cx, left, ".."); - let rhs = Sugg::hir(cx, right, ".."); - - if !is_comparing_arrays { - diag.span_suggestion( - expr.span, - "consider comparing them within some margin of error", - format!( - "({}).abs() {} error_margin", - lhs - rhs, - if op == BinOpKind::Eq { '<' } else { '>' } - ), - Applicability::HasPlaceholders, // snippet - ); - } - diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); - }); - } else if op == BinOpKind::Rem { - if is_integer_const(cx, right, 1) { - span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); - } - - if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { - if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint( - cx, - MODULO_ONE, - expr.span, - "any number modulo -1 will panic/overflow or result in 0", - ); - } - }; - } + check_binary(cx, expr, cmp, left, right); + return; }, _ => {}, } @@ -760,3 +695,74 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) } } } + +fn check_binary( + cx: &LateContext<'a>, + expr: &Expr<'_>, + cmp: &rustc_span::source_map::Spanned, + left: &'a Expr<'_>, + right: &'a Expr<'_>, +) { + let op = cmp.node; + if op.is_comparison() { + check_nan(cx, left, expr); + check_nan(cx, right, expr); + check_to_owned(cx, left, right, true); + check_to_owned(cx, right, left, false); + } + if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { + if is_allowed(cx, left) || is_allowed(cx, right) { + return; + } + + // Allow comparing the results of signum() + if is_signum(cx, left) && is_signum(cx, right) { + return; + } + + if let Some(name) = get_item_name(cx, expr) { + let name = name.as_str(); + if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { + return; + } + } + let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); + let (lint, msg) = get_lint_and_message( + is_named_constant(cx, left) || is_named_constant(cx, right), + is_comparing_arrays, + ); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let lhs = Sugg::hir(cx, left, ".."); + let rhs = Sugg::hir(cx, right, ".."); + + if !is_comparing_arrays { + diag.span_suggestion( + expr.span, + "consider comparing them within some margin of error", + format!( + "({}).abs() {} error_margin", + lhs - rhs, + if op == BinOpKind::Eq { '<' } else { '>' } + ), + Applicability::HasPlaceholders, // snippet + ); + } + diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); + }); + } else if op == BinOpKind::Rem { + if is_integer_const(cx, right, 1) { + span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } + + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { + if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); + } + }; + } +} -- cgit 1.4.1-3-g733a5 From af1cc5c91131e0ec30f0f34691a5e635350295a1 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sat, 7 Nov 2020 16:00:42 -0700 Subject: add suspicious_operation_groupings lint run `cargo dev new_lint --category correctness --name suspicious_chained_operators --pass early` add (currently failing) tests for suspicious_chained_operators add some tests to answer a question that came up during implementation write usage code for functions we'll need to find or create Complete left-right tracking TODO get it compiling with several `todo!` invocations. refactor to a set of incomplete functions that don't expect to be able to edit a `Span` create placeholder for `suggestion_with_swapped_ident` function and correct some comments add `inside_larger_boolean_expression` test fill out `get_ident` and `suggestion_with_swapped_ident` Implementi the `IdentIter` start on implementing the `IdentIter` handle the `ExprKind::Path` case in `IdentIter` on second thought, make the iterator type dynamic so we don't need an explicit type for each one we will need handle `ExprKind::MacCall` in `IdentIter` Try handling `box x` expressions restructure `IdentIter` set `self.done` when returning `None` Handle `ExprKind::Array` reduce duplication with a macro that we expect to use several more times handle ExprKind::Call add `new_p` convenience method handle `MethodCall` handle `Tup` and `Binary` handle `Unary` simplify by not returning an additional `Expr` from the `IdentIter` add cross product test against false positives rename suspicious_chained_operators to suspicious_operation_groupings within files For the record, the exact commands run were: find . -type f -name "*.md" -exec sed -i 's/suspicious_chained_operators/suspicious_operation_groupings/g' {} + find . -type f -name "*.rs" -exec sed -i 's/suspicious_chained_operators/suspicious_operation_groupings/g' {} + find . -type f -name "*.rs" -exec sed -i 's/SUSPICIOUS_CHAINED_OPERATORS/SUSPICIOUS_OPERATION_GROUPINGS/g' {} + find . -type f -name "*.rs" -exec sed -i 's/SuspiciousChainedOperators/SuspiciousOperationGroupings/g' {} + Also: rename file to match module name rename test file to match lint name start implementing `IdentDifference` creation add `IdentIter` utility use `ident_iter::IdentIter` fix bug in `suggestion_with_swapped_ident` add `inside_if_statements` test implement `Add` `todo`s register `SuspiciousOperationGroupings` lint pass fill in `chained_binops`, and fill in a stopgap version of `ident_difference_expr`, but then notice that the lint does not seem to ever be run in the tests run `cargo dev update_lints` and not that the `suspicious_operation_groupings` lint still does not seem to be run fix base index incrementing bug fix paired_identifiers bug, and remove ident from `Single` change help prefix and note our first successful lint messages! add odd_number_of_pairs test get the `non_boolean_operators` test passing, with two copies of the error message extract `is_useless_with_eq_exprs` so we can know when `eq_op` will already handle something add `not_caught_by_eq_op` tests since `s1.b * s1.b` was (reasonably) not caught by `eq_op` cover the case where the change should be made on either side of the expression with `not_caught_by_eq_op` tests produce the expected suggestion on the `not_caught_by_eq_op_middle_change_left` test confirm that the previous tests still pass and update references fix early continue bug and get `not_caught_by_eq_op_middle_change_right` passing note that `not_caught_by_eq_op_start` already passes fix bugs based on misunderstanding of what `Iterator::skip` does, and note that `not_caught_by_eq_op_end` now passes add several parens tests and make some of them pass handle parens inside `chained_binops_helper` and note that this makes several tests pass get `inside_larger_boolean_expression_with_unsorted_ops` test passing by extracting out `check_same_op_binops` function also run `cargo dev fmt` note that `inside_function_call` already passes add another `if_statement` test remove the matching op requirement, making `inside_larger_boolean_expression_with_unsorted_ops` pass prevent non-change suggestions from being emitted get the `Nested` tests passing, and remove apparently false note about eq_op add a test to justify comment in `ident_difference_expr_with_base_location` but find that the failure mode seems different than expected complete `todo` making `do_not_give_bad_suggestions_for_this_unusual_expr` pass and add some more tests that already pass add test to `eq_op` note that `inside_fn_with_similar_expression` already passes fix `inside_an_if_statement` and note that it already passes attempt to implement if statement extraction and notice that we don't seem to handle unary ops correctly add `maximum_unary_minus_right_tree` test and make it pass add two tests and note one of them passes filter out unary operations in several places, and find that the issue seems to be that we don't currently recognize the error in `multiple_comparison_types_and_unary_minus` even so. remove filtering that was causing bad suggestions remove tests that were deemed too much for now run `cargo dev fmt` correct eq_op post-merge fill out the description and delete debugging code run `cargo dev update_lints` update eq_op references add parens to work around rustfmt issue #3666 and run rustfmt https://github.com/rust-lang/rustfmt/issues/3666#issuecomment-714612257 update references after formatting fix dogfood issues fix multi-cursor edit fix missed dogfood error fix more dogfood pedantic issues, including function length even more nesting insert hidden definition of Vec3 so docs compile add spaces to second struct def reword test description comment Co-authored-by: llogiq add local `use BinOpKind::*;` Apply suggestions from code review Co-authored-by: llogiq switch `SUSPICIOUS_OPERATION_GROUPINGS` to a style lint run `cargo dev update_lints` put both usages of `op_types` in the same closure to satisfy `borrowck` fix compile error --- CHANGELOG.md | 1 + clippy_lints/src/eq_op.rs | 27 +- clippy_lints/src/lib.rs | 5 + clippy_lints/src/suspicious_operation_groupings.rs | 693 +++++++++++++++++++++ clippy_lints/src/utils/ast_utils.rs | 11 + clippy_lints/src/utils/ast_utils/ident_iter.rs | 45 ++ tests/ui/eq_op.rs | 9 + tests/ui/eq_op.stderr | 10 +- tests/ui/suspicious_operation_groupings.rs | 207 ++++++ tests/ui/suspicious_operation_groupings.stderr | 166 +++++ 10 files changed, 1150 insertions(+), 24 deletions(-) create mode 100644 clippy_lints/src/suspicious_operation_groupings.rs create mode 100644 clippy_lints/src/utils/ast_utils/ident_iter.rs create mode 100644 tests/ui/suspicious_operation_groupings.rs create mode 100644 tests/ui/suspicious_operation_groupings.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b9e4b0e6704..e76a781f13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2073,6 +2073,7 @@ Released 2018-09-13 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 3201adbf9a0..6308f6e2e7e 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,10 +1,10 @@ use crate::utils::{ - eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, - span_lint_and_then, + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, + multispan_sugg, snippet, span_lint, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && eq_expr_value(cx, left, right) { + if is_useless_with_eq_exprs(higher::binop(op.node)) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, @@ -245,22 +245,3 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } - -fn is_valid_operator(op: BinOp) -> bool { - matches!( - op.node, - BinOpKind::Sub - | BinOpKind::Div - | BinOpKind::Eq - | BinOpKind::Lt - | BinOpKind::Le - | BinOpKind::Gt - | BinOpKind::Ge - | BinOpKind::Ne - | BinOpKind::And - | BinOpKind::Or - | BinOpKind::BitXor - | BinOpKind::BitAnd - | BinOpKind::BitOr - ) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 67a3a3fcf48..6eb5f6a7f48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -308,6 +308,7 @@ mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; mod strings; +mod suspicious_operation_groupings; mod suspicious_trait_impl; mod swap; mod tabs_in_doc_comments; @@ -834,6 +835,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &strings::STRING_LIT_AS_BYTES, &strings::STRING_TO_STRING, &strings::STR_TO_STRING, + &suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &swap::ALMOST_SWAPPED, @@ -1066,6 +1068,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box types::UnitArg); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); + store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); store.register_late_pass(|| box map_unit_fn::MapUnit); store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); @@ -1547,6 +1550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1698,6 +1702,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(&try_err::TRY_ERR), diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs new file mode 100644 index 00000000000..cccd24ccf94 --- /dev/null +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -0,0 +1,693 @@ +use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use core::ops::{Add, AddAssign}; +use if_chain::if_chain; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for unlikely usages of binary operators that are almost + /// certainly typos and/or copy/paste errors, given the other usages + /// of binary operators nearby. + /// **Why is this bad?** + /// They are probably bugs and if they aren't then they look like bugs + /// and you should add a comment explaining why you are doing such an + /// odd set of operations. + /// **Known problems:** + /// There may be some false positives if you are trying to do something + /// unusual that happens to look like a typo. + /// + /// **Example:** + /// + /// ```rust + /// struct Vec3 { + /// x: f64, + /// y: f64, + /// z: f64, + /// } + /// + /// impl Eq for Vec3 {} + /// + /// impl PartialEq for Vec3 { + /// fn eq(&self, other: &Self) -> bool { + /// // This should trigger the lint because `self.x` is compared to `other.y` + /// self.x == other.y && self.y == other.y && self.z == other.z + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// # struct Vec3 { + /// # x: f64, + /// # y: f64, + /// # z: f64, + /// # } + /// // same as above except: + /// impl PartialEq for Vec3 { + /// fn eq(&self, other: &Self) -> bool { + /// // Note we now compare other.x to self.x + /// self.x == other.x && self.y == other.y && self.z == other.z + /// } + /// } + /// ``` + pub SUSPICIOUS_OPERATION_GROUPINGS, + style, + "groupings of binary operations that look suspiciously like typos" +} + +declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]); + +impl EarlyLintPass for SuspiciousOperationGroupings { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + + if let Some(binops) = extract_related_binops(&expr.kind) { + check_binops(cx, &binops.iter().collect::>()); + + let mut op_types = Vec::with_capacity(binops.len()); + // We could use a hashmap, etc. to avoid being O(n*m) here, but + // we want the lints to be emitted in a consistent order. Besides, + // m, (the number of distinct `BinOpKind`s in `binops`) + // will often be small, and does have an upper limit. + binops.iter().map(|b| b.op).for_each(|op| { + if !op_types.contains(&op) { + op_types.push(op); + } + }); + + for op_type in op_types { + let ops: Vec<_> = binops.iter().filter(|b| b.op == op_type).collect(); + + check_binops(cx, &ops); + } + } + } +} + +fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) { + let binop_count = binops.len(); + if binop_count < 2 { + // Single binary operation expressions would likely be false + // positives. + return; + } + + let mut one_ident_difference_count = 0; + let mut no_difference_info = None; + let mut double_difference_info = None; + let mut expected_ident_loc = None; + + let mut paired_identifiers = FxHashSet::default(); + + for (i, BinaryOp { left, right, op, .. }) in binops.iter().enumerate() { + match ident_difference_expr(left, right) { + IdentDifference::NoDifference => { + if is_useless_with_eq_exprs(*op) { + // The `eq_op` lint should catch this in this case. + return; + } + + no_difference_info = Some(i); + }, + IdentDifference::Single(ident_loc) => { + one_ident_difference_count += 1; + if let Some(previous_expected) = expected_ident_loc { + if previous_expected != ident_loc { + // This expression doesn't match the form we're + // looking for. + return; + } + } else { + expected_ident_loc = Some(ident_loc); + } + + // If there was only a single difference, all other idents + // must have been the same, and thus were paired. + for id in skip_index(IdentIter::from(*left), ident_loc.index) { + paired_identifiers.insert(id); + } + }, + IdentDifference::Double(ident_loc1, ident_loc2) => { + double_difference_info = Some((i, ident_loc1, ident_loc2)); + }, + IdentDifference::Multiple | IdentDifference::NonIdent => { + // It's too hard to know whether this is a bug or not. + return; + }, + } + } + + let mut applicability = Applicability::MachineApplicable; + + if let Some(expected_loc) = expected_ident_loc { + match (no_difference_info, double_difference_info) { + (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc), + (None, Some((double_difference_index, ident_loc1, ident_loc2))) => { + if_chain! { + if one_ident_difference_count == binop_count - 1; + if let Some(binop) = binops.get(double_difference_index); + then { + let changed_loc = if ident_loc1 == expected_loc { + ident_loc2 + } else if ident_loc2 == expected_loc { + ident_loc1 + } else { + // This expression doesn't match the form we're + // looking for. + return; + }; + + if let Some(sugg) = ident_swap_sugg( + cx, + &paired_identifiers, + binop, + changed_loc, + &mut applicability, + ) { + emit_suggestion( + cx, + binop.span, + sugg, + applicability, + ); + } + } + } + }, + _ => {}, + } + } +} + +fn attempt_to_emit_no_difference_lint( + cx: &EarlyContext<'_>, + binops: &[&BinaryOp<'_>], + i: usize, + expected_loc: IdentLocation, +) { + if let Some(binop) = binops.get(i).cloned() { + // We need to try and figure out which identifier we should + // suggest using instead. Since there could be multiple + // replacement candidates in a given expression, and we're + // just taking the first one, we may get some bad lint + // messages. + let mut applicability = Applicability::MaybeIncorrect; + + // We assume that the correct ident is one used elsewhere in + // the other binops, in a place that there was a single + // difference between idents before. + let old_left_ident = get_ident(binop.left, expected_loc); + let old_right_ident = get_ident(binop.right, expected_loc); + + for b in skip_index(binops.iter(), i) { + if_chain! { + if let (Some(old_ident), Some(new_ident)) = + (old_left_ident, get_ident(b.left, expected_loc)); + if old_ident != new_ident; + if let Some(sugg) = suggestion_with_swapped_ident( + cx, + binop.left, + expected_loc, + new_ident, + &mut applicability, + ); + then { + emit_suggestion( + cx, + binop.span, + replace_left_sugg(cx, &binop, &sugg, &mut applicability), + applicability, + ); + return; + } + } + + if_chain! { + if let (Some(old_ident), Some(new_ident)) = + (old_right_ident, get_ident(b.right, expected_loc)); + if old_ident != new_ident; + if let Some(sugg) = suggestion_with_swapped_ident( + cx, + binop.right, + expected_loc, + new_ident, + &mut applicability, + ); + then { + emit_suggestion( + cx, + binop.span, + replace_right_sugg(cx, &binop, &sugg, &mut applicability), + applicability, + ); + return; + } + } + } + } +} + +fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicability: Applicability) { + span_lint_and_sugg( + cx, + SUSPICIOUS_OPERATION_GROUPINGS, + span, + "This sequence of operators looks suspiciously like a bug.", + "I think you meant", + sugg, + applicability, + ) +} + +fn ident_swap_sugg( + cx: &EarlyContext<'_>, + paired_identifiers: &FxHashSet, + binop: &BinaryOp<'_>, + location: IdentLocation, + applicability: &mut Applicability, +) -> Option { + let left_ident = get_ident(&binop.left, location)?; + let right_ident = get_ident(&binop.right, location)?; + + let sugg = match ( + paired_identifiers.contains(&left_ident), + paired_identifiers.contains(&right_ident), + ) { + (true, true) | (false, false) => { + // We don't have a good guess of what ident should be + // used instead, in these cases. + *applicability = Applicability::MaybeIncorrect; + + // We arbitraily choose one side to suggest changing, + // since we don't have a better guess. If the user + // ends up duplicating a clause, the `logic_bug` lint + // should catch it. + + let right_suggestion = + suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + + replace_right_sugg(cx, binop, &right_suggestion, applicability) + }, + (false, true) => { + // We haven't seen a pair involving the left one, so + // it's probably what is wanted. + + let right_suggestion = + suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + + replace_right_sugg(cx, binop, &right_suggestion, applicability) + }, + (true, false) => { + // We haven't seen a pair involving the right one, so + // it's probably what is wanted. + let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?; + + replace_left_sugg(cx, binop, &left_suggestion, applicability) + }, + }; + + Some(sugg) +} + +fn replace_left_sugg( + cx: &EarlyContext<'_>, + binop: &BinaryOp<'_>, + left_suggestion: &str, + applicability: &mut Applicability, +) -> String { + format!( + "{} {} {}", + left_suggestion, + binop.op.to_string(), + snippet_with_applicability(cx, binop.right.span, "..", applicability), + ) +} + +fn replace_right_sugg( + cx: &EarlyContext<'_>, + binop: &BinaryOp<'_>, + right_suggestion: &str, + applicability: &mut Applicability, +) -> String { + format!( + "{} {} {}", + snippet_with_applicability(cx, binop.left.span, "..", applicability), + binop.op.to_string(), + right_suggestion, + ) +} + +#[derive(Clone, Debug)] +struct BinaryOp<'exprs> { + op: BinOpKind, + span: Span, + left: &'exprs Expr, + right: &'exprs Expr, +} + +impl BinaryOp<'exprs> { + fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { + Self { op, span, left, right } + } +} + +fn strip_non_ident_wrappers(expr: &Expr) -> &Expr { + let mut output = expr; + loop { + output = match &output.kind { + ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner, + _ => { + return output; + }, + }; + } +} + +fn extract_related_binops(kind: &ExprKind) -> Option>> { + append_opt_vecs(chained_binops(kind), if_statment_binops(kind)) +} + +fn if_statment_binops(kind: &ExprKind) -> Option>> { + match kind { + ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind), + ExprKind::Paren(ref e) => if_statment_binops(&e.kind), + ExprKind::Block(ref block, _) => { + let mut output = None; + for stmt in &block.stmts { + match stmt.kind { + StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => { + output = append_opt_vecs(output, if_statment_binops(&e.kind)); + }, + _ => {}, + } + } + output + }, + _ => None, + } +} + +fn append_opt_vecs(target_opt: Option>, source_opt: Option>) -> Option> { + match (target_opt, source_opt) { + (Some(mut target), Some(mut source)) => { + target.reserve(source.len()); + for op in source.drain(..) { + target.push(op); + } + Some(target) + }, + (Some(v), None) | (None, Some(v)) => Some(v), + (None, None) => None, + } +} + +fn chained_binops(kind: &ExprKind) -> Option>> { + match kind { + ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer), + ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind), + _ => None, + } +} + +fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option>> { + match (&left_outer.kind, &right_outer.kind) { + ( + ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), + ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e), + ) => chained_binops_helper(left_e, right_e), + (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer), + (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => { + chained_binops_helper(left_outer, right_e) + }, + ( + ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right), + ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right), + ) => match ( + chained_binops_helper(left_left, left_right), + chained_binops_helper(right_left, right_right), + ) { + (Some(mut left_ops), Some(mut right_ops)) => { + left_ops.reserve(right_ops.len()); + for op in right_ops.drain(..) { + left_ops.push(op); + } + Some(left_ops) + }, + (Some(mut left_ops), _) => { + left_ops.push(BinaryOp::new(*right_op, right_outer.span, (right_left, right_right))); + Some(left_ops) + }, + (_, Some(mut right_ops)) => { + right_ops.insert(0, BinaryOp::new(*left_op, left_outer.span, (left_left, left_right))); + Some(right_ops) + }, + (None, None) => Some(vec![ + BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)), + BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)), + ]), + }, + _ => None, + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +struct IdentLocation { + index: usize, +} + +impl Add for IdentLocation { + type Output = IdentLocation; + + fn add(self, other: Self) -> Self::Output { + Self { + index: self.index + other.index, + } + } +} + +impl AddAssign for IdentLocation { + fn add_assign(&mut self, other: Self) { + *self = *self + other + } +} + +#[derive(Clone, Copy, Debug)] +enum IdentDifference { + NoDifference, + Single(IdentLocation), + Double(IdentLocation, IdentLocation), + Multiple, + NonIdent, +} + +impl Add for IdentDifference { + type Output = IdentDifference; + + fn add(self, other: Self) -> Self::Output { + match (self, other) { + (Self::NoDifference, output) | (output, Self::NoDifference) => output, + (Self::Multiple, _) + | (_, Self::Multiple) + | (Self::Double(_, _), Self::Single(_)) + | (Self::Single(_) | Self::Double(_, _), Self::Double(_, _)) => Self::Multiple, + (Self::NonIdent, _) | (_, Self::NonIdent) => Self::NonIdent, + (Self::Single(il1), Self::Single(il2)) => Self::Double(il1, il2), + } + } +} + +impl AddAssign for IdentDifference { + fn add_assign(&mut self, other: Self) { + *self = *self + other + } +} + +impl IdentDifference { + /// Returns true if learning about more differences will not change the value + /// of this `IdentDifference`, and false otherwise. + fn is_complete(&self) -> bool { + match self { + Self::NoDifference | Self::Single(_) | Self::Double(_, _) => false, + Self::Multiple | Self::NonIdent => true, + } + } +} + +fn ident_difference_expr(left: &Expr, right: &Expr) -> IdentDifference { + ident_difference_expr_with_base_location(left, right, IdentLocation::default()).0 +} + +fn ident_difference_expr_with_base_location( + left: &Expr, + right: &Expr, + mut base: IdentLocation, +) -> (IdentDifference, IdentLocation) { + // Ideally, this function should not use IdentIter because it should return + // early if the expressions have any non-ident differences. We want that early + // return because if without that restriction the lint would lead to false + // positives. + // + // But, we cannot (easily?) use a `rustc_ast::visit::Visitor`, since we need + // the two expressions to be walked in lockstep. And without a `Visitor`, we'd + // have to do all the AST traversal ourselves, which is a lot of work, since to + // do it properly we'd need to be able to handle more or less every possible + // AST node since `Item`s can be written inside `Expr`s. + // + // In practice, it seems likely that expressions, above a certain size, that + // happen to use the exact same idents in the exact same order, and which are + // not structured the same, would be rare. Therefore it seems likely that if + // we do only the first layer of matching ourselves and eventually fallback on + // IdentIter, then the output of this function will be almost always be correct + // in practice. + // + // If it turns out that problematic cases are more prelavent than we assume, + // then we should be able to change this function to do the correct traversal, + // without needing to change the rest of the code. + + #![allow(clippy::enum_glob_use)] + use ExprKind::*; + + match ( + &strip_non_ident_wrappers(left).kind, + &strip_non_ident_wrappers(right).kind, + ) { + (Yield(_), Yield(_)) + | (Try(_), Try(_)) + | (Paren(_), Paren(_)) + | (Repeat(_, _), Repeat(_, _)) + | (Struct(_, _, _), Struct(_, _, _)) + | (MacCall(_), MacCall(_)) + | (LlvmInlineAsm(_), LlvmInlineAsm(_)) + | (InlineAsm(_), InlineAsm(_)) + | (Ret(_), Ret(_)) + | (Continue(_), Continue(_)) + | (Break(_, _), Break(_, _)) + | (AddrOf(_, _, _), AddrOf(_, _, _)) + | (Path(_, _), Path(_, _)) + | (Range(_, _, _), Range(_, _, _)) + | (Index(_, _), Index(_, _)) + | (Field(_, _), Field(_, _)) + | (AssignOp(_, _, _), AssignOp(_, _, _)) + | (Assign(_, _, _), Assign(_, _, _)) + | (TryBlock(_), TryBlock(_)) + | (Await(_), Await(_)) + | (Async(_, _, _), Async(_, _, _)) + | (Block(_, _), Block(_, _)) + | (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _)) + | (Match(_, _), Match(_, _)) + | (Loop(_, _), Loop(_, _)) + | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) + | (While(_, _, _), While(_, _, _)) + | (If(_, _, _), If(_, _, _)) + | (Let(_, _), Let(_, _)) + | (Type(_, _), Type(_, _)) + | (Cast(_, _), Cast(_, _)) + | (Lit(_), Lit(_)) + | (Unary(_, _), Unary(_, _)) + | (Binary(_, _, _), Binary(_, _, _)) + | (Tup(_), Tup(_)) + | (MethodCall(_, _, _), MethodCall(_, _, _)) + | (Call(_, _), Call(_, _)) + | (ConstBlock(_), ConstBlock(_)) + | (Array(_), Array(_)) + | (Box(_), Box(_)) => { + // keep going + }, + _ => { + return (IdentDifference::NonIdent, base); + }, + } + + let mut difference = IdentDifference::NoDifference; + + for (left_attr, right_attr) in left.attrs.iter().zip(right.attrs.iter()) { + let (new_difference, new_base) = + ident_difference_via_ident_iter_with_base_location(left_attr, right_attr, base); + base = new_base; + difference += new_difference; + if difference.is_complete() { + return (difference, base); + } + } + + let (new_difference, new_base) = ident_difference_via_ident_iter_with_base_location(left, right, base); + base = new_base; + difference += new_difference; + + (difference, base) +} + +fn ident_difference_via_ident_iter_with_base_location>( + left: Iterable, + right: Iterable, + mut base: IdentLocation, +) -> (IdentDifference, IdentLocation) { + // See the note in `ident_difference_expr_with_base_location` about `IdentIter` + let mut difference = IdentDifference::NoDifference; + + let mut left_iterator = left.into(); + let mut right_iterator = right.into(); + + loop { + match (left_iterator.next(), right_iterator.next()) { + (Some(left_ident), Some(right_ident)) => { + if !eq_id(left_ident, right_ident) { + difference += IdentDifference::Single(base); + if difference.is_complete() { + return (difference, base); + } + } + }, + (Some(_), None) | (None, Some(_)) => { + return (IdentDifference::NonIdent, base); + }, + (None, None) => { + return (difference, base); + }, + } + base += IdentLocation { index: 1 }; + } +} + +fn get_ident(expr: &Expr, location: IdentLocation) -> Option { + IdentIter::from(expr).nth(location.index) +} + +fn suggestion_with_swapped_ident( + cx: &EarlyContext<'_>, + expr: &Expr, + location: IdentLocation, + new_ident: Ident, + applicability: &mut Applicability, +) -> Option { + get_ident(expr, location).and_then(|current_ident| { + if eq_id(current_ident, new_ident) { + // We never want to suggest a non-change + return None; + } + + Some(format!( + "{}{}{}", + snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), + new_ident.to_string(), + snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), + )) + }) +} + +fn skip_index(iter: Iter, index: usize) -> impl Iterator +where + Iter: Iterator, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index fcf7a4b1367..31b4e25411b 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -10,6 +10,17 @@ use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; use std::mem; +pub mod ident_iter; +pub use ident_iter::IdentIter; + +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { + use BinOpKind::*; + matches!( + kind, + Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr + ) +} + /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) diff --git a/clippy_lints/src/utils/ast_utils/ident_iter.rs b/clippy_lints/src/utils/ast_utils/ident_iter.rs new file mode 100644 index 00000000000..eefcbabd835 --- /dev/null +++ b/clippy_lints/src/utils/ast_utils/ident_iter.rs @@ -0,0 +1,45 @@ +use core::iter::FusedIterator; +use rustc_ast::visit::{walk_attribute, walk_expr, Visitor}; +use rustc_ast::{Attribute, Expr}; +use rustc_span::symbol::Ident; + +pub struct IdentIter(std::vec::IntoIter); + +impl Iterator for IdentIter { + type Item = Ident; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl FusedIterator for IdentIter {} + +impl From<&Expr> for IdentIter { + fn from(expr: &Expr) -> Self { + let mut visitor = IdentCollector::default(); + + walk_expr(&mut visitor, expr); + + IdentIter(visitor.0.into_iter()) + } +} + +impl From<&Attribute> for IdentIter { + fn from(attr: &Attribute) -> Self { + let mut visitor = IdentCollector::default(); + + walk_attribute(&mut visitor, attr); + + IdentIter(visitor.0.into_iter()) + } +} + +#[derive(Default)] +struct IdentCollector(Vec); + +impl Visitor<'_> for IdentCollector { + fn visit_ident(&mut self, ident: Ident) { + self.0.push(ident); + } +} diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 4e09d19ea21..7ab23320db6 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -86,3 +86,12 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn check_nested(n1: &Nested, n2: &Nested) -> bool { + // `n2.inner.0.0` mistyped as `n1.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index ad81b35a766..8ef658af8df 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,13 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:96:5 + | +LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: aborting due to 28 previous errors diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs new file mode 100644 index 00000000000..dd6f4ec7bd9 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.rs @@ -0,0 +1,207 @@ +#![warn(clippy::suspicious_operation_groupings)] + +struct Vec3 { + x: f64, + y: f64, + z: f64, +} + +impl Eq for Vec3 {} + +impl PartialEq for Vec3 { + fn eq(&self, other: &Self) -> bool { + // This should trigger the lint because `self.x` is compared to `other.y` + self.x == other.y && self.y == other.y && self.z == other.z + } +} + +struct S { + a: i32, + b: i32, + c: i32, + d: i32, +} + +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { + // There's no `s1.b` + s1.a < s2.a && s1.a < s2.b +} + +struct SAOnly { + a: i32, +} + +impl S { + fn a(&self) -> i32 { + 0 + } +} + +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SAOnly) -> bool { + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1.a() < s1.b +} + +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool { + macro_rules! s1 { + () => { + S { + a: 1, + b: 1, + c: 1, + d: 1, + } + }; + } + + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1!().a < s1.b +} + +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SAOnly) -> bool { + // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid + s1.a < s2.a && s1.b < s1.b +} + +fn permissable(s1: &S, s2: &S) -> bool { + // Something like this seems like it might actually be what is desired. + s1.a == s2.b +} + +fn non_boolean_operators(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d +} + +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.c + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + s1.a * s2.a + s2.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s1.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 { + // There's no `s2.a` + s1.a * s1.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s1.c +} + +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) { + ( + s1.b * s2.c - s1.c * s2.b, + s1.c * s2.a - s1.a * s2.c, + s1.a * s2.b - s1.b * s2.a, + ) +} + +fn outer_parens_simple(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + (s1.a * s2.a + s1.b * s1.b) +} + +fn outer_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) +} + +fn inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) +} + +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) +} + +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) +} + +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) +} + +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) +} + +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + (s1.a * s2.a + s2.b * s2.b) / 2 +} + +fn inside_function_call(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) +} + +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d +} + +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d +} + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.2.0` + (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 +} + +// `eq_op` should catch this one. +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool { + if strict { + s1.a < s2.a && s1.b < s2.b + } else { + // There's no `s1.b` in this subexpression + s1.a <= s2.a && s1.a <= s2.b + } +} + +fn inside_an_if_statement(s1: &S, s2: &S) { + // There's no `s1.b` + if s1.a < s2.a && s1.a < s2.b { + s1.c = s2.c; + } +} + +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) +} + +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) +} + +fn main() {} diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr new file mode 100644 index 00000000000..ce7108217f1 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -0,0 +1,166 @@ +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:14:9 + | +LL | self.x == other.y && self.y == other.y && self.z == other.z + | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | + = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:14:9 + | +LL | self.x == other.y && self.y == other.y && self.z == other.z + | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:27:20 + | +LL | s1.a < s2.a && s1.a < s2.b + | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:75:33 + | +LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:80:19 + | +LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:80:19 + | +LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:85:19 + | +LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:90:19 + | +LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:95:5 + | +LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.a * s2.a` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:100:33 + | +LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:113:20 + | +LL | (s1.a * s2.a + s1.b * s1.b) + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:118:34 + | +LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:123:38 + | +LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:128:39 + | +LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:133:42 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:133:42 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:138:40 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:143:40 + | +LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:148:20 + | +LL | (s1.a * s2.a + s2.b * s2.b) / 2 + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:153:35 + | +LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:158:29 + | +LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d + | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:163:17 + | +LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d + | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:172:77 + | +LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: I think you meant: `(n1.inner.2).0 == (n2.inner.2).0` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:186:25 + | +LL | s1.a <= s2.a && s1.a <= s2.b + | ^^^^^^^^^^^^ help: I think you meant: `s1.b <= s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:192:23 + | +LL | if s1.a < s2.a && s1.a < s2.b { + | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:199:48 + | +LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) + | ^^^^^^^^^^^^^ help: I think you meant: `-s1.c * -s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:204:27 + | +LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) + | ^^^^^^^^^^^^^ help: I think you meant: `-s1.b < -s2.b` + +error: aborting due to 27 previous errors + -- cgit 1.4.1-3-g733a5 From c1b991588f3b2945bddfded808bdab45e250a8dd Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 28 Nov 2020 17:03:20 +0100 Subject: Fix weird dogfood error --- clippy_lints/src/redundant_closure_call.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 49cb2ffc4e3..f398b3fff25 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { cx: &'a LateContext<'tcx>, path: &'tcx hir::Path<'tcx>, count: usize, - }; + } impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type Map = Map<'tcx>; @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } - }; + } let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count -- cgit 1.4.1-3-g733a5 From 0e5aee1fc1251493c35a4344700798e9a586ef16 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 28 Nov 2020 17:18:15 +0100 Subject: items_after_statements: don't lint when they a separated by trailing semicolons --- clippy_lints/src/items_after_statements.rs | 4 ++-- tests/ui/item_after_statement.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 8998fae09de..0927d218446 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -58,12 +58,12 @@ impl EarlyLintPass for ItemsAfterStatements { return; } - // skip initial items + // skip initial items and trailing semicolons let stmts = item .stmts .iter() .map(|stmt| &stmt.kind) - .skip_while(|s| matches!(**s, StmtKind::Item(..))); + .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty)); // lint on all further items for stmt in stmts { diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index 377e58e4417..d439ca1e4e1 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -37,3 +37,16 @@ fn mac() { b!(); println!("{}", a); } + +fn semicolon() { + struct S { + a: u32, + }; + impl S { + fn new(a: u32) -> Self { + Self { a } + } + } + + let _ = S::new(3); +} -- cgit 1.4.1-3-g733a5 From f7b2098e1c4f8e13ec2194f7f094f471b4056f97 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 29 Nov 2020 01:55:15 +0900 Subject: Fix a false positive in `unnecessary_wraps` --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 2 ++ tests/ui/unnecessary_wraps.rs | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 50dd760432d..004b8416fc1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3878,7 +3878,7 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } // Returns `true` if `expr` contains a return expression -fn contains_return(expr: &hir::Expr<'_>) -> bool { +pub(crate) fn contains_return(expr: &hir::Expr<'_>) -> bool { struct RetCallFinder { found: bool, } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 25ecc7a82f1..7b550c702cd 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,4 @@ +use crate::methods::contains_return; use crate::utils::{ in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, @@ -95,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if let ExprKind::Path(ref qpath) = func.kind; if match_qpath(qpath, path); if args.len() == 1; + if !contains_return(&args[0]); then { suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); true diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index a53dec8f91a..a4570098d71 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -109,6 +109,13 @@ impl B for A { } } +fn issue_6384(s: &str) -> Option<&str> { + Some(match s { + "a" => "A", + _ => return None, + }) +} + fn main() { // method calls are not linted func1(true, true); -- cgit 1.4.1-3-g733a5 From 2c26cb14db4d3a86aea0a897f9b727cef4e72e27 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 29 Nov 2020 02:18:05 +0900 Subject: Move `contains_return` to utils/mod.rs --- clippy_lints/src/methods/mod.rs | 43 +++++------------------------------ clippy_lints/src/unnecessary_wraps.rs | 3 +-- clippy_lints/src/utils/mod.rs | 30 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 39 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 004b8416fc1..1476408e0fb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -14,10 +14,8 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; -use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -28,11 +26,12 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, - match_trait_method, match_type, match_var, meets_msrv, method_calls, method_chain_args, paths, remove_blocks, - return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, + contains_return, contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + walk_ptrs_ty_depth, SpanlessEq, }; use semver::{Version, VersionReq}; @@ -3877,36 +3876,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } } -// Returns `true` if `expr` contains a return expression -pub(crate) fn contains_return(expr: &hir::Expr<'_>) -> bool { - struct RetCallFinder { - found: bool, - } - - impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if self.found { - return; - } - if let hir::ExprKind::Ret(..) = &expr.kind { - self.found = true; - } else { - intravisit::walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - } - - let mut visitor = RetCallFinder { found: false }; - visitor.visit_expr(expr); - visitor.found -} - fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if_chain! { if args.len() == 2; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 7b550c702cd..360df2a6752 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,5 @@ -use crate::methods::contains_return; use crate::utils::{ - in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 6f89e51279a..850abc3bae7 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -572,6 +572,36 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { cn.result } +/// Returns `true` if `expr` contains a return expression +pub fn contains_return(expr: &hir::Expr<'_>) -> bool { + struct RetCallFinder { + found: bool, + } + + impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if self.found { + return; + } + if let hir::ExprKind::Ret(..) = &expr.kind { + self.found = true; + } else { + hir::intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + } + + let mut visitor = RetCallFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + /// Converts a span to a code snippet if available, otherwise use default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want -- cgit 1.4.1-3-g733a5 From e266708c42dd3d9489586e65b9c6cd1bee0046d5 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Wed, 4 Nov 2020 21:16:25 +0300 Subject: do not trigger MATCH_LIKE_MATCHES_MACRO lint with attrs - it can't be solved completely for attrs evaluated into `false` - change applicability to MaybeIncorrect and mention it in docs --- clippy_lints/src/matches.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d695af4de21..c49abbf781e 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -459,7 +459,8 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** None + /// **Known problems:** It can be FP triggered, when some arms have `cfg` + /// attributes, which evaluate into `false`. /// /// **Example:** /// ```rust @@ -1167,13 +1168,16 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr if b0 != b1; let if_guard = &b0_arms[0].guard; if if_guard.is_none() || b0_arms.len() == 1; + if b0_arms[0].attrs.is_empty(); if b0_arms[1..].iter() .all(|arm| { find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && - arm.guard.is_none() + arm.guard.is_none() && arm.attrs.is_empty() }); then { - let mut applicability = Applicability::MachineApplicable; + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; b0_arms.iter() -- cgit 1.4.1-3-g733a5 From 22e7775aa798c1e4688089c150c0a077b9875bf0 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 28 Nov 2020 18:58:53 +0100 Subject: Change formulation of known problems section --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c49abbf781e..52da580b521 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -459,8 +459,8 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** It can be FP triggered, when some arms have `cfg` - /// attributes, which evaluate into `false`. + /// **Known problems:** This lint falsely triggers, if there are arms with + /// `cfg` attributes that remove an arm evaluating to `false`. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From 76f2c10fb6afb9071c56e8b6e410671573648ef4 Mon Sep 17 00:00:00 2001 From: Rajkumar Natarajan Date: Sat, 28 Nov 2020 12:19:57 -0500 Subject: issue_6357 update unreachable macro usage --- clippy_lints/src/panic_unimplemented.rs | 9 ++------- tests/ui/panicking_macros.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 31b03ecd101..359620cc079 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { /// ``` pub UNREACHABLE, restriction, - "`unreachable!` should not be present in production code" + "usage of the `unreachable!` macro" } declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); @@ -85,12 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { } else if is_expn_of(expr.span, "todo").is_some() { span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint( - cx, - UNREACHABLE, - span, - "`unreachable` should not be present in production code", - ); + span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); } else if is_expn_of(expr.span, "panic").is_some() { span_lint(cx, PANIC, span, "`panic` should not be present in production code"); } diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 83234c0ed92..6028323a3c8 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -62,7 +62,7 @@ error: `unimplemented` should not be present in production code LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 | LL | unreachable!(); @@ -70,7 +70,7 @@ LL | unreachable!(); | = note: `-D clippy::unreachable` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); @@ -78,7 +78,7 @@ LL | unreachable!("message"); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); @@ -102,7 +102,7 @@ error: `unimplemented` should not be present in production code LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); -- cgit 1.4.1-3-g733a5 From 84cdb0a939cec1e13d6f464bb7b036ff3b92dfb0 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 28 Nov 2020 22:40:46 +0100 Subject: Fix formatting --- clippy_lints/src/matches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 52da580b521..665c59d7093 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -460,7 +460,7 @@ declare_clippy_lint! { /// **Why is this bad?** Readability and needless complexity. /// /// **Known problems:** This lint falsely triggers, if there are arms with - /// `cfg` attributes that remove an arm evaluating to `false`. + /// `cfg` attributes that remove an arm evaluating to `false`. /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From cd087e5c5e65fa61c0562ceb2d2488f8f5660454 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Sat, 28 Nov 2020 19:41:27 +0530 Subject: add rustc-semver to dependencies switch Version/VersionReq usages to RustcVersion --- clippy_lints/Cargo.toml | 1 + clippy_lints/src/manual_non_exhaustive.rs | 14 ++++---------- clippy_lints/src/manual_strip.rs | 14 ++++---------- clippy_lints/src/matches.rs | 14 ++++---------- clippy_lints/src/methods/mod.rs | 16 +++++----------- clippy_lints/src/utils/mod.rs | 10 +++++----- 6 files changed, 23 insertions(+), 46 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..45fd87b169f 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -28,6 +28,7 @@ smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" +rustc-semver="1.0.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 703e6feeca5..91849e74887 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -4,17 +4,11 @@ use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantDat use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use semver::{Version, VersionReq}; -const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version { - major: 1, - minor: 40, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -66,12 +60,12 @@ declare_clippy_lint! { #[derive(Clone)] pub struct ManualNonExhaustive { - msrv: Option, + msrv: Option, } impl ManualNonExhaustive { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv } } } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index e17e3adb94f..f593abdb104 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -13,18 +13,12 @@ use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; -use semver::{Version, VersionReq}; -const MANUAL_STRIP_MSRV: Version = Version { - major: 1, - minor: 45, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); declare_clippy_lint! { /// **What it does:** @@ -61,12 +55,12 @@ declare_clippy_lint! { } pub struct ManualStrip { - msrv: Option, + msrv: Option, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv } } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7d64fa6c262..86e3e2f637e 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -20,10 +20,10 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty, TyS}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::{sym, Symbol}; -use semver::{Version, VersionReq}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -535,13 +535,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { - msrv: Option, + msrv: Option, infallible_destructuring_match_linted: bool, } impl Matches { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv, ..Matches::default() @@ -568,13 +568,7 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); -const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version { - major: 1, - minor: 42, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0); impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1476408e0fb..8002c27a5e9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -18,6 +18,7 @@ use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -33,7 +34,6 @@ use crate::utils::{ snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; -use semver::{Version, VersionReq}; declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. @@ -1405,12 +1405,12 @@ declare_clippy_lint! { } pub struct Methods { - msrv: Option, + msrv: Option, } impl Methods { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Option) -> Self { Self { msrv } } } @@ -3470,13 +3470,7 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { ); } -const OPTION_AS_REF_DEREF_MSRV: Version = Version { - major: 1, - minor: 40, - patch: 0, - pre: Vec::new(), - build: Vec::new(), -}; +const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s fn lint_option_as_ref_deref<'tcx>( @@ -3485,7 +3479,7 @@ fn lint_option_as_ref_deref<'tcx>( as_ref_args: &[hir::Expr<'_>], map_args: &[hir::Expr<'_>], is_mut: bool, - msrv: Option<&VersionReq>, + msrv: Option<&RustcVersion>, ) { if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { return; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 850abc3bae7..d68f0698153 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -51,6 +51,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; @@ -59,13 +60,12 @@ use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; -use semver::{Version, VersionReq}; use smallvec::SmallVec; use crate::consts::{constant, Constant}; -pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { - if let Ok(version) = VersionReq::parse(msrv) { +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { @@ -75,8 +75,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt None } -pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { - msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) +pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { + msrv.map_or(true, |msrv| msrv > lint_msrv) } macro_rules! extract_msrv_attr { -- cgit 1.4.1-3-g733a5 From a75ab302d28b6751fa1d1d5e47f8a75cebbaaf79 Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sat, 28 Nov 2020 23:17:43 -0800 Subject: fix dogfood tests --- clippy_lints/src/lib.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..52237b76c35 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -966,22 +966,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - let parsed_msrv = conf.msrv.as_ref().and_then(|s| { + let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); None }) }); - let msrv = parsed_msrv.clone(); - store.register_late_pass(move || box methods::Methods::new(msrv.clone())); - let msrv = parsed_msrv.clone(); - store.register_late_pass(move || box matches::Matches::new(msrv.clone())); - let msrv = parsed_msrv.clone(); - store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone())); - let msrv = parsed_msrv; - store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone())); - + store.register_late_pass(move || box methods::Methods::new(msrv)); + store.register_late_pass(move || box matches::Matches::new(msrv)); + store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); + store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); -- cgit 1.4.1-3-g733a5 From af095db64c48caa73211f40e1648dadd6a23a2ca Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sun, 29 Nov 2020 00:33:07 -0800 Subject: fix msrv check --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d68f0698153..9c5e55b1ed5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -76,7 +76,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt } pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv > lint_msrv) + msrv.map_or(true, |msrv| msrv >= lint_msrv) } macro_rules! extract_msrv_attr { -- cgit 1.4.1-3-g733a5 From 2838b044875b84bb9dc515ba3aa0c1b9772d870b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 8 Nov 2020 13:39:46 +0100 Subject: add internal-lints feature to enable clippys internal lints (off by default) --- .github/workflows/clippy_bors.yml | 4 +- Cargo.toml | 5 +- clippy_lints/Cargo.toml | 2 + clippy_lints/src/lib.rs | 28 ++++-- clippy_lints/src/utils/diagnostics.rs | 4 +- clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/paths.rs | 4 + tests/compile-test.rs | 15 ++- tests/dogfood.rs | 47 +++++++--- .../ui-internal/collapsible_span_lint_calls.fixed | 91 +++++++++++++++++++ tests/ui-internal/collapsible_span_lint_calls.rs | 101 +++++++++++++++++++++ .../ui-internal/collapsible_span_lint_calls.stderr | 49 ++++++++++ tests/ui-internal/custom_ice_message.rs | 10 ++ tests/ui-internal/custom_ice_message.stderr | 13 +++ tests/ui-internal/default_lint.rs | 27 ++++++ tests/ui-internal/default_lint.stderr | 21 +++++ tests/ui-internal/invalid_paths.rs | 23 +++++ tests/ui-internal/invalid_paths.stderr | 16 ++++ tests/ui-internal/lint_without_lint_pass.rs | 44 +++++++++ tests/ui-internal/lint_without_lint_pass.stderr | 21 +++++ tests/ui-internal/match_type_on_diag_item.rs | 50 ++++++++++ tests/ui-internal/match_type_on_diag_item.stderr | 33 +++++++ tests/ui-internal/outer_expn_data.fixed | 28 ++++++ tests/ui-internal/outer_expn_data.rs | 28 ++++++ tests/ui-internal/outer_expn_data.stderr | 15 +++ tests/ui/collapsible_span_lint_calls.fixed | 91 ------------------- tests/ui/collapsible_span_lint_calls.rs | 101 --------------------- tests/ui/collapsible_span_lint_calls.stderr | 49 ---------- tests/ui/custom_ice_message.rs | 10 -- tests/ui/custom_ice_message.stderr | 13 --- tests/ui/default_lint.rs | 27 ------ tests/ui/default_lint.stderr | 21 ----- tests/ui/invalid_paths.rs | 23 ----- tests/ui/invalid_paths.stderr | 16 ---- tests/ui/lint_without_lint_pass.rs | 44 --------- tests/ui/lint_without_lint_pass.stderr | 21 ----- tests/ui/match_type_on_diag_item.rs | 50 ---------- tests/ui/match_type_on_diag_item.stderr | 33 ------- tests/ui/outer_expn_data.fixed | 28 ------ tests/ui/outer_expn_data.rs | 28 ------ tests/ui/outer_expn_data.stderr | 15 --- 41 files changed, 654 insertions(+), 596 deletions(-) create mode 100644 tests/ui-internal/collapsible_span_lint_calls.fixed create mode 100644 tests/ui-internal/collapsible_span_lint_calls.rs create mode 100644 tests/ui-internal/collapsible_span_lint_calls.stderr create mode 100644 tests/ui-internal/custom_ice_message.rs create mode 100644 tests/ui-internal/custom_ice_message.stderr create mode 100644 tests/ui-internal/default_lint.rs create mode 100644 tests/ui-internal/default_lint.stderr create mode 100644 tests/ui-internal/invalid_paths.rs create mode 100644 tests/ui-internal/invalid_paths.stderr create mode 100644 tests/ui-internal/lint_without_lint_pass.rs create mode 100644 tests/ui-internal/lint_without_lint_pass.stderr create mode 100644 tests/ui-internal/match_type_on_diag_item.rs create mode 100644 tests/ui-internal/match_type_on_diag_item.stderr create mode 100644 tests/ui-internal/outer_expn_data.fixed create mode 100644 tests/ui-internal/outer_expn_data.rs create mode 100644 tests/ui-internal/outer_expn_data.stderr delete mode 100644 tests/ui/collapsible_span_lint_calls.fixed delete mode 100644 tests/ui/collapsible_span_lint_calls.rs delete mode 100644 tests/ui/collapsible_span_lint_calls.stderr delete mode 100644 tests/ui/custom_ice_message.rs delete mode 100644 tests/ui/custom_ice_message.stderr delete mode 100644 tests/ui/default_lint.rs delete mode 100644 tests/ui/default_lint.stderr delete mode 100644 tests/ui/invalid_paths.rs delete mode 100644 tests/ui/invalid_paths.stderr delete mode 100644 tests/ui/lint_without_lint_pass.rs delete mode 100644 tests/ui/lint_without_lint_pass.stderr delete mode 100644 tests/ui/match_type_on_diag_item.rs delete mode 100644 tests/ui/match_type_on_diag_item.stderr delete mode 100644 tests/ui/outer_expn_data.fixed delete mode 100644 tests/ui/outer_expn_data.rs delete mode 100644 tests/ui/outer_expn_data.stderr (limited to 'clippy_lints/src') diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 7509d90c6c2..11c1eeac1cf 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -132,10 +132,10 @@ jobs: run: cargo build --features deny-warnings - name: Test - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings --features internal-lints - name: Test clippy_lints - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings --features internal-lints working-directory: clippy_lints - name: Test rustc_tools_util diff --git a/Cargo.toml b/Cargo.toml index 1ddcd18598d..a765390c603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ path = "src/driver.rs" clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update semver = "0.11" -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } tempfile = { version = "3.1.0", optional = true } [dev-dependencies] @@ -49,8 +49,9 @@ derive-new = "0.5" rustc-workspace-hack = "1.0.0" [build-dependencies] -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } [features] deny-warnings = [] integration = ["tempfile"] +internal-lints = ["clippy_lints/internal-lints"] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..969249cc446 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -36,3 +36,5 @@ syn = { version = "1", features = ["full"] } [features] deny-warnings = [] +# build clippy with internal lints enabled, off by default +internal-lints = [] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..a58f7eb3666 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -904,14 +904,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, + #[cfg(feature = "internal-lints")] &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + #[cfg(feature = "internal-lints")] &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::DEFAULT_LINT, + #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + #[cfg(feature = "internal-lints")] &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + #[cfg(feature = "internal-lints")] &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, @@ -932,11 +941,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); - store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); - store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); - store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + #[cfg(feature = "internal-lints")] + { + store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + } store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); @@ -1122,6 +1134,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); + #[cfg(feature = "internal-lints")] store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); @@ -1136,6 +1149,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); + #[cfg(feature = "internal-lints")] store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); @@ -1152,6 +1166,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); + #[cfg(feature = "internal-lints")] store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); @@ -1177,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + #[cfg(feature = "internal-lints")] store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); @@ -1317,7 +1333,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), ]); - + #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 0a58231558e..a7a6b5855b7 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -186,7 +186,9 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[allow(clippy::collapsible_span_lint_calls)] + +#[allow(clippy::unknown_clippy_lints)] +#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 850abc3bae7..63f14c592bd 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -14,6 +14,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod inspector; +#[cfg(feature = "internal-lints")] pub mod internal_lints; pub mod numeric_literal; pub mod paths; diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 61aeabb7ba7..16e6a016c9e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -31,6 +31,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; +#[cfg(feature = "internal-lints")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; @@ -61,8 +62,10 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; +#[cfg(feature = "internal-lints")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; @@ -133,6 +136,7 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0e8f7683103..ec3af94b9ca 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -12,6 +12,9 @@ use std::path::{Path, PathBuf}; mod cargo; +// whether to run internal tests or not +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); + fn host_lib() -> PathBuf { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } @@ -96,6 +99,16 @@ fn run_mode(cfg: &mut compiletest::Config) { compiletest::run_tests(&cfg); } +fn run_internal_tests(cfg: &mut compiletest::Config) { + // only run internal tests with the internal-tests feature + if !RUN_INTERNAL_TESTS { + return; + } + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui-internal"); + compiletest::run_tests(&cfg); +} + fn run_ui_toml(config: &mut compiletest::Config) { fn run_tests(config: &compiletest::Config, mut tests: Vec) -> Result { let mut result = true; @@ -199,7 +212,6 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Some("main.rs") => {}, _ => continue, } - let paths = compiletest::common::TestPaths { file: file_path, base: config.src_base.clone(), @@ -253,4 +265,5 @@ fn compile_test() { run_mode(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); + run_internal_tests(&mut config); } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 48e0478f169..eae25adf839 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -18,20 +18,39 @@ fn dogfood_clippy() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let output = Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::internal"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); + let output = if cfg!(feature = "internal-lints") { + // with internal lints and internal warnings + Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .args(&["--features", "internal-lints"]) + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .args(&["-D", "clippy::internal"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap() + } else { + // without internal lints or warnings + Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap() + }; println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed new file mode 100644 index 00000000000..e588c23345e --- /dev/null +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +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>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs new file mode 100644 index 00000000000..d5dd3bb562b --- /dev/null +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -0,0 +1,101 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +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>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_help(expr.span, help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.help(help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_note(expr.span, note_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + }); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr new file mode 100644 index 00000000000..874d4a9f255 --- /dev/null +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -0,0 +1,49 @@ +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:75:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` + | +note: the lint level is defined here + --> $DIR/collapsible_span_lint_calls.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:78:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_help(expr.span, help_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:81:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.help(help_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` + +error: this call is collspible + --> $DIR/collapsible_span_lint_calls.rs:84:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_note(expr.span, note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` + +error: this call is collspible + --> $DIR/collapsible_span_lint_calls.rs:87:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.note(note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` + +error: aborting due to 5 previous errors + diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs new file mode 100644 index 00000000000..5b30c9d5721 --- /dev/null +++ b/tests/ui-internal/custom_ice_message.rs @@ -0,0 +1,10 @@ +// rustc-env:RUST_BACKTRACE=0 +// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" +// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" +// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" + +#![deny(clippy::internal)] + +fn it_looks_like_you_are_trying_to_kill_clippy() {} + +fn main() {} diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr new file mode 100644 index 00000000000..a1b8e2ee162 --- /dev/null +++ b/tests/ui-internal/custom_ice_message.stderr @@ -0,0 +1,13 @@ +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +error: internal compiler error: unexpected panic + +note: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new + +note: Clippy version: foo + +query stack during panic: +end of query stack diff --git a/tests/ui-internal/default_lint.rs b/tests/ui-internal/default_lint.rs new file mode 100644 index 00000000000..053faae02ce --- /dev/null +++ b/tests/ui-internal/default_lint.rs @@ -0,0 +1,27 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_DEFAULT, + Warn, + "default lint description", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); +declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); + +fn main() {} diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr new file mode 100644 index 00000000000..5c5836a7d29 --- /dev/null +++ b/tests/ui-internal/default_lint.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT_DEFAULT` has the default lint description + --> $DIR/default_lint.rs:17:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT_DEFAULT, +LL | | Warn, +LL | | "default lint description", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/default_lint.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` + = 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-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs new file mode 100644 index 00000000000..01e28ae5e9d --- /dev/null +++ b/tests/ui-internal/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-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr new file mode 100644 index 00000000000..bd69d661b71 --- /dev/null +++ b/tests/ui-internal/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 + diff --git a/tests/ui-internal/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs new file mode 100644 index 00000000000..beaef79a340 --- /dev/null +++ b/tests/ui-internal/lint_without_lint_pass.rs @@ -0,0 +1,44 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; +use rustc_lint::LintPass; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL, + Warn, + "", + report_in_external_macro: true +} + +pub struct Pass; +impl LintPass for Pass { + fn name(&self) -> &'static str { + "TEST_LINT" + } +} + +declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); + +pub struct Pass3; +impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]); + +fn main() {} diff --git a/tests/ui-internal/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr new file mode 100644 index 00000000000..1257dae96d7 --- /dev/null +++ b/tests/ui-internal/lint_without_lint_pass.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT` is not added to any `LintPass` + --> $DIR/lint_without_lint_pass.rs:11:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT, +LL | | Warn, +LL | | "", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/lint_without_lint_pass.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` + = 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-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs new file mode 100644 index 00000000000..fe950b0aa7c --- /dev/null +++ b/tests/ui-internal/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-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr new file mode 100644 index 00000000000..82465dbaf6e --- /dev/null +++ b/tests/ui-internal/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 + diff --git a/tests/ui-internal/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed new file mode 100644 index 00000000000..b0b3498f057 --- /dev/null +++ b/tests/ui-internal/outer_expn_data.fixed @@ -0,0 +1,28 @@ +// run-rustfix + +#![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}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn_data(); + } +} + +fn main() {} diff --git a/tests/ui-internal/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs new file mode 100644 index 00000000000..55a3fed00d0 --- /dev/null +++ b/tests/ui-internal/outer_expn_data.rs @@ -0,0 +1,28 @@ +// run-rustfix + +#![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}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn().expn_data(); + } +} + +fn main() {} diff --git a/tests/ui-internal/outer_expn_data.stderr b/tests/ui-internal/outer_expn_data.stderr new file mode 100644 index 00000000000..56b6ce1f78e --- /dev/null +++ b/tests/ui-internal/outer_expn_data.stderr @@ -0,0 +1,15 @@ +error: usage of `outer_expn().expn_data()` + --> $DIR/outer_expn_data.rs:24:34 + | +LL | let _ = expr.span.ctxt().outer_expn().expn_data(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` + | +note: the lint level is defined here + --> $DIR/outer_expn_data.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` + +error: aborting due to previous error + diff --git a/tests/ui/collapsible_span_lint_calls.fixed b/tests/ui/collapsible_span_lint_calls.fixed deleted file mode 100644 index e588c23345e..00000000000 --- a/tests/ui/collapsible_span_lint_calls.fixed +++ /dev/null @@ -1,91 +0,0 @@ -// run-rustfix -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -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>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl EarlyLintPass for Pass { - fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { - let lint_msg = "lint message"; - let help_msg = "help message"; - let note_msg = "note message"; - let sugg = "new_call()"; - let predicate = true; - - span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable); - span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - - // This expr shouldn't trigger this lint. - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.note(note_msg); - if predicate { - db.note(note_msg); - } - }) - } -} - -fn main() {} diff --git a/tests/ui/collapsible_span_lint_calls.rs b/tests/ui/collapsible_span_lint_calls.rs deleted file mode 100644 index d5dd3bb562b..00000000000 --- a/tests/ui/collapsible_span_lint_calls.rs +++ /dev/null @@ -1,101 +0,0 @@ -// run-rustfix -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -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>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl EarlyLintPass for Pass { - fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { - let lint_msg = "lint message"; - let help_msg = "help message"; - let note_msg = "note message"; - let sugg = "new_call()"; - let predicate = true; - - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.span_help(expr.span, help_msg); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.help(help_msg); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.span_note(expr.span, note_msg); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.note(note_msg); - }); - - // This expr shouldn't trigger this lint. - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.note(note_msg); - if predicate { - db.note(note_msg); - } - }) - } -} - -fn main() {} diff --git a/tests/ui/collapsible_span_lint_calls.stderr b/tests/ui/collapsible_span_lint_calls.stderr deleted file mode 100644 index 874d4a9f255..00000000000 --- a/tests/ui/collapsible_span_lint_calls.stderr +++ /dev/null @@ -1,49 +0,0 @@ -error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:75:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` - | -note: the lint level is defined here - --> $DIR/collapsible_span_lint_calls.rs:2:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` - -error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:78:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.span_help(expr.span, help_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` - -error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:81:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.help(help_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` - -error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:84:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.span_note(expr.span, note_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` - -error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:87:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.note(note_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/custom_ice_message.rs b/tests/ui/custom_ice_message.rs deleted file mode 100644 index 5b30c9d5721..00000000000 --- a/tests/ui/custom_ice_message.rs +++ /dev/null @@ -1,10 +0,0 @@ -// rustc-env:RUST_BACKTRACE=0 -// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" -// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" -// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" - -#![deny(clippy::internal)] - -fn it_looks_like_you_are_trying_to_kill_clippy() {} - -fn main() {} diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr deleted file mode 100644 index a1b8e2ee162..00000000000 --- a/tests/ui/custom_ice_message.stderr +++ /dev/null @@ -1,13 +0,0 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new - -note: Clippy version: foo - -query stack during panic: -end of query stack diff --git a/tests/ui/default_lint.rs b/tests/ui/default_lint.rs deleted file mode 100644 index 053faae02ce..00000000000 --- a/tests/ui/default_lint.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -extern crate rustc_lint; - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_tool_lint! { - pub clippy::TEST_LINT_DEFAULT, - Warn, - "default lint description", - report_in_external_macro: true -} - -declare_lint_pass!(Pass => [TEST_LINT]); -declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); - -fn main() {} diff --git a/tests/ui/default_lint.stderr b/tests/ui/default_lint.stderr deleted file mode 100644 index 5c5836a7d29..00000000000 --- a/tests/ui/default_lint.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error: the lint `TEST_LINT_DEFAULT` has the default lint description - --> $DIR/default_lint.rs:17:1 - | -LL | / declare_tool_lint! { -LL | | pub clippy::TEST_LINT_DEFAULT, -LL | | Warn, -LL | | "default lint description", -LL | | report_in_external_macro: true -LL | | } - | |_^ - | -note: the lint level is defined here - --> $DIR/default_lint.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` - = 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/invalid_paths.rs b/tests/ui/invalid_paths.rs deleted file mode 100644 index 01e28ae5e9d..00000000000 --- a/tests/ui/invalid_paths.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![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 deleted file mode 100644 index bd69d661b71..00000000000 --- a/tests/ui/invalid_paths.stderr +++ /dev/null @@ -1,16 +0,0 @@ -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 - diff --git a/tests/ui/lint_without_lint_pass.rs b/tests/ui/lint_without_lint_pass.rs deleted file mode 100644 index beaef79a340..00000000000 --- a/tests/ui/lint_without_lint_pass.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -extern crate rustc_lint; -use rustc_lint::LintPass; - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_tool_lint! { - pub clippy::TEST_LINT_REGISTERED, - Warn, - "", - report_in_external_macro: true -} - -declare_tool_lint! { - pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL, - Warn, - "", - report_in_external_macro: true -} - -pub struct Pass; -impl LintPass for Pass { - fn name(&self) -> &'static str { - "TEST_LINT" - } -} - -declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); - -pub struct Pass3; -impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]); - -fn main() {} diff --git a/tests/ui/lint_without_lint_pass.stderr b/tests/ui/lint_without_lint_pass.stderr deleted file mode 100644 index 1257dae96d7..00000000000 --- a/tests/ui/lint_without_lint_pass.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error: the lint `TEST_LINT` is not added to any `LintPass` - --> $DIR/lint_without_lint_pass.rs:11:1 - | -LL | / declare_tool_lint! { -LL | | pub clippy::TEST_LINT, -LL | | Warn, -LL | | "", -LL | | report_in_external_macro: true -LL | | } - | |_^ - | -note: the lint level is defined here - --> $DIR/lint_without_lint_pass.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` - = 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/match_type_on_diag_item.rs b/tests/ui/match_type_on_diag_item.rs deleted file mode 100644 index fe950b0aa7c..00000000000 --- a/tests/ui/match_type_on_diag_item.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![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 deleted file mode 100644 index 82465dbaf6e..00000000000 --- a/tests/ui/match_type_on_diag_item.stderr +++ /dev/null @@ -1,33 +0,0 @@ -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 - diff --git a/tests/ui/outer_expn_data.fixed b/tests/ui/outer_expn_data.fixed deleted file mode 100644 index b0b3498f057..00000000000 --- a/tests/ui/outer_expn_data.fixed +++ /dev/null @@ -1,28 +0,0 @@ -// run-rustfix - -#![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}; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let _ = expr.span.ctxt().outer_expn_data(); - } -} - -fn main() {} diff --git a/tests/ui/outer_expn_data.rs b/tests/ui/outer_expn_data.rs deleted file mode 100644 index 55a3fed00d0..00000000000 --- a/tests/ui/outer_expn_data.rs +++ /dev/null @@ -1,28 +0,0 @@ -// run-rustfix - -#![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}; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let _ = expr.span.ctxt().outer_expn().expn_data(); - } -} - -fn main() {} diff --git a/tests/ui/outer_expn_data.stderr b/tests/ui/outer_expn_data.stderr deleted file mode 100644 index 56b6ce1f78e..00000000000 --- a/tests/ui/outer_expn_data.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: usage of `outer_expn().expn_data()` - --> $DIR/outer_expn_data.rs:24:34 - | -LL | let _ = expr.span.ctxt().outer_expn().expn_data(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` - | -note: the lint level is defined here - --> $DIR/outer_expn_data.rs:3:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` - -error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From 958e2e20de762fa45f50e41a58c97548f79f8100 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 13 Nov 2020 02:12:48 +0100 Subject: fix clippy-dev update_lints --- clippy_dev/src/lib.rs | 30 +++++++++++++++++++++++------- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/src/lib.rs | 36 ++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 26 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 43cb2954b74..1453ac7efa3 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -146,16 +146,32 @@ pub fn gen_deprecated<'a>(lints: impl Iterator) -> Vec } #[must_use] -pub fn gen_register_lint_list<'a>(lints: impl Iterator) -> Vec { - let pre = " store.register_lints(&[".to_string(); - let post = " ]);".to_string(); - let mut inner = lints +pub fn gen_register_lint_list<'a>( + internal_lints: impl Iterator, + usable_lints: impl Iterator, +) -> Vec { + let header = " store.register_lints(&[".to_string(); + let footer = " ]);".to_string(); + let internal_lints = internal_lints + .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .map(|l| { + format!( + " #[cfg(feature = \"internal-lints\")]\n &{}::{},", + l.module, + l.name.to_uppercase() + ) + }) + .collect::>(); + let other_lints = usable_lints + .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) .sorted() .collect::>(); - inner.insert(0, pre); - inner.push(post); - inner + let mut lint_list = vec![header]; + lint_list.extend(internal_lints); + lint_list.extend(other_lints); + lint_list.push(footer); + lint_list } /// Gathers all files in `src/clippy_lints` and gathers all lints inside diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index fcf093f8835..edf6c5f57a4 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -68,7 +68,7 @@ pub fn run(update_mode: UpdateMode) { "end register lints", false, update_mode == UpdateMode::Change, - || gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())), + || gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), ) .changed; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a58f7eb3666..fed7da3ee4f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -498,6 +498,24 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // begin register lints, do not remove this comment, it’s used in `update_lints` store.register_lints(&[ + #[cfg(feature = "internal-lints")] + &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::DEFAULT_LINT, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::PRODUCE_ICE, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -904,24 +922,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] - &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, -- cgit 1.4.1-3-g733a5 From 5df286b636e0bf71a665599f099da18a2b90936c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 27 Nov 2020 12:15:05 -0600 Subject: Improve SpanlessEq for blocks --- clippy_lints/src/utils/hir_utils.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index e4ad105c351..d847d22275e 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -81,7 +81,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - match (&left.kind, &right.kind) { + match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, @@ -306,6 +306,32 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } +/// Some simple reductions like `{ return }` => `return` +fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> { + if let ExprKind::Block(block, _) = kind { + match (block.stmts, block.expr) { + // `{}` => `()` + ([], None) => &ExprKind::Tup(&[]), + ([], Some(expr)) => match expr.kind { + // `{ return .. }` => `return ..` + ExprKind::Ret(..) => &expr.kind, + _ => kind, + }, + ([stmt], None) => match stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { + // `{ return ..; }` => `return ..` + ExprKind::Ret(..) => &expr.kind, + _ => kind, + }, + _ => kind, + }, + _ => kind, + } + } else { + kind + } +} + fn swap_binop<'a>( binop: BinOpKind, lhs: &'a Expr<'a>, -- cgit 1.4.1-3-g733a5 From 6e1fbfdb8fe9a6b543fa2b0e688f928d2ee354b8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 27 Nov 2020 17:13:19 -0600 Subject: Add LocalUseVisitor --- clippy_lints/src/utils/visitors.rs | 55 +++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/visitors.rs b/clippy_lints/src/utils/visitors.rs index b0837b6c43e..28b3e79d7a6 100644 --- a/clippy_lints/src/utils/visitors.rs +++ b/clippy_lints/src/utils/visitors.rs @@ -1,5 +1,7 @@ use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, QPath, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -123,3 +125,54 @@ where !ret_finder.failed } } + +pub struct LocalUsedVisitor { + pub local_hir_id: HirId, + pub used: bool, +} + +impl LocalUsedVisitor { + pub fn new(local_hir_id: HirId) -> Self { + Self { + local_hir_id, + used: false, + } + } + + fn check(&mut self, t: T, visit: fn(&mut Self, T)) -> bool { + visit(self, t); + std::mem::replace(&mut self.used, false) + } + + pub fn check_arm(&mut self, arm: &Arm<'_>) -> bool { + self.check(arm, Self::visit_arm) + } + + pub fn check_expr(&mut self, expr: &Expr<'_>) -> bool { + self.check(expr, Self::visit_expr) + } + + pub fn check_stmt(&mut self, stmt: &Stmt<'_>) -> bool { + self.check(stmt, Self::visit_stmt) + } +} + +impl<'v> Visitor<'v> for LocalUsedVisitor { + type Map = Map<'v>; + + fn visit_expr(&mut self, expr: &'v Expr<'v>) { + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { + if let Res::Local(id) = path.res { + if id == self.local_hir_id { + self.used = true; + return; + } + } + } + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} -- cgit 1.4.1-3-g733a5 From 28dec3b708c3e0d5e45b6c70f054860cbd53d624 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 24 Nov 2020 20:37:07 -0600 Subject: Add collapsible_match lint --- CHANGELOG.md | 1 + clippy_lints/src/collapsible_match.rs | 172 +++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + tests/ui/collapsible_match.rs | 278 ++++++++++++++++++++++++++++++++++ tests/ui/collapsible_match.stderr | 237 +++++++++++++++++++++++++++++ 5 files changed, 693 insertions(+) create mode 100644 clippy_lints/src/collapsible_match.rs create mode 100644 tests/ui/collapsible_match.rs create mode 100644 tests/ui/collapsible_match.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e76a781f13b..e65e7cc639f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1770,6 +1770,7 @@ Released 2018-09-13 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs new file mode 100644 index 00000000000..a34ba2d00a8 --- /dev/null +++ b/clippy_lints/src/collapsible_match.rs @@ -0,0 +1,172 @@ +use crate::utils::visitors::LocalUsedVisitor; +use crate::utils::{span_lint_and_then, SpanlessEq}; +use if_chain::if_chain; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{MultiSpan, Span}; + +declare_clippy_lint! { + /// **What it does:** Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together + /// without adding any branches. + /// + /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only + /// cases where merging would most likely make the code more readable. + /// + /// **Why is this bad?** It is unnecessarily verbose and complex. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(n) => match n { + /// Ok(n) => n, + /// _ => return, + /// } + /// None => return, + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(Ok(n)) => n, + /// _ => return, + /// }; + /// } + /// ``` + pub COLLAPSIBLE_MATCH, + style, + "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." +} + +declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Match(_expr, arms, _source) = expr.kind { + if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) { + for arm in arms { + check_arm(arm, wild_arm, cx); + } + } + } + } +} + +fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) { + if_chain! { + let expr = strip_singleton_blocks(arm.body); + if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; + // the outer arm pattern and the inner match + if expr_in.span.ctxt() == arm.pat.span.ctxt(); + // there must be no more than two arms in the inner match for this lint + if arms_inner.len() == 2; + // no if guards on the inner match + if arms_inner.iter().all(|arm| arm.guard.is_none()); + // match expression must be a local binding + // match { .. } + if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind; + if let Res::Local(binding_id) = path.res; + // one of the branches must be "wild-like" + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); + let (wild_inner_arm, non_wild_inner_arm) = + (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); + if !pat_contains_or(non_wild_inner_arm.pat); + // the binding must come from the pattern of the containing match arm + // .... => match { .. } + if let Some(binding_span) = find_pat_binding(arm.pat, binding_id); + // the "wild-like" branches must be equal + if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); + // the binding must not be used in the if guard + if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard)); + // ...or anywhere in the inner match + if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm)); + then { + span_lint_and_then( + cx, + COLLAPSIBLE_MATCH, + expr.span, + "Unnecessary nested match", + |diag| { + let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); + help_span.push_span_label(binding_span, "Replace this binding".into()); + help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); + diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern."); + }, + ); + } + } +} + +fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { + while let ExprKind::Block(block, _) = expr.kind { + match (block.stmts, block.expr) { + ([stmt], None) => match stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e, + _ => break, + }, + ([], Some(e)) => expr = e, + _ => break, + } + } + expr +} + +/// A "wild-like" pattern is wild ("_") or `None`. +/// For this lint to apply, both the outer and inner match expressions +/// must have "wild-like" branches that can be combined. +fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool { + if arm.guard.is_some() { + return false; + } + match arm.pat.kind { + PatKind::Binding(..) | PatKind::Wild => true, + PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true, + _ => false, + } +} + +fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { + let mut span = None; + pat.walk_short(|p| match &p.kind { + // ignore OR patterns + PatKind::Or(_) => false, + PatKind::Binding(_bm, _, _ident, _) => { + let found = p.hir_id == hir_id; + if found { + span = Some(p.span); + } + !found + }, + _ => true, + }); + span +} + +fn pat_contains_or(pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + let is_or = matches!(p.kind, PatKind::Or(_)); + result |= is_or; + !is_or + }); + result +} + +fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { + if let Some(none_id) = tcx.lang_items().option_none_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res { + if let Some(variant_id) = tcx.parent(id) { + return variant_id == none_id; + } + } + } + false +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..ca190986cdb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -172,6 +172,7 @@ mod cargo_common_metadata; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; +mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; @@ -531,6 +532,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, + &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, @@ -960,6 +962,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box len_zero::LenZero); store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); + store.register_late_pass(|| box collapsible_match::CollapsibleMatch); store.register_late_pass(|| box unicode::Unicode); store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); @@ -1351,6 +1354,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), + LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), @@ -1617,6 +1621,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), + LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&doc::MISSING_SAFETY_DOC), diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs new file mode 100644 index 00000000000..75640b70ff0 --- /dev/null +++ b/tests/ui/collapsible_match.rs @@ -0,0 +1,278 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] + +fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { + // match without block + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // match with block + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // if let, if let + if let Ok(val) = res_opt { + if let Some(n) = val { + take(n); + } + } + + // if let else, if let else + if let Ok(val) = res_opt { + if let Some(n) = val { + take(n); + } else { + return; + } + } else { + return; + } + + // if let, match + if let Ok(val) = res_opt { + match val { + Some(n) => foo(n), + _ => (), + } + } + + // match, if let + match res_opt { + Ok(val) => { + if let Some(n) = val { + take(n); + } + }, + _ => {}, + } + + // if let else, match + if let Ok(val) = res_opt { + match val { + Some(n) => foo(n), + _ => return, + } + } else { + return; + } + + // match, if let else + match res_opt { + Ok(val) => { + if let Some(n) = val { + take(n); + } else { + return; + } + }, + _ => return, + } + + // None in inner match same as outer wild branch + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + None => return, + }, + _ => return, + } + + // None in outer match same as inner wild branch + match opt_opt { + Some(val) => match val { + Some(n) => foo(n), + _ => return, + }, + None => return, + } + + // if guards on outer match + { + match res_opt { + Ok(val) if make() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ if make() => return, + _ => return, + } + } + + // macro + { + macro_rules! mac { + ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { + match $outer { + $pat => match $e { + $inner_pat => $then, + _ => return, + }, + _ => return, + } + }; + } + // Lint this since the patterns are not defined by the macro. + // Allows the lint to work on if_chain! for example. + // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that + // there is still a better way to write this. + mac!(res_opt => Ok(val), val => Some(n), foo(n)); + } +} + +fn negative_cases(res_opt: Result, String>, res_res: Result, String>) { + // no wild pattern in outer match + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + Err(_) => return, + } + + // inner branch is not wild or None + match res_res { + Ok(val) => match val { + Ok(n) => foo(n), + Err(_) => return, + }, + _ => return, + } + + // statement before inner match + match res_opt { + Ok(val) => { + "hi buddy"; + match val { + Some(n) => foo(n), + _ => return, + } + }, + _ => return, + } + + // statement after inner match + match res_opt { + Ok(val) => { + match val { + Some(n) => foo(n), + _ => return, + } + "hi buddy"; + }, + _ => return, + } + + // wild branches do not match + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => { + "sup"; + return; + }, + }, + _ => return, + } + + // binding used in if guard + match res_opt { + Ok(val) if val.is_some() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // binding used in inner match body + match res_opt { + Ok(val) => match val { + Some(_) => take(val), + _ => return, + }, + _ => return, + } + + // if guard on inner match + { + match res_opt { + Ok(val) => match val { + Some(n) if make() => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + _ => make(), + _ if make() => return, + }, + _ => return, + } + } + + // differing macro contexts + { + macro_rules! mac { + ($val:ident) => { + match $val { + Some(n) => foo(n), + _ => return, + } + }; + } + match res_opt { + Ok(val) => mac!(val), + _ => return, + } + } + + // OR pattern + enum E { + A(T), + B(T), + C(T), + }; + match make::>>() { + E::A(val) | E::B(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match make::>>() { + Some(val) => match val { + E::A(val) | E::B(val) => foo(val), + _ => return, + }, + _ => return, + } +} + +fn make() -> T { + unimplemented!() +} + +fn foo(t: T) -> U { + unimplemented!() +} + +fn take(t: T) {} + +fn main() {} diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr new file mode 100644 index 00000000000..a9e4f911914 --- /dev/null +++ b/tests/ui/collapsible_match.stderr @@ -0,0 +1,237 @@ +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:7:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | + = note: `-D clippy::collapsible-match` implied by `-D warnings` +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:7:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:16:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:16:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:25:9 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:24:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:32:9 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } else { +LL | | return; +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:31:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:43:9 + | +LL | / match val { +LL | | Some(n) => foo(n), +LL | | _ => (), +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:42:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | match val { +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:52:13 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:51:12 + | +LL | Ok(val) => { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:61:9 + | +LL | / match val { +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:60:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | match val { +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:72:13 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } else { +LL | | return; +LL | | } + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:71:12 + | +LL | Ok(val) => { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:83:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | None => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:83:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:92:22 + | +LL | Some(val) => match val { + | ______________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:92:14 + | +LL | Some(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:102:34 + | +LL | Ok(val) if make() => match val { + | __________________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:102:16 + | +LL | Ok(val) if make() => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:109:24 + | +LL | Ok(val) => match val { + | ________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:109:16 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:123:29 + | +LL | $pat => match $e { + | _____________________________^ +LL | | $inner_pat => $then, +LL | | _ => return, +LL | | }, + | |_____________________^ +... +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ------------------------------------------------- in this macro invocation + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:135:28 + | +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ^^^ ^^^^^^^ with this pattern + | | + | Replace this binding + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From fff5fa65816f482357989f25a58e98688cb7363d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 24 Nov 2020 20:57:09 -0600 Subject: Eat collapsible_match dogfood --- clippy_lints/src/default.rs | 3 +-- clippy_lints/src/if_let_some_result.rs | 3 +-- clippy_lints/src/implicit_return.rs | 3 +-- clippy_lints/src/implicit_saturating_sub.rs | 3 +-- clippy_lints/src/large_const_arrays.rs | 3 +-- clippy_lints/src/large_stack_arrays.rs | 3 +-- clippy_lints/src/loops.rs | 6 ++---- clippy_lints/src/manual_strip.rs | 3 +-- clippy_lints/src/matches.rs | 24 +++++++++------------- .../src/methods/manual_saturating_arithmetic.rs | 3 +-- clippy_lints/src/needless_bool.rs | 11 +++------- clippy_lints/src/question_mark.rs | 3 +-- clippy_lints/src/strings.rs | 3 +-- clippy_lints/src/trait_bounds.rs | 3 +-- clippy_lints/src/transmuting_null.rs | 3 +-- clippy_lints/src/types.rs | 6 ++---- clippy_lints/src/utils/higher.rs | 3 +-- 17 files changed, 30 insertions(+), 56 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 612c5355338..f69f6f1412a 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -280,8 +280,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; - if let ExprKind::Path(ref qpath) = binding.kind; - if let QPath::Resolved(_, path) = qpath; + if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; if let Some(second_binding_name) = path.segments.last(); if second_binding_name.ident.name == binding_name; then { diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index e0a1f4c5ca4..1194bd7e55e 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -41,8 +41,7 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match - if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let + if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index ed7f3b9293d..03e95c9e27f 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -68,8 +68,7 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let StmtKind::Semi(expr, ..) = &stmt.kind; // make sure it's a break, otherwise we want to skip - if let ExprKind::Break(.., break_expr) = &expr.kind; - if let Some(break_expr) = break_expr; + if let ExprKind::Break(.., Some(break_expr)) = &expr.kind; then { lint(cx, expr.span, break_expr.span, LINT_BREAK); } diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index b57fe8dc426..3a01acd8fdc 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -59,8 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let Some(target) = subtracts_one(cx, e); // Extracting out the variable name - if let ExprKind::Path(ref assign_path) = target.kind; - if let QPath::Resolved(_, ref ares_path) = assign_path; + if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind; then { // Handle symmetric conditions in the if statement diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 025ff86da39..a76595ed089 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -52,8 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if let ItemKind::Const(hir_ty, _) = &item.kind; let ty = hir_ty_to_ty(cx.tcx, hir_ty); if let ty::Array(element_type, cst) = ty.kind(); - if let ConstKind::Value(val) = cst.val; - if let ConstValue::Scalar(element_count) = val; + if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes()); if self.maximum_allowed_size < element_count * element_size; diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 9fd3780e14e..9a448ab1256 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -43,8 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { if_chain! { if let ExprKind::Repeat(_, _) = expr.kind; if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind(); - if let ConstKind::Value(val) = cst.val; - if let ConstValue::Scalar(element_count) = val; + if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes()); if self.maximum_allowed_size < element_count * element_size; diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 143cbea5537..b0de355e242 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1919,8 +1919,7 @@ fn check_for_single_element_loop<'tcx>( if_chain! { if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; - if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind; - if let [arg_expression] = arg_expr_list; + if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); if let ExprKind::Block(ref block, _) = body.kind; @@ -2025,8 +2024,7 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option let node_str = cx.tcx.hir().get(hir_id); if_chain! { if let Node::Binding(pat) = node_str; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if let BindingAnnotation::Mutable = bind_ann; + if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind; then { return Some(hir_id); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index e17e3adb94f..80f3045415e 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -225,8 +225,7 @@ fn find_stripping<'tcx>( if is_ref_str(self.cx, ex); let unref = peel_ref(ex); if let ExprKind::Index(indexed, index) = &unref.kind; - if let Some(range) = higher::range(index); - if let higher::Range { start, end, .. } = range; + if let Some(higher::Range { start, end, .. }) = higher::range(index); if let ExprKind::Path(path) = &indexed.kind; if qpath_res(self.cx, path, ex.hir_id) == self.target; then { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7d64fa6c262..c223462af26 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -652,8 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), pat.span); if !in_macro(pat.span); - if let PatKind::Struct(ref qpath, fields, true) = pat.kind; - if let QPath::Resolved(_, ref path) = qpath; + if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); if let ty::Adt(def, _) = ty.kind(); @@ -962,16 +961,14 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) if let QPath::Resolved(_, p) = path { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } - } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind { - if let QPath::Resolved(_, p) = path { - // Some simple checks for exhaustive patterns. - // There is a room for improvements to detect more cases, - // but it can be more expensive to do so. - let is_pattern_exhaustive = - |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); - if patterns.iter().all(is_pattern_exhaustive) { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); - } + } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind { + // Some simple checks for exhaustive patterns. + // There is a room for improvements to detect more cases, + // but it can be more expensive to do so. + let is_pattern_exhaustive = + |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); + if patterns.iter().all(is_pattern_exhaustive) { + missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } } } @@ -1446,8 +1443,7 @@ fn is_ref_some_arm(arm: &Arm<'_>) -> Option { if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; - if let ExprKind::Path(ref qpath) = args[0].kind; - if let &QPath::Resolved(_, ref path2) = qpath; + if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { return Some(rb) diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 40a62575861..44c974b9d98 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -90,8 +90,7 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option return Some(MinMax::Max), diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index a799a644e97..42f97b2ac49 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -6,7 +6,6 @@ use crate::utils::sugg::Sugg; use crate::utils::{ higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, }; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; @@ -198,13 +197,9 @@ struct ExpressionInfoWithSpan { } fn is_unary_not(e: &Expr<'_>) -> (bool, Span) { - if_chain! { - if let ExprKind::Unary(unop, operand) = e.kind; - if let UnOp::UnNot = unop; - then { - return (true, operand.span); - } - }; + if let ExprKind::Unary(UnOp::UnNot, operand) = e.kind { + return (true, operand.span); + } (false, e.span) } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d9b280b7a85..b91233ac582 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -176,8 +176,7 @@ impl QuestionMark { if block.stmts.len() == 1; if let Some(expr) = block.stmts.iter().last(); if let StmtKind::Semi(ref expr) = expr.kind; - if let ExprKind::Ret(ret_expr) = expr.kind; - if let Some(ret_expr) = ret_expr; + if let ExprKind::Ret(Some(ret_expr)) = expr.kind; then { return Some(ret_expr); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 42c45be3b45..77e79073378 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -222,8 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if method_names[0] == sym!(as_bytes); // Check for slicer - if let ExprKind::Struct(ref path, _, _) = right.kind; - if let QPath::LangItem(LangItem::Range, _) = path; + if let ExprKind::Struct(QPath::LangItem(LangItem::Range, _), _, _) = right.kind; then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index d4acf8df46d..daff5f81e8c 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -168,8 +168,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !in_macro(bound_predicate.span); - if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind; - if let QPath::Resolved(_, Path { ref segments, .. }) = path; + if let TyKind::Path(QPath::Resolved(_, Path { ref segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index d60306336c6..6b171a0fa1a 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -48,8 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { if_chain! { if let ExprKind::Path(ref _qpath) = args[0].kind; let x = const_eval_context.expr(&args[0]); - if let Some(constant) = x; - if let Constant::RawPtr(0) = constant; + if let Some(Constant::RawPtr(0)) = x; then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c8bdc5a71e6..74ba53e6a9a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -738,8 +738,7 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { if_chain! { if let Some(did) = qpath_res(cx, qpath, id).opt_def_id(); - if let Some(node) = cx.tcx.hir().get_if_local(did); - if let Node::GenericParam(generic_param) = node; + if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; if synthetic == Some(SyntheticTyParamKind::ImplTrait); then { @@ -1470,8 +1469,7 @@ fn check_loss_of_sign(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast // don't lint for positive constants let const_val = constant(cx, &cx.typeck_results(), op); if_chain! { - if let Some((const_val, _)) = const_val; - if let Constant::Int(n) = const_val; + if let Some((Constant::Int(n), _)) = const_val; if let ty::Int(ity) = *cast_from.kind(); if sext(cx.tcx, n, ity) >= 0; then { diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 6d7c5058b4f..01ffac5b559 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -162,8 +162,7 @@ pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr< if let hir::Block { expr: Some(expr), .. } = &**block; if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind; if let hir::ExprKind::DropTemps(cond) = &cond.kind; - if let [arm, ..] = &arms[..]; - if let hir::Arm { body, .. } = arm; + if let [hir::Arm { body, .. }, ..] = &arms[..]; then { return Some((cond, body)); } -- cgit 1.4.1-3-g733a5 From a5d6855333c55636bc0fc56efcc83eac7c57ffa7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 28 Nov 2020 20:41:29 -0600 Subject: Use LocalUsedVisitor in more places --- clippy_lints/src/let_if_seq.rs | 53 +++++------------------------------------- clippy_lints/src/loops.rs | 29 ++--------------------- 2 files changed, 8 insertions(+), 74 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 8243b0a29bc..0d2d95324c4 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,12 +1,11 @@ +use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{higher, qpath_res, snippet, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::intravisit; use rustc_hir::BindingAnnotation; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -66,10 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; if let hir::StmtKind::Expr(ref if_) = expr.kind; if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_); - if !used_in_expr(cx, canonical_id, cond); + if !LocalUsedVisitor::new(canonical_id).check_expr(cond); if let hir::ExprKind::Block(ref then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); - if !used_in_expr(cx, canonical_id, value); + if !LocalUsedVisitor::new(canonical_id).check_expr(value); then { let span = stmt.span.to(if_.span); @@ -136,32 +135,6 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { } } -struct UsedVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - id: hir::HirId, - used: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Path(ref qpath) = expr.kind; - if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id); - if self.id == local_id; - then { - self.used = true; - return; - } - } - intravisit::walk_expr(self, expr); - } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - fn check_assign<'tcx>( cx: &LateContext<'tcx>, decl: hir::HirId, @@ -176,18 +149,10 @@ fn check_assign<'tcx>( if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id); if decl == local_id; then { - let mut v = UsedVisitor { - cx, - id: decl, - used: false, - }; - - for s in block.stmts.iter().take(block.stmts.len()-1) { - intravisit::walk_stmt(&mut v, s); + let mut v = LocalUsedVisitor::new(decl); - if v.used { - return None; - } + if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) { + return None; } return Some(value); @@ -196,9 +161,3 @@ fn check_assign<'tcx>( None } - -fn used_in_expr<'tcx>(cx: &LateContext<'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool { - let mut v = UsedVisitor { cx, id, used: false }; - intravisit::walk_expr(&mut v, expr); - v.used -} diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index b0de355e242..400148ab81d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2,6 +2,7 @@ use crate::consts::constant; use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; +use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, @@ -2069,28 +2070,6 @@ fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { } } -struct LocalUsedVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - local: HirId, - used: bool, -} - -impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if same_var(self.cx, expr, self.local) { - self.used = true; - } else { - walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - struct VarVisitor<'a, 'tcx> { /// context reference cx: &'a LateContext<'tcx>, @@ -2124,11 +2103,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { then { let index_used_directly = same_var(self.cx, idx, self.var); let indexed_indirectly = { - let mut used_visitor = LocalUsedVisitor { - cx: self.cx, - local: self.var, - used: false, - }; + let mut used_visitor = LocalUsedVisitor::new(self.var); walk_expr(&mut used_visitor, idx); used_visitor.used }; -- cgit 1.4.1-3-g733a5 From 252083f7e02a3a9174bb39821fd20356ada3dd4a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 16 Nov 2020 12:44:05 +0100 Subject: address review comments and rebase ci: always build with internal lints group up internal lints in lib.rs dogfood: we already pass --all-features, no need to enable internal-lints again --- .github/workflows/clippy_bors.yml | 7 ------ clippy_dev/src/lib.rs | 6 ++--- clippy_lints/src/lib.rs | 25 ++++++++---------- tests/dogfood.rs | 53 +++++++++++++++------------------------ 4 files changed, 33 insertions(+), 58 deletions(-) (limited to 'clippy_lints/src') diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index a8b4925176c..784463fe0df 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -128,13 +128,6 @@ jobs: SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - - name: Build - run: cargo build --features deny-warnings - - # compiletest would panic due to "Found multiple rlibs for crate `clippy_lints`" - - name: clean rlibs - run: rm -f ./target/debug/deps/libclippy_lints* - - name: Build with internal lints run: cargo build --features deny-warnings,internal-lints diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 1453ac7efa3..f51c45e9eb5 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -160,13 +160,11 @@ pub fn gen_register_lint_list<'a>( l.module, l.name.to_uppercase() ) - }) - .collect::>(); + }); let other_lints = usable_lints .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) - .sorted() - .collect::>(); + .sorted(); let mut lint_list = vec![header]; lint_list.extend(internal_lints); lint_list.extend(other_lints); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fed7da3ee4f..8fbd44528b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -939,17 +939,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeAPI); + + // all the internal lints #[cfg(feature = "internal-lints")] { + store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); + store.register_early_pass(|| box utils::internal_lints::ProduceIce); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); } store.register_late_pass(|| box utils::author::Author); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); + store.register_late_pass(|| box serde_api::SerdeAPI); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); @@ -1134,8 +1140,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); - #[cfg(feature = "internal-lints")] - store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); @@ -1149,8 +1153,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); - #[cfg(feature = "internal-lints")] - store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); @@ -1166,8 +1168,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); - #[cfg(feature = "internal-lints")] - store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); @@ -1175,7 +1175,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1192,8 +1191,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - #[cfg(feature = "internal-lints")] - store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); @@ -1202,7 +1199,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); - store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), @@ -1333,6 +1329,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), ]); + #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), diff --git a/tests/dogfood.rs b/tests/dogfood.rs index eae25adf839..a6163a83d76 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -18,39 +18,26 @@ fn dogfood_clippy() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let output = if cfg!(feature = "internal-lints") { - // with internal lints and internal warnings - Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .args(&["--features", "internal-lints"]) - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .args(&["-D", "clippy::internal"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap() - } else { - // without internal lints or warnings - Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap() - }; + let mut command = Command::new(&*CLIPPY_PATH); + command + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + + // internal lints only exist if we build with the internal-lints feature + if cfg!(feature = "internal-lints") { + command.args(&["-D", "clippy::internal"]); + } + + let output = command.output().unwrap(); + println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); -- cgit 1.4.1-3-g733a5 From f059febe85f49fd7b432a24542321f9b948a49de Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 13 Nov 2020 12:13:50 -0600 Subject: Add redundant else lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/redundant_else.rs | 135 ++++++++++++++++++++++++++++++++ tests/ui/redundant_else.rs | 154 +++++++++++++++++++++++++++++++++++++ tests/ui/redundant_else.stderr | 80 +++++++++++++++++++ 5 files changed, 374 insertions(+) create mode 100644 clippy_lints/src/redundant_else.rs create mode 100644 tests/ui/redundant_else.rs create mode 100644 tests/ui/redundant_else.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e76a781f13b..c3351793c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2023,6 +2023,7 @@ Released 2018-09-13 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..66895a866ee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod question_mark; mod ranges; mod redundant_clone; mod redundant_closure_call; +mod redundant_else; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -810,6 +811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_closure_call::REDUNDANT_CLOSURE_CALL, + &redundant_else::REDUNDANT_ELSE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -1113,6 +1115,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); @@ -1294,6 +1297,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), + LintId::of(&redundant_else::REDUNDANT_ELSE), LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs new file mode 100644 index 00000000000..3d585cd27a3 --- /dev/null +++ b/clippy_lints/src/redundant_else.rs @@ -0,0 +1,135 @@ +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `else` blocks that can be removed without changing semantics. + /// + /// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity. + /// + /// **Known problems:** Some may prefer to keep the `else` block for clarity. + /// + /// **Example:** + /// + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } else { + /// print!("Moving on..."); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } + /// print!("Moving on..."); + /// } + /// ``` + pub REDUNDANT_ELSE, + pedantic, + "`else` branch that can be removed without changing semantics" +} + +declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]); + +impl EarlyLintPass for RedundantElse { + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) { + if in_external_macro(cx.sess, stmt.span) { + return; + } + // Only look at expressions that are a whole statement + let expr: &Expr = match &stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + _ => return, + }; + // if else + let (mut then, mut els): (&Block, &Expr) = match &expr.kind { + ExprKind::If(_, then, Some(els)) => (then, els), + _ => return, + }; + loop { + if !BreakVisitor::default().check_block(then) { + // then block does not always break + return; + } + match &els.kind { + // else if else + ExprKind::If(_, next_then, Some(next_els)) => { + then = next_then; + els = next_els; + continue; + }, + // else if without else + ExprKind::If(..) => return, + // done + _ => break, + } + } + span_lint_and_help( + cx, + REDUNDANT_ELSE, + els.span, + "redundant else block", + None, + "remove the `else` block and move the contents out", + ); + } +} + +/// Call `check` functions to check if an expression always breaks control flow +#[derive(Default)] +struct BreakVisitor { + is_break: bool, +} + +impl<'ast> Visitor<'ast> for BreakVisitor { + fn visit_block(&mut self, block: &'ast Block) { + self.is_break = match block.stmts.as_slice() { + [.., last] => self.check_stmt(last), + _ => false, + }; + } + + fn visit_expr(&mut self, expr: &'ast Expr) { + self.is_break = match expr.kind { + ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true, + ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)), + ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els), + ExprKind::If(_, _, None) + // ignore loops for simplicity + | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false, + _ => { + walk_expr(self, expr); + return; + }, + }; + } +} + +impl BreakVisitor { + fn check(&mut self, item: T, visit: fn(&mut Self, T)) -> bool { + visit(self, item); + std::mem::replace(&mut self.is_break, false) + } + + fn check_block(&mut self, block: &Block) -> bool { + self.check(block, Self::visit_block) + } + + fn check_expr(&mut self, expr: &Expr) -> bool { + self.check(expr, Self::visit_expr) + } + + fn check_stmt(&mut self, stmt: &Stmt) -> bool { + self.check(stmt, Self::visit_stmt) + } +} diff --git a/tests/ui/redundant_else.rs b/tests/ui/redundant_else.rs new file mode 100644 index 00000000000..737c8a9f8db --- /dev/null +++ b/tests/ui/redundant_else.rs @@ -0,0 +1,154 @@ +#![warn(clippy::redundant_else)] +#![allow(clippy::needless_return)] + +fn main() { + loop { + // break + if foo() { + println!("Love your neighbor;"); + break; + } else { + println!("yet don't pull down your hedge."); + } + // continue + if foo() { + println!("He that lies down with Dogs,"); + continue; + } else { + println!("shall rise up with fleas."); + } + // match block + if foo() { + match foo() { + 1 => break, + _ => return, + } + } else { + println!("You may delay, but time will not."); + } + } + // else if + if foo() { + return; + } else if foo() { + return; + } else { + println!("A fat kitchen makes a lean will."); + } + // let binding outside of block + let _ = { + if foo() { + return; + } else { + 1 + } + }; + // else if with let binding outside of block + let _ = { + if foo() { + return; + } else if foo() { + return; + } else { + 2 + } + }; + // inside if let + let _ = if let Some(1) = foo() { + let _ = 1; + if foo() { + return; + } else { + 1 + } + } else { + 1 + }; + + // + // non-lint cases + // + + // sanity check + if foo() { + let _ = 1; + } else { + println!("Who is wise? He that learns from every one."); + } + // else if without else + if foo() { + return; + } else if foo() { + foo() + }; + // nested if return + if foo() { + if foo() { + return; + } + } else { + foo() + }; + // match with non-breaking branch + if foo() { + match foo() { + 1 => foo(), + _ => return, + } + } else { + println!("Three may keep a secret, if two of them are dead."); + } + // let binding + let _ = if foo() { + return; + } else { + 1 + }; + // assign + let a; + a = if foo() { + return; + } else { + 1 + }; + // assign-op + a += if foo() { + return; + } else { + 1 + }; + // if return else if else + if foo() { + return; + } else if foo() { + 1 + } else { + 2 + }; + // if else if return else + if foo() { + 1 + } else if foo() { + return; + } else { + 2 + }; + // else if with let binding + let _ = if foo() { + return; + } else if foo() { + return; + } else { + 2 + }; + // inside function call + Box::new(if foo() { + return; + } else { + 1 + }); +} + +fn foo() -> T { + unimplemented!("I'm not Santa Claus") +} diff --git a/tests/ui/redundant_else.stderr b/tests/ui/redundant_else.stderr new file mode 100644 index 00000000000..9000cdc814b --- /dev/null +++ b/tests/ui/redundant_else.stderr @@ -0,0 +1,80 @@ +error: redundant else block + --> $DIR/redundant_else.rs:10:16 + | +LL | } else { + | ________________^ +LL | | println!("yet don't pull down your hedge."); +LL | | } + | |_________^ + | + = note: `-D clippy::redundant-else` implied by `-D warnings` + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:17:16 + | +LL | } else { + | ________________^ +LL | | println!("shall rise up with fleas."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:26:16 + | +LL | } else { + | ________________^ +LL | | println!("You may delay, but time will not."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:35:12 + | +LL | } else { + | ____________^ +LL | | println!("A fat kitchen makes a lean will."); +LL | | } + | |_____^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:42:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:52:16 + | +LL | } else { + | ________________^ +LL | | 2 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:61:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 70f6a2cae22cc1245ee62ca493f3027a76b3a381 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 13 Nov 2020 12:46:37 -0600 Subject: Eat redundant else dogfood --- clippy_lints/src/functions.rs | 13 +++++-------- clippy_lints/src/len_zero.rs | 3 +-- clippy_lints/src/matches.rs | 5 ++--- clippy_lints/src/methods/unnecessary_filter_map.rs | 5 ++--- clippy_lints/src/non_expressive_names.rs | 7 +++---- 5 files changed, 13 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 8b58d1f2601..fd93548b55c 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -405,13 +405,10 @@ impl<'tcx> Functions { break; } if in_comment { - match line.find("*/") { - Some(i) => { - line = &line[i + 2..]; - in_comment = false; - continue; - }, - None => break, + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; } } else { let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); @@ -423,8 +420,8 @@ impl<'tcx> Functions { in_comment = true; continue; } - break; } + break; } if code_in_line { line_count += 1; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8842901d90b..6fe53351090 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -222,9 +222,8 @@ fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplIte let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) { if cx.access_levels.is_exported(is_empty.id.hir_id) { return; - } else { - "a private" } + "a private" } else { "no corresponding" }; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 7d64fa6c262..5c3901e5b28 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -696,10 +696,9 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { // single statement/expr "else" block, don't lint return; - } else { - // block with 2+ statements or 1 expr and 1+ statement - Some(els) } + // block with 2+ statements or 1 expr and 1+ statement + Some(els) } else { // not a block, don't lint return; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 75e123eb593..d082a88cd2d 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -69,10 +69,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } } return (true, false); - } else { - // We don't know. It might do anything. - return (true, true); } + // We don't know. It might do anything. + return (true, true); } } (true, true) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 5b42b61fcde..446426b3e61 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -409,11 +409,10 @@ fn levenstein_not_1(a_name: &str, b_name: &str) -> bool { if let Some(b2) = b_chars.next() { // check if there's just one character inserted return a != b2 || a_chars.ne(b_chars); - } else { - // tuple - // ntuple - return true; } + // tuple + // ntuple + return true; } // for item in items true -- cgit 1.4.1-3-g733a5 From e7258ac714941ed5534ae31cddac7164ce1f50c1 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Mon, 30 Nov 2020 11:24:10 +0530 Subject: bump rustc-semver version. use in built method to compare versions --- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 45fd87b169f..9b8ff266f5c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -28,7 +28,7 @@ smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" -rustc-semver="1.0.0" +rustc-semver="1.1.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 9c5e55b1ed5..b2f16007e35 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -76,7 +76,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt } pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv >= lint_msrv) + msrv.map_or(true, |msrv| msrv.meets(*lint_msrv)) } macro_rules! extract_msrv_attr { -- cgit 1.4.1-3-g733a5 From 415394c3fc0e008026b3f2a37fbd57a58449b4d3 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Tue, 1 Dec 2020 09:44:43 +0000 Subject: Fix false positive in write_literal and print_literal due to numeric literals --- clippy_lints/src/write.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index ff414f748ef..78d23e1e0ef 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,7 +2,8 @@ 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 rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; +use if_chain::if_chain; +use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; @@ -442,7 +443,12 @@ impl Write { return (Some(fmtstr), None); }; match &token_expr.kind { - ExprKind::Lit(_) => { + ExprKind::Lit(lit) + if match lit.kind { + LitKind::Int(_, _) | LitKind::Float(_, _) => false, + _ => true, + } => + { let mut all_simple = true; let mut seen = false; for arg in &args { @@ -460,10 +466,16 @@ impl Write { span_lint(cx, lint, token_expr.span, "literal with an empty format string"); } idx += 1; - }, + } ExprKind::Assign(lhs, rhs, _) => { - if let ExprKind::Lit(_) = rhs.kind { - if let ExprKind::Path(_, p) = &lhs.kind { + if_chain! { + if let ExprKind::Lit(ref lit) = rhs.kind; + if match lit.kind { + LitKind::Int(_, _) | LitKind::Float(_, _) => false, + _ => true, + }; + if let ExprKind::Path(_, p) = &lhs.kind; + then { let mut all_simple = true; let mut seen = false; for arg in &args { -- cgit 1.4.1-3-g733a5 From 8135ab8a22f4aa6ad071574798397aa131377ca2 Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 16:11:52 -0500 Subject: Moved map_err_ignore to restriction and updated help message --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 5298e16a04d..324a11f140a 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -99,7 +99,7 @@ declare_clippy_lint! { /// } /// ``` pub MAP_ERR_IGNORE, - pedantic, + restriction, "`map_err` should not ignore the original error" } @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { body_span, "`map_err(|_|...` ignores the original error", None, - "Consider wrapping the error in an enum variant", + "Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error", ); } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 390d7ce2e4e..8193f7cfb8e 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider wrapping the error in an enum variant + = help: Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From f633ef6bba70b91f0415316fe10bb1f9c016ba33 Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 17:22:03 -0500 Subject: didn't update lint correctly --- clippy_lints/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 167e5b6b87f..013406347f2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1214,6 +1214,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&integer_division::INTEGER_DIVISION), LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(&mem_forget::MEM_FORGET), @@ -1280,7 +1281,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), LintId::of(&manual_ok_or::MANUAL_OK_OR), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_SAME_ARMS), -- cgit 1.4.1-3-g733a5 From 75482f917d7fc65190274bd577dcc2e30ba34aff Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 17:44:50 -0500 Subject: Apply suggestions from code review updated help message for the user Co-authored-by: Jane Lusby --- clippy_lints/src/map_err_ignore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 324a11f140a..f3c0515b9bc 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -133,9 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { cx, MAP_ERR_IGNORE, body_span, - "`map_err(|_|...` ignores the original error", + "`map_err(|_|...` wildcard pattern discards the original error", None, - "Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error", + "Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", ); } } -- cgit 1.4.1-3-g733a5 From b6113066429bc3108f62b920ccbfc79accfef2dd Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 27 Nov 2020 22:44:02 -0300 Subject: Add lint unsafe_sizeof_count_copies --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unsafe_sizeof_count_copies.rs | 98 ++++++++++++++++++ clippy_lints/src/utils/paths.rs | 4 + tests/ui/unsafe_sizeof_count_copies.rs | 54 ++++++++++ tests/ui/unsafe_sizeof_count_copies.stderr | 131 +++++++++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 clippy_lints/src/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/unsafe_sizeof_count_copies.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e65e7cc639f..e0f3b82ad25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2124,6 +2124,7 @@ Released 2018-09-13 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name +[`unsafe_sizeof_count_copies`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_sizeof_count_copies [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 167e5b6b87f..1bce0130b40 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -329,6 +329,7 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unsafe_sizeof_count_copies; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + &unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -998,6 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_late_pass(|| box unsafe_sizeof_count_copies::UnsafeSizeofCountCopies); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -1605,6 +1608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1883,6 +1887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs new file mode 100644 index 00000000000..2422df8feba --- /dev/null +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -0,0 +1,98 @@ +//! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee +//! count + +use crate::utils::{match_def_path, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Ty as TyM, TyS}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects expressions where + /// size_of:: is used as the count argument to unsafe + /// memory copying functions like ptr::copy and + /// ptr::copy_nonoverlapping where T is the pointee type + /// of the pointers used + /// + /// **Why is this bad?** These functions expect a count + /// of T and not a number of bytes, which can lead to + /// copying the incorrect amount of bytes, which can + /// result in Undefined Behaviour + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,no_run + /// # use std::ptr::copy_nonoverlapping; + /// # use std::mem::size_of; + /// + /// const SIZE: usize = 128; + /// let x = [2u8; SIZE]; + /// let mut y = [2u8; SIZE]; + /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + /// ``` + pub UNSAFE_SIZEOF_COUNT_COPIES, + correctness, + "unsafe memory copying using a byte count instead of a count of T" +} + +declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); + +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + match &expr.kind { + ExprKind::Call(ref count_func, _func_args) => { + if_chain! { + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) + || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next() + } else { + None + } + } + }, + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { + get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right)) + }, + _ => None, + } +} + +impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) + || match_def_path(cx, def_id, &paths::COPY); + + // Get the pointee type + let _substs = cx.typeck_results().node_substs(func.hir_id); + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + + // Find a size_of call in the count parameter expression and + // check that it's the same type + if let [_src, _dest, count] = &**func_args; + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count); + if TyS::same_type(pointee_ty, ty_used_for_size_of); + then { + span_lint_and_help( + cx, + UNSAFE_SIZEOF_COUNT_COPIES, + expr.span, + "unsafe memory copying using a byte count (Multiplied by size_of::) \ + instead of a count of T", + None, + "use a count of elements instead of a count of bytes for the count parameter, \ + it already gets multiplied by the size of the pointed to type" + ); + } + }; + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 16e6a016c9e..fe763d4bfbb 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -20,6 +20,8 @@ pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; +pub const COPY: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; +pub const COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; @@ -73,6 +75,8 @@ pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "Manua pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; +pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"]; +pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OPS_MODULE: [&str; 2] = ["core", "ops"]; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs new file mode 100644 index 00000000000..0077ed07fce --- /dev/null +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -0,0 +1,54 @@ +#![warn(clippy::unsafe_sizeof_count_copies)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{copy, copy_nonoverlapping}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count is size_of (Should trigger the lint) + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + // Count expression involving multiplication of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + + // Count expression involving nested multiplications of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; + + // Count expression involving divisions of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; + + // No size_of calls (Should not trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + // Different types for pointee and size_of (Should not trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; +} diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr new file mode 100644 index 00000000000..6804df8cdfc --- /dev/null +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -0,0 +1,131 @@ +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:14:14 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:15:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:17:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:21:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:24:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:25:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:29:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:31:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:35:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:38:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:39:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: aborting due to 16 previous errors + -- cgit 1.4.1-3-g733a5 From 0f954babef41b16e26b900d3858ceac4005a4506 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 01:47:32 -0300 Subject: Make the unsafe_sizeof_count_copies lint find copy_{from,to} method calls --- clippy_lints/src/unsafe_sizeof_count_copies.rs | 66 +++++++++++++++++++------- tests/ui/unsafe_sizeof_count_copies.rs | 5 ++ tests/ui/unsafe_sizeof_count_copies.stderr | 60 +++++++++++++++++------ 3 files changed, 99 insertions(+), 32 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs index 2422df8feba..5df7d72564e 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_hir::BinOpKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Ty as TyM, TyS}; +use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { match &expr.kind { ExprKind::Call(ref count_func, _func_args) => { if_chain! { @@ -62,35 +62,65 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let [_src, _dest, count] = &**args; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) + || match_def_path(cx, def_id, &paths::COPY); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; + if_chain! { + // Find calls to copy_{from,to}{,_nonoverlapping} + if let ExprKind::MethodCall(ref method_path, _, ref args, _) = expr.kind; + if let [ptr_self, _, count] = &**args; + let method_ident = method_path.ident.as_str(); + if method_ident== "copy_to" || method_ident == "copy_from" + || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; + + // Get the pointee type + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + cx.typeck_results().expr_ty(ptr_self).kind(); + then { + return Some((pointee_ty, count)); + } + }; + None +} + impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(ref func, ref func_args) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY); + const HELP_MSG: &str = "use a count of elements instead of a count of bytes \ + for the count parameter, it already gets multiplied by the size of the pointed to type"; + + const LINT_MSG: &str = "unsafe memory copying using a byte count \ + (Multiplied by size_of::) instead of a count of T"; - // Get the pointee type - let _substs = cx.typeck_results().node_substs(func.hir_id); - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + if_chain! { + // Find calls to unsafe copy functions and get + // the pointee type and count parameter expression + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); // Find a size_of call in the count parameter expression and // check that it's the same type - if let [_src, _dest, count] = &**func_args; - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count); + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); if TyS::same_type(pointee_ty, ty_used_for_size_of); then { span_lint_and_help( cx, UNSAFE_SIZEOF_COUNT_COPIES, expr.span, - "unsafe memory copying using a byte count (Multiplied by size_of::) \ - instead of a count of T", + LINT_MSG, None, - "use a count of elements instead of a count of bytes for the count parameter, \ - it already gets multiplied by the size of the pointed to type" + HELP_MSG ); } }; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 0077ed07fce..0bb22314cc0 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -14,6 +14,11 @@ fn main() { unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 6804df8cdfc..14ca04617c2 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -18,13 +18,45 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T --> $DIR/unsafe_sizeof_count_copies.rs:17:14 | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:19:14 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:20:14 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + --> $DIR/unsafe_sizeof_count_copies.rs:23:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:21:14 + --> $DIR/unsafe_sizeof_count_copies.rs:26:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +72,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + --> $DIR/unsafe_sizeof_count_copies.rs:27:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +80,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:24:14 + --> $DIR/unsafe_sizeof_count_copies.rs:29:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +88,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:25:14 + --> $DIR/unsafe_sizeof_count_copies.rs:30:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +96,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + --> $DIR/unsafe_sizeof_count_copies.rs:33:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +104,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:29:14 + --> $DIR/unsafe_sizeof_count_copies.rs:34:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +112,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:31:14 + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + --> $DIR/unsafe_sizeof_count_copies.rs:37:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZ = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:35:14 + --> $DIR/unsafe_sizeof_count_copies.rs:40:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 + --> $DIR/unsafe_sizeof_count_copies.rs:41:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:38:14 + --> $DIR/unsafe_sizeof_count_copies.rs:43:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:39:14 + --> $DIR/unsafe_sizeof_count_copies.rs:44:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors -- cgit 1.4.1-3-g733a5 From 1b80990fe01646868f85245f608203e23f64184a Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 14:23:59 -0300 Subject: Make the unsafe_sizeof_count_copies lint work with more functions Specifically: - find std::ptr::write_bytes - find std::ptr::swap_nonoverlapping - find std::ptr::slice_from_raw_parts - find std::ptr::slice_from_raw_parts_mut - pointer_primitive::write_bytes --- clippy_lints/src/unsafe_sizeof_count_copies.rs | 42 ++++++--- clippy_lints/src/utils/paths.rs | 4 + tests/ui/unsafe_sizeof_count_copies.rs | 12 ++- tests/ui/unsafe_sizeof_count_copies.stderr | 122 ++++++++++++++++--------- 4 files changed, 126 insertions(+), 54 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs index 5df7d72564e..8a4538091e7 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -41,8 +41,8 @@ declare_clippy_lint! { declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { - match &expr.kind { - ExprKind::Call(ref count_func, _func_args) => { + match expr.kind { + ExprKind::Call(count_func, _func_args) => { if_chain! { if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); @@ -56,7 +56,7 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right)) + get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) }, _ => None, } @@ -64,13 +64,16 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(ref func, ref args) = expr.kind; - if let [_src, _dest, count] = &**args; + // Find calls to ptr::{copy, copy_nonoverlapping} + // and ptr::{swap_nonoverlapping, write_bytes}, + if let ExprKind::Call(func, args) = expr.kind; + if let [_, _, count] = args; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY); + || match_def_path(cx, def_id, &paths::COPY) + || match_def_path(cx, def_id, &paths::WRITE_BYTES) + || match_def_path(cx, def_id, &paths::PTR_SWAP_NONOVERLAPPING); // Get the pointee type if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); @@ -79,11 +82,11 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - } }; if_chain! { - // Find calls to copy_{from,to}{,_nonoverlapping} - if let ExprKind::MethodCall(ref method_path, _, ref args, _) = expr.kind; - if let [ptr_self, _, count] = &**args; + // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; + if let [ptr_self, _, count] = args; let method_ident = method_path.ident.as_str(); - if method_ident== "copy_to" || method_ident == "copy_from" + if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from" || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; // Get the pointee type @@ -93,6 +96,21 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - return Some((pointee_ty, count)); } }; + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(func, args) = expr.kind; + if let [_data, count] = args; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS) + || match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS_MUT); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; None } @@ -102,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { for the count parameter, it already gets multiplied by the size of the pointed to type"; const LINT_MSG: &str = "unsafe memory copying using a byte count \ - (Multiplied by size_of::) instead of a count of T"; + (multiplied by size_of/size_of_val::) instead of a count of T"; if_chain! { // Find calls to unsafe copy functions and get diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index fe763d4bfbb..87c020a99db 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -104,6 +104,9 @@ pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"]; +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"]; +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; @@ -158,3 +161,4 @@ pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; +pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 0bb22314cc0..6aed8c31f7e 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -1,7 +1,9 @@ #![warn(clippy::unsafe_sizeof_count_copies)] use std::mem::{size_of, size_of_val}; -use std::ptr::{copy, copy_nonoverlapping}; +use std::ptr::{ + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, +}; fn main() { const SIZE: usize = 128; @@ -22,6 +24,14 @@ fn main() { unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + + unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 14ca04617c2..6f491bc4e4a 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -1,5 +1,5 @@ -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:14:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:16:14 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,157 +7,197 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:15:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:17:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:17:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:19:14 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:18:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:20:14 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:19:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:21:14 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:20:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:24:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:23:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:25:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:26:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:27:14 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:30:14 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + | +LL | unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:33:14 + | +LL | unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:27:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:37:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:29:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:39:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:30:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:40:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:33:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:43:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:34:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:44:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:46:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:37:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:47:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:40:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:50:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:41:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:51:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:43:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:53:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:44:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:54:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: aborting due to 20 previous errors +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From af9685bb1e7e27a7b21d9939a42c1e9dce8c4df5 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Dec 2020 20:55:38 -0300 Subject: Rename unsafe_sizeof_count_copies to size_of_in_element_count Also fix review comments: - Use const arrays and iterate them for the method/function names - merge 2 if_chain's into one using a rest pattern - remove unnecessary unsafe block in test And make the lint only point to the count expression instead of the entire function call --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/size_of_in_element_count.rs | 138 +++++++++++++++++++++++ clippy_lints/src/unsafe_sizeof_count_copies.rs | 146 ------------------------- tests/ui/size_of_in_element_count.rs | 50 +++++++++ tests/ui/size_of_in_element_count.stderr | 131 ++++++++++++++++++++++ tests/ui/unsafe_sizeof_count_copies.rs | 49 --------- tests/ui/unsafe_sizeof_count_copies.stderr | 131 ---------------------- 8 files changed, 325 insertions(+), 332 deletions(-) create mode 100644 clippy_lints/src/size_of_in_element_count.rs delete mode 100644 clippy_lints/src/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/size_of_in_element_count.rs create mode 100644 tests/ui/size_of_in_element_count.stderr delete mode 100644 tests/ui/unsafe_sizeof_count_copies.rs delete mode 100644 tests/ui/unsafe_sizeof_count_copies.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e0f3b82ad25..c7e02aaf4e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2057,6 +2057,7 @@ Released 2018-09-13 [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive @@ -2124,7 +2125,6 @@ Released 2018-09-13 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name -[`unsafe_sizeof_count_copies`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_sizeof_count_copies [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1bce0130b40..06961064a4b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -306,6 +306,7 @@ mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; +mod size_of_in_element_count; mod slow_vector_initialization; mod stable_sort_primitive; mod strings; @@ -329,7 +330,6 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; -mod unsafe_sizeof_count_copies; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -917,7 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES, + &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -1000,7 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); - store.register_late_pass(|| box unsafe_sizeof_count_copies::UnsafeSizeofCountCopies); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -1608,7 +1608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1887,7 +1887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs new file mode 100644 index 00000000000..9701e793700 --- /dev/null +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -0,0 +1,138 @@ +//! Lint on use of `size_of` or `size_of_val` of T in an expression +//! expecting a count of T + +use crate::utils::{match_def_path, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects expressions where + /// size_of:: or size_of_val:: is used as a + /// count of elements of type T + /// + /// **Why is this bad?** These functions expect a count + /// of T and not a number of bytes + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,no_run + /// # use std::ptr::copy_nonoverlapping; + /// # use std::mem::size_of; + /// + /// const SIZE: usize = 128; + /// let x = [2u8; SIZE]; + /// let mut y = [2u8; SIZE]; + /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + /// ``` + pub SIZE_OF_IN_ELEMENT_COUNT, + correctness, + "using size_of:: or size_of_val:: where a count of elements of T is expected" +} + +declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); + +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + match expr.kind { + ExprKind::Call(count_func, _func_args) => { + if_chain! { + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) + || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next() + } else { + None + } + } + }, + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { + get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) + }, + _ => None, + } +} + +const FUNCTIONS: [[&str; 3]; 6] = [ + paths::COPY_NONOVERLAPPING, + paths::COPY, + paths::WRITE_BYTES, + paths::PTR_SWAP_NONOVERLAPPING, + paths::PTR_SLICE_FROM_RAW_PARTS, + paths::PTR_SLICE_FROM_RAW_PARTS_MUT, + ]; +const METHODS: [&str; 5] = [ + "write_bytes", + "copy_to", + "copy_from", + "copy_to_nonoverlapping", + "copy_from_nonoverlapping", + ]; +fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Find calls to ptr::{copy, copy_nonoverlapping} + // and ptr::{swap_nonoverlapping, write_bytes}, + if let ExprKind::Call(func, args) = expr.kind; + if let [.., count] = args; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; + if_chain! { + // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; + if let [ptr_self, _, count] = args; + let method_ident = method_path.ident.as_str(); + if METHODS.iter().any(|m| *m == &*method_ident); + + // Get the pointee type + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + cx.typeck_results().expr_ty(ptr_self).kind(); + then { + return Some((pointee_ty, count)); + } + }; + None +} + +impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + const HELP_MSG: &str = "use a count of elements instead of a count of bytes\ + , it already gets multiplied by the size of the type"; + + const LINT_MSG: &str = "found a count of bytes \ + instead of a count of elements of T"; + + if_chain! { + // Find calls to unsafe copy functions and get + // the pointee type and count parameter expression + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); + + // Find a size_of call in the count parameter expression and + // check that it's the same type + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); + if TyS::same_type(pointee_ty, ty_used_for_size_of); + then { + span_lint_and_help( + cx, + SIZE_OF_IN_ELEMENT_COUNT, + count_expr.span, + LINT_MSG, + None, + HELP_MSG + ); + } + }; + } +} diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs deleted file mode 100644 index 8a4538091e7..00000000000 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee -//! count - -use crate::utils::{match_def_path, paths, span_lint_and_help}; -use if_chain::if_chain; -use rustc_hir::BinOpKind; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Detects expressions where - /// size_of:: is used as the count argument to unsafe - /// memory copying functions like ptr::copy and - /// ptr::copy_nonoverlapping where T is the pointee type - /// of the pointers used - /// - /// **Why is this bad?** These functions expect a count - /// of T and not a number of bytes, which can lead to - /// copying the incorrect amount of bytes, which can - /// result in Undefined Behaviour - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,no_run - /// # use std::ptr::copy_nonoverlapping; - /// # use std::mem::size_of; - /// - /// const SIZE: usize = 128; - /// let x = [2u8; SIZE]; - /// let mut y = [2u8; SIZE]; - /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - /// ``` - pub UNSAFE_SIZEOF_COUNT_COPIES, - correctness, - "unsafe memory copying using a byte count instead of a count of T" -} - -declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); - -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { - match expr.kind { - ExprKind::Call(count_func, _func_args) => { - if_chain! { - if let ExprKind::Path(ref count_func_qpath) = count_func.kind; - if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) - || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); - then { - cx.typeck_results().node_substs(count_func.hir_id).types().next() - } else { - None - } - } - }, - ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { - get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) - }, - _ => None, - } -} - -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - if_chain! { - // Find calls to ptr::{copy, copy_nonoverlapping} - // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, args) = expr.kind; - if let [_, _, count] = args; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY) - || match_def_path(cx, def_id, &paths::WRITE_BYTES) - || match_def_path(cx, def_id, &paths::PTR_SWAP_NONOVERLAPPING); - - // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } - }; - if_chain! { - // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; - if let [ptr_self, _, count] = args; - let method_ident = method_path.ident.as_str(); - if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from" - || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; - - // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = - cx.typeck_results().expr_ty(ptr_self).kind(); - then { - return Some((pointee_ty, count)); - } - }; - if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(func, args) = expr.kind; - if let [_data, count] = args; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS) - || match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS_MUT); - - // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } - }; - None -} - -impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - const HELP_MSG: &str = "use a count of elements instead of a count of bytes \ - for the count parameter, it already gets multiplied by the size of the pointed to type"; - - const LINT_MSG: &str = "unsafe memory copying using a byte count \ - (multiplied by size_of/size_of_val::) instead of a count of T"; - - if_chain! { - // Find calls to unsafe copy functions and get - // the pointee type and count parameter expression - if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); - - // Find a size_of call in the count parameter expression and - // check that it's the same type - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); - if TyS::same_type(pointee_ty, ty_used_for_size_of); - then { - span_lint_and_help( - cx, - UNSAFE_SIZEOF_COUNT_COPIES, - expr.span, - LINT_MSG, - None, - HELP_MSG - ); - } - }; - } -} diff --git a/tests/ui/size_of_in_element_count.rs b/tests/ui/size_of_in_element_count.rs new file mode 100644 index 00000000000..d4658ebf72d --- /dev/null +++ b/tests/ui/size_of_in_element_count.rs @@ -0,0 +1,50 @@ +#![warn(clippy::size_of_in_element_count)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{ + copy, copy_nonoverlapping, slice_from_raw_parts, + slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, +}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count is size_of (Should trigger the lint) + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + + slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + + // Count expression involving multiplication of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + + // Count expression involving nested multiplications of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + + // Count expression involving divisions of size_of (Should trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + + // No size_of calls (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + // Different types for pointee and size_of (Should not trigger the lint) + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; +} diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr new file mode 100644 index 00000000000..80c3fec1b05 --- /dev/null +++ b/tests/ui/size_of_in_element_count.stderr @@ -0,0 +1,131 @@ +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:17:68 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:18:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:20:49 + | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:21:64 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:22:51 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:23:66 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:25:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:26:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:28:46 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:29:47 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:31:66 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:33:46 + | +LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:34:38 + | +LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:37:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:40:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:43:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 16 previous errors + diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs deleted file mode 100644 index 2a9adeb6bd9..00000000000 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![warn(clippy::unsafe_sizeof_count_copies)] - -use std::mem::{size_of, size_of_val}; -use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, -}; - -fn main() { - const SIZE: usize = 128; - const HALF_SIZE: usize = SIZE / 2; - const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; - - // Count is size_of (Should trigger the lint) - unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - - unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - - unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - - unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - - // Count expression involving multiplication of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - - // Count expression involving nested multiplications of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - - // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - - // No size_of calls (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - // Different types for pointee and size_of (Should not trigger the lint) - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; -} diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr deleted file mode 100644 index 7989e96dd21..00000000000 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ /dev/null @@ -1,131 +0,0 @@ -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:16:14 - | -LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:17:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:19:14 - | -LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:20:14 - | -LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:21:14 - | -LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 - | -LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:24:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:25:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:27:14 - | -LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:28:14 - | -LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:30:14 - | -LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:32:14 - | -LL | unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:33:14 - | -LL | unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:39:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:42:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: aborting due to 16 previous errors - -- cgit 1.4.1-3-g733a5 From c1a5329475d041dbeb077ecda6ae71f690b4bcc1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Nov 2020 21:54:50 -0300 Subject: Add more functions to size_of_in_element_count Specifically ptr::{sub, wrapping_sub, add, wrapping_add, offset, wrapping_offset} and slice::{from_raw_parts, from_raw_parts_mut} The lint now also looks for size_of calls through casts (Since offset takes an isize) --- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/size_of_in_element_count.rs | 48 ++++++++------ clippy_lints/src/utils/paths.rs | 2 + tests/ui/size_of_in_element_count.rs | 15 ++++- tests/ui/size_of_in_element_count.stderr | 98 +++++++++++++++++++++++----- 5 files changed, 127 insertions(+), 42 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 06961064a4b..4ef595bcffd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -848,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, + &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, @@ -917,7 +918,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -1562,6 +1562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), @@ -1608,7 +1609,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1872,6 +1872,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1887,7 +1888,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 9701e793700..210cf5773e1 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -54,31 +54,40 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) }, + ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr), _ => None, } } -const FUNCTIONS: [[&str; 3]; 6] = [ - paths::COPY_NONOVERLAPPING, - paths::COPY, - paths::WRITE_BYTES, - paths::PTR_SWAP_NONOVERLAPPING, - paths::PTR_SLICE_FROM_RAW_PARTS, - paths::PTR_SLICE_FROM_RAW_PARTS_MUT, +fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + const FUNCTIONS: [&[&str]; 8] = [ + &paths::COPY_NONOVERLAPPING, + &paths::COPY, + &paths::WRITE_BYTES, + &paths::PTR_SWAP_NONOVERLAPPING, + &paths::PTR_SLICE_FROM_RAW_PARTS, + &paths::PTR_SLICE_FROM_RAW_PARTS_MUT, + &paths::SLICE_FROM_RAW_PARTS, + &paths::SLICE_FROM_RAW_PARTS_MUT, ]; -const METHODS: [&str; 5] = [ - "write_bytes", - "copy_to", - "copy_from", - "copy_to_nonoverlapping", - "copy_from_nonoverlapping", + const METHODS: [&str; 11] = [ + "write_bytes", + "copy_to", + "copy_from", + "copy_to_nonoverlapping", + "copy_from_nonoverlapping", + "add", + "wrapping_add", + "sub", + "wrapping_sub", + "offset", + "wrapping_offset", ]; -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { // Find calls to ptr::{copy, copy_nonoverlapping} // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, args) = expr.kind; - if let [.., count] = args; + if let ExprKind::Call(func, [.., count]) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); @@ -91,13 +100,12 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - }; if_chain! { // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; - if let [ptr_self, _, count] = args; + if let ExprKind::MethodCall(method_path, _, [ptr_self, .., count], _) = expr.kind; let method_ident = method_path.ident.as_str(); if METHODS.iter().any(|m| *m == &*method_ident); // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = cx.typeck_results().expr_ty(ptr_self).kind(); then { return Some((pointee_ty, count)); @@ -115,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { instead of a count of elements of T"; if_chain! { - // Find calls to unsafe copy functions and get + // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 87c020a99db..6fdc7b4587f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -128,6 +128,8 @@ pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGu pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; diff --git a/tests/ui/size_of_in_element_count.rs b/tests/ui/size_of_in_element_count.rs index d4658ebf72d..b13e390705a 100644 --- a/tests/ui/size_of_in_element_count.rs +++ b/tests/ui/size_of_in_element_count.rs @@ -1,10 +1,11 @@ #![warn(clippy::size_of_in_element_count)] +#![allow(clippy::ptr_offset_with_cast)] use std::mem::{size_of, size_of_val}; use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, - slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, }; +use std::slice::{from_raw_parts, from_raw_parts_mut}; fn main() { const SIZE: usize = 128; @@ -33,6 +34,16 @@ fn main() { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + + unsafe { y.as_mut_ptr().sub(size_of::()) }; + y.as_ptr().wrapping_sub(size_of::()); + unsafe { y.as_ptr().add(size_of::()) }; + y.as_mut_ptr().wrapping_add(size_of::()); + unsafe { y.as_ptr().offset(size_of::() as isize) }; + y.as_mut_ptr().wrapping_offset(size_of::() as isize); + // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr index 80c3fec1b05..b7f421ec997 100644 --- a/tests/ui/size_of_in_element_count.stderr +++ b/tests/ui/size_of_in_element_count.stderr @@ -1,5 +1,5 @@ error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:17:68 + --> $DIR/size_of_in_element_count.rs:18:68 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:18:62 + --> $DIR/size_of_in_element_count.rs:19:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:20:49 + --> $DIR/size_of_in_element_count.rs:21:49 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:21:64 + --> $DIR/size_of_in_element_count.rs:22:64 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of:: $DIR/size_of_in_element_count.rs:22:51 + --> $DIR/size_of_in_element_count.rs:23:51 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:23:66 + --> $DIR/size_of_in_element_count.rs:24:66 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::< = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:25:47 + --> $DIR/size_of_in_element_count.rs:26:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:26:47 + --> $DIR/size_of_in_element_count.rs:27:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:28:46 + --> $DIR/size_of_in_element_count.rs:29:46 | LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:29:47 + --> $DIR/size_of_in_element_count.rs:30:47 | LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:31:66 + --> $DIR/size_of_in_element_count.rs:32:66 | LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::< = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:33:46 + --> $DIR/size_of_in_element_count.rs:34:46 | LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:34:38 + --> $DIR/size_of_in_element_count.rs:35:38 | LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,71 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:37:62 + --> $DIR/size_of_in_element_count.rs:37:49 + | +LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:38:41 + | +LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:40:33 + | +LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:41:29 + | +LL | y.as_ptr().wrapping_sub(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:42:29 + | +LL | unsafe { y.as_ptr().add(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:43:33 + | +LL | y.as_mut_ptr().wrapping_add(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:44:32 + | +LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:45:36 + | +LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:48:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +176,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:40:62 + --> $DIR/size_of_in_element_count.rs:51:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +184,12 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:43:47 + --> $DIR/size_of_in_element_count.rs:54:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: aborting due to 16 previous errors +error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 01f32116028a127b4a946c72b8ed5de3e03be477 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 4 Dec 2020 10:01:09 +0100 Subject: Turn unnecessary_wraps applicability to MaybeIncorrect --- clippy_lints/src/unnecessary_wraps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 360df2a6752..5d801511a0b 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { diag.multipart_suggestion( "...and change the returning expressions", suggs, - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); }, ); -- cgit 1.4.1-3-g733a5 From 75140e813f3701e76ab64e091653395ec397f68d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 4 Dec 2020 23:36:07 +0900 Subject: Fix a style of texts in `size_of_in_element_count` --- clippy_lints/src/size_of_in_element_count.rs | 11 +++---- tests/ui/size_of_in_element_count.stderr | 48 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 30 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 210cf5773e1..ea7a76146f5 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -11,11 +11,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Detects expressions where - /// size_of:: or size_of_val:: is used as a - /// count of elements of type T + /// `size_of::` or `size_of_val::` is used as a + /// count of elements of type `T` /// /// **Why is this bad?** These functions expect a count - /// of T and not a number of bytes + /// of `T` and not a number of bytes /// /// **Known problems:** None. /// @@ -23,7 +23,6 @@ declare_clippy_lint! { /// ```rust,no_run /// # use std::ptr::copy_nonoverlapping; /// # use std::mem::size_of; - /// /// const SIZE: usize = 128; /// let x = [2u8; SIZE]; /// let mut y = [2u8; SIZE]; @@ -31,7 +30,7 @@ declare_clippy_lint! { /// ``` pub SIZE_OF_IN_ELEMENT_COUNT, correctness, - "using size_of:: or size_of_val:: where a count of elements of T is expected" + "using `size_of::` or `size_of_val::` where a count of elements of `T` is expected" } declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); @@ -120,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { , it already gets multiplied by the size of the type"; const LINT_MSG: &str = "found a count of bytes \ - instead of a count of elements of T"; + instead of a count of elements of `T`"; if_chain! { // Find calls to functions with an element count parameter and get diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr index b7f421ec997..8cf3612abda 100644 --- a/tests/ui/size_of_in_element_count.stderr +++ b/tests/ui/size_of_in_element_count.stderr @@ -1,4 +1,4 @@ -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:18:68 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; @@ -7,7 +7,7 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:19:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; @@ -15,7 +15,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:21:49 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; @@ -23,7 +23,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:22:64 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; @@ -31,7 +31,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of:: $DIR/size_of_in_element_count.rs:23:51 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; @@ -39,7 +39,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:24:66 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; @@ -47,7 +47,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::< | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:26:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; @@ -55,7 +55,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:27:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; @@ -63,7 +63,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:29:46 | LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; @@ -71,7 +71,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:30:47 | LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; @@ -79,7 +79,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:32:66 | LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; @@ -87,7 +87,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::< | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:34:46 | LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); @@ -95,7 +95,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:35:38 | LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); @@ -103,7 +103,7 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:37:49 | LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; @@ -111,7 +111,7 @@ LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:38:41 | LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; @@ -119,7 +119,7 @@ LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:40:33 | LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; @@ -127,7 +127,7 @@ LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:41:29 | LL | y.as_ptr().wrapping_sub(size_of::()); @@ -135,7 +135,7 @@ LL | y.as_ptr().wrapping_sub(size_of::()); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:42:29 | LL | unsafe { y.as_ptr().add(size_of::()) }; @@ -143,7 +143,7 @@ LL | unsafe { y.as_ptr().add(size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:43:33 | LL | y.as_mut_ptr().wrapping_add(size_of::()); @@ -151,7 +151,7 @@ LL | y.as_mut_ptr().wrapping_add(size_of::()); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:44:32 | LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; @@ -159,7 +159,7 @@ LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:45:36 | LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); @@ -167,7 +167,7 @@ LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:48:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; @@ -175,7 +175,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:51:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; @@ -183,7 +183,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:54:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; -- cgit 1.4.1-3-g733a5 From 6edd59885605d2cf0aa8727cf2cd30cd13098804 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 4 Dec 2020 21:26:47 +0000 Subject: Added a lint-fraction-readability flag to the configuration --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/literal_representation.rs | 37 ++++++++++++++----- clippy_lints/src/utils/conf.rs | 2 ++ tests/ui-toml/lint_decimal_readability/clippy.toml | 1 + tests/ui-toml/lint_decimal_readability/test.rs | 22 ++++++++++++ tests/ui-toml/lint_decimal_readability/test.stderr | 10 ++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/unreadable_literal.fixed | 18 +++++++--- tests/ui/unreadable_literal.rs | 18 +++++++--- tests/ui/unreadable_literal.stderr | 42 ++++++++++++---------- 10 files changed, 118 insertions(+), 37 deletions(-) create mode 100644 tests/ui-toml/lint_decimal_readability/clippy.toml create mode 100644 tests/ui-toml/lint_decimal_readability/test.rs create mode 100644 tests/ui-toml/lint_decimal_readability/test.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a861a34aeb7..58e91dd32bd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,7 +1138,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); + let literal_representation_lint_fraction_readability = conf.lint_fraction_readability; + store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); let enum_variant_name_threshold = conf.enum_variant_name_threshold; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index e8a741683da..3920e5b6e83 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -11,7 +11,7 @@ use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// **What it does:** Warns if a long integral or floating-point constant does @@ -32,7 +32,7 @@ declare_clippy_lint! { /// ``` pub UNREADABLE_LITERAL, pedantic, - "long integer literal without underscores" + "long literal without underscores" } declare_clippy_lint! { @@ -208,7 +208,13 @@ impl WarningType { } } -declare_lint_pass!(LiteralDigitGrouping => [ +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct LiteralDigitGrouping { + lint_fraction_readability: bool, +} + +impl_lint_pass!(LiteralDigitGrouping => [ UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, @@ -223,7 +229,7 @@ impl EarlyLintPass for LiteralDigitGrouping { } if let ExprKind::Lit(ref lit) = expr.kind { - Self::check_lit(cx, lit) + self.check_lit(cx, lit) } } } @@ -232,7 +238,13 @@ impl EarlyLintPass for LiteralDigitGrouping { const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; impl LiteralDigitGrouping { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + pub fn new(lint_fraction_readability: bool) -> Self { + Self { + lint_fraction_readability, + } + } + + fn check_lit(&self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); @@ -247,9 +259,12 @@ impl LiteralDigitGrouping { let result = (|| { - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?; + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?; + let fractional_group_size = Self::get_group_size( + fraction.rsplit('_'), + num_lit.radix, + self.lint_fraction_readability)?; let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, @@ -363,7 +378,11 @@ impl LiteralDigitGrouping { /// Returns the size of the digit groups (or None if ungrouped) if successful, /// otherwise returns a `WarningType` for linting. - fn get_group_size<'a>(groups: impl Iterator, radix: Radix) -> Result, WarningType> { + fn get_group_size<'a>( + groups: impl Iterator, + radix: Radix, + lint_unreadable: bool, + ) -> Result, WarningType> { let mut groups = groups.map(str::len); let first = groups.next().expect("At least one group"); @@ -380,7 +399,7 @@ impl LiteralDigitGrouping { } else { Ok(Some(second)) } - } else if first > 5 { + } else if first > 5 && lint_unreadable { Err(WarningType::UnreadableLiteral) } else { Ok(None) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index fc6304118d9..c9d5f781f1b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -170,6 +170,8 @@ define_Conf! { (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), + /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. + (lint_fraction_readability, "lint_fraction_readability": bool, true), } impl Default for Conf { diff --git a/tests/ui-toml/lint_decimal_readability/clippy.toml b/tests/ui-toml/lint_decimal_readability/clippy.toml new file mode 100644 index 00000000000..635e282dc06 --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/clippy.toml @@ -0,0 +1 @@ +lint-fraction-readability = false \ No newline at end of file diff --git a/tests/ui-toml/lint_decimal_readability/test.rs b/tests/ui-toml/lint_decimal_readability/test.rs new file mode 100644 index 00000000000..9377eb69b23 --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/test.rs @@ -0,0 +1,22 @@ +#[deny(clippy::unreadable_literal)] + +fn allow_inconsistent_digit_grouping() { + #![allow(clippy::inconsistent_digit_grouping)] + let _pass1 = 100_200_300.123456789; +} + +fn main() { + allow_inconsistent_digit_grouping(); + + let _pass1 = 100_200_300.100_200_300; + let _pass2 = 1.123456789; + let _pass3 = 1.0; + let _pass4 = 10000.00001; + let _pass5 = 1.123456789e1; + + // due to clippy::inconsistent-digit-grouping + let _fail1 = 100_200_300.123456789; + + // fail due to the integer part + let _fail2 = 100200300.300200100; +} diff --git a/tests/ui-toml/lint_decimal_readability/test.stderr b/tests/ui-toml/lint_decimal_readability/test.stderr new file mode 100644 index 00000000000..9119ef19a7b --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/test.stderr @@ -0,0 +1,10 @@ +error: digits grouped inconsistently by underscores + --> $DIR/test.rs:18:18 + | +LL | let _fail1 = 100_200_300.123456789; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index af3d9ecf6e8..eff4da7e658 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `lint-fraction-readability`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 4043d53299f..c2e38037add 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -10,6 +10,14 @@ macro_rules! foo { }; } +struct Bar(f32); + +macro_rules! bar { + () => { + Bar(100200300400.100200300400500) + }; +} + fn main() { let _good = ( 0b1011_i64, @@ -26,10 +34,12 @@ fn main() { let _good_sci = 1.1234e1; let _bad_sci = 1.123_456e1; - let _fail9 = 0x00ab_cdef; - let _fail10: u32 = 0xBAFE_BAFE; - let _fail11 = 0x0abc_deff; - let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail1 = 0x00ab_cdef; + let _fail2: u32 = 0xBAFE_BAFE; + let _fail3 = 0x0abc_deff; + let _fail4: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail5 = 1.100_300_400; let _ = foo!(); + let _ = bar!(); } diff --git a/tests/ui/unreadable_literal.rs b/tests/ui/unreadable_literal.rs index e658a5f28c9..8296945b25e 100644 --- a/tests/ui/unreadable_literal.rs +++ b/tests/ui/unreadable_literal.rs @@ -10,6 +10,14 @@ macro_rules! foo { }; } +struct Bar(f32); + +macro_rules! bar { + () => { + Bar(100200300400.100200300400500) + }; +} + fn main() { let _good = ( 0b1011_i64, @@ -26,10 +34,12 @@ fn main() { let _good_sci = 1.1234e1; let _bad_sci = 1.123456e1; - let _fail9 = 0xabcdef; - let _fail10: u32 = 0xBAFEBAFE; - let _fail11 = 0xabcdeff; - let _fail12: i128 = 0xabcabcabcabcabcabc; + let _fail1 = 0xabcdef; + let _fail2: u32 = 0xBAFEBAFE; + let _fail3 = 0xabcdeff; + let _fail4: i128 = 0xabcabcabcabcabcabc; + let _fail5 = 1.100300400; let _ = foo!(); + let _ = bar!(); } diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 8645cabeabb..8436aac17ac 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,5 +1,5 @@ error: digits of hex or binary literal not grouped by four - --> $DIR/unreadable_literal.rs:17:9 + --> $DIR/unreadable_literal.rs:25:9 | LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` @@ -7,7 +7,7 @@ LL | 0x1_234_567, = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:17 + --> $DIR/unreadable_literal.rs:33:17 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` @@ -15,52 +15,58 @@ LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); = note: `-D clippy::unreadable-literal` implied by `-D warnings` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:31 + --> $DIR/unreadable_literal.rs:33:31 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:49 + --> $DIR/unreadable_literal.rs:33:49 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^ help: consider: `123_456_f32` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:61 + --> $DIR/unreadable_literal.rs:33:61 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:27:20 + --> $DIR/unreadable_literal.rs:35:20 | LL | let _bad_sci = 1.123456e1; | ^^^^^^^^^^ help: consider: `1.123_456e1` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:29:18 + --> $DIR/unreadable_literal.rs:37:18 | -LL | let _fail9 = 0xabcdef; +LL | let _fail1 = 0xabcdef; | ^^^^^^^^ help: consider: `0x00ab_cdef` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:30:24 + --> $DIR/unreadable_literal.rs:38:23 | -LL | let _fail10: u32 = 0xBAFEBAFE; - | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` +LL | let _fail2: u32 = 0xBAFEBAFE; + | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:31:19 + --> $DIR/unreadable_literal.rs:39:18 | -LL | let _fail11 = 0xabcdeff; - | ^^^^^^^^^ help: consider: `0x0abc_deff` +LL | let _fail3 = 0xabcdeff; + | ^^^^^^^^^ help: consider: `0x0abc_deff` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:32:25 + --> $DIR/unreadable_literal.rs:40:24 | -LL | let _fail12: i128 = 0xabcabcabcabcabcabc; - | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` +LL | let _fail4: i128 = 0xabcabcabcabcabcabc; + | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` -error: aborting due to 10 previous errors +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:41:18 + | +LL | let _fail5 = 1.100300400; + | ^^^^^^^^^^^ help: consider: `1.100_300_400` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 18383c69c1ebf0b6bc0f1ecdeee062f0767a69e8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 4 Dec 2020 22:05:52 +0000 Subject: Updated code for CI --- clippy_lints/src/literal_representation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 3920e5b6e83..87a957a9bd2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -244,7 +244,7 @@ impl LiteralDigitGrouping { } } - fn check_lit(&self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); -- cgit 1.4.1-3-g733a5 From 898b7c594cfdf1eb56d24a4c6fa02678cf5029a8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 5 Dec 2020 20:59:53 +0000 Subject: Renamed the configuraiton to unreadable-literal-lint-fractions --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/lint_decimal_readability/clippy.toml | 2 +- tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58e91dd32bd..2b99ed570b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,7 +1138,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - let literal_representation_lint_fraction_readability = conf.lint_fraction_readability; + let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c9d5f781f1b..6403ff6dad1 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -171,7 +171,7 @@ define_Conf! { /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. - (lint_fraction_readability, "lint_fraction_readability": bool, true), + (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true), } impl Default for Conf { diff --git a/tests/ui-toml/lint_decimal_readability/clippy.toml b/tests/ui-toml/lint_decimal_readability/clippy.toml index 635e282dc06..6feaf7d5c0c 100644 --- a/tests/ui-toml/lint_decimal_readability/clippy.toml +++ b/tests/ui-toml/lint_decimal_readability/clippy.toml @@ -1 +1 @@ -lint-fraction-readability = false \ No newline at end of file +unreadable-literal-lint-fractions = false \ No newline at end of file diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index eff4da7e658..7b3c476461d 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `lint-fraction-readability`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From e90b977a082eac4283a52b53d4da3a507a8ee9a0 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 22 Nov 2020 22:04:18 +0900 Subject: Fix FP in `unnecessary_lazy_evaluations` --- clippy_lints/src/utils/usage.rs | 21 ++++++++++++++------- tests/ui/unnecessary_lazy_eval_unfixable.rs | 4 ++++ 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index a7d0ea6ccfb..fc0db7f64ec 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector { } impl<'tcx> ParamBindingIdCollector { fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { - let mut finder = ParamBindingIdCollector { - binding_hir_ids: Vec::new(), - }; - finder.visit_body(body); - finder.binding_hir_ids + let mut hir_ids: Vec = Vec::new(); + for param in body.params.iter() { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_param(param); + for hir_id in &finder.binding_hir_ids { + hir_ids.push(*hir_id); + } + } + hir_ids } } impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { type Map = Map<'tcx>; - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.binding_hir_ids.push(hir_id); } + intravisit::walk_pat(self, pat); } fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs index 2e923bc97a2..b05dd143bfd 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -15,4 +15,8 @@ fn main() { } let _ = Ok(1).unwrap_or_else(|e::E| 2); let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); + + // Fix #6343 + let arr = [(Some(1),)]; + Some(&0).and_then(|&i| arr[i].0); } -- cgit 1.4.1-3-g733a5 From ba1249465c036b2ccc6daf34749ea9d5df77f358 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Dec 2020 16:45:10 +0900 Subject: cargo dev fmt --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 7 ++++--- clippy_lints/src/pass_by_ref_or_value.rs | 7 ++++--- clippy_lints/src/redundant_clone.rs | 5 ++++- clippy_lints/src/types.rs | 4 +++- clippy_lints/src/unnecessary_wraps.rs | 5 ++++- clippy_lints/src/utils/ast_utils.rs | 5 ++++- clippy_lints/src/utils/mod.rs | 2 +- 9 files changed, 26 insertions(+), 13 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index edecba57e44..55e4755c278 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -480,7 +480,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { | ItemKind::ForeignMod(..) => return false, // We found a main function ... ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { - let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 25245b3dbf0..38e2ce563ee 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let has_const_generic_params = generics .params .iter() - .any(|param| matches!(param.kind, GenericParamKind::Const{ .. })); + .any(|param| matches!(param.kind, GenericParamKind::Const { .. })); if already_const(header) || has_const_generic_params { return; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 532c0266946..5043b7b1fc3 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -90,9 +90,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index f03facc235e..6a17d654ac9 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -244,9 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index f0e507105a6..06adbb523d7 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -390,7 +390,10 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { let local = place.local; if local == self.used.0 - && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)) + && !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { self.used.1 = true; } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 74ba53e6a9a..fd74783335d 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1104,7 +1104,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], expr: None, .. + stmts: &[], + expr: None, + .. }, _, ) diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5d801511a0b..e763da593d4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -74,7 +74,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 31b4e25411b..f0267e4c792 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -408,7 +408,10 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { - matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))) + matches!( + (l, r), + (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) + ) } pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a6b64c90e8..007e72d129f 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1500,7 +1500,7 @@ pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. }) + matches!(item.kind, ItemKind::Impl { of_trait: Some(_), .. }) } else { false } -- cgit 1.4.1-3-g733a5 From b81141cfb91cbf39e87e32a27530537f18e85405 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 22 Nov 2020 19:02:57 +0100 Subject: Add lint print_stderr Resolves #6348 Almost identical to print_stdout, this lint applies to the `eprintln!` and `eprint!` macros rather than `println!` and `print!`. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/write.rs | 23 +++++++++++++++++++++++ tests/ui/print_stderr.rs | 6 ++++++ tests/ui/print_stderr.stderr | 16 ++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 tests/ui/print_stderr.rs create mode 100644 tests/ui/print_stderr.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e02aaf4e1..1f2c4f310de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2006,6 +2006,7 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr [`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2b99ed570b1..0e630a352fe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -934,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &wildcard_imports::WILDCARD_IMPORTS, &write::PRINTLN_EMPTY_STRING, &write::PRINT_LITERAL, + &write::PRINT_STDERR, &write::PRINT_STDOUT, &write::PRINT_WITH_NEWLINE, &write::USE_DEBUG, @@ -1247,6 +1248,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(&write::PRINT_STDERR), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), ]); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index ff414f748ef..2248bc1e32e 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -75,6 +75,24 @@ declare_clippy_lint! { "printing on stdout" } +declare_clippy_lint! { + /// **What it does:** Checks for printing on *stderr*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// **Why is this bad?** People often print on *stderr* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// **Known problems:** Only catches `eprint!` and `eprintln!` calls. + /// + /// **Example:** + /// ```rust + /// eprintln!("Hello world!"); + /// ``` + pub PRINT_STDERR, + restriction, + "printing on stderr" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `Debug` formatting. The purpose of this /// lint is to catch debugging remnants. @@ -201,6 +219,7 @@ impl_lint_pass!(Write => [ PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, PRINT_STDOUT, + PRINT_STDERR, USE_DEBUG, PRINT_LITERAL, WRITE_WITH_NEWLINE, @@ -260,6 +279,10 @@ impl EarlyLintPass for Write { ); } } + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + } else if mac.path == sym!(eprint) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs new file mode 100644 index 00000000000..e53f46b1c2f --- /dev/null +++ b/tests/ui/print_stderr.rs @@ -0,0 +1,6 @@ +#![warn(clippy::print_stderr)] + +fn main() { + eprintln!("Hello"); + eprint!("World"); +} diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr new file mode 100644 index 00000000000..7252fce72c6 --- /dev/null +++ b/tests/ui/print_stderr.stderr @@ -0,0 +1,16 @@ +error: use of `eprintln!` + --> $DIR/print_stderr.rs:4:5 + | +LL | eprintln!("Hello"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stderr` implied by `-D warnings` + +error: use of `eprint!` + --> $DIR/print_stderr.rs:5:5 + | +LL | eprint!("World"); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 7063c36c912990bd67327a41445706a451fe5b48 Mon Sep 17 00:00:00 2001 From: Josias Date: Fri, 4 Dec 2020 15:39:09 +0100 Subject: Add eprint! to print_with_newline lint --- clippy_lints/src/write.rs | 20 ++++++ tests/ui/eprint_with_newline.rs | 49 +++++++++++++++ tests/ui/eprint_with_newline.stderr | 121 ++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 tests/ui/eprint_with_newline.rs create mode 100644 tests/ui/eprint_with_newline.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 2248bc1e32e..0fd56f78572 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -283,6 +283,26 @@ impl EarlyLintPass for Write { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); } else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + "using `eprint!()` with a format string that ends in a single newline", + |err| { + err.multipart_suggestion( + "use `eprintln!` instead", + vec![ + (mac.path.span, String::from("eprintln")), + (newline_span(&fmt_str), String::new()), + ], + Applicability::MachineApplicable, + ); + }, + ); + } + } } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/eprint_with_newline.rs b/tests/ui/eprint_with_newline.rs new file mode 100644 index 00000000000..8df32649ad9 --- /dev/null +++ b/tests/ui/eprint_with_newline.rs @@ -0,0 +1,49 @@ +#![allow(clippy::print_literal)] +#![warn(clippy::print_with_newline)] + +fn main() { + eprint!("Hello\n"); + eprint!("Hello {}\n", "world"); + eprint!("Hello {} {}\n", "world", "#2"); + eprint!("{}\n", 1265); + eprint!("\n"); + + // these are all fine + eprint!(""); + eprint!("Hello"); + eprintln!("Hello"); + eprintln!("Hello\n"); + eprintln!("Hello {}\n", "world"); + eprint!("Issue\n{}", 1265); + eprint!("{}", 1265); + eprint!("\n{}", 1275); + eprint!("\n\n"); + eprint!("like eof\n\n"); + eprint!("Hello {} {}\n\n", "world", "#2"); + eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + eprintln!("\nbla\n\n"); // #3126 + + // Escaping + eprint!("\\n"); // #3514 + eprint!("\\\n"); // should fail + eprint!("\\\\n"); + + // Raw strings + eprint!(r"\n"); // #3778 + + // Literal newlines should also fail + eprint!( + " +" + ); + eprint!( + r" +" + ); + + // Don't warn on CRLF (#4208) + eprint!("\r\n"); + eprint!("foo\r\n"); + eprint!("\\r\n"); //~ ERROR + eprint!("foo\rbar\n") // ~ ERROR +} diff --git a/tests/ui/eprint_with_newline.stderr b/tests/ui/eprint_with_newline.stderr new file mode 100644 index 00000000000..31811d1d92a --- /dev/null +++ b/tests/ui/eprint_with_newline.stderr @@ -0,0 +1,121 @@ +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:5:5 + | +LL | eprint!("Hello/n"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-with-newline` implied by `-D warnings` +help: use `eprintln!` instead + | +LL | eprintln!("Hello"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:6:5 + | +LL | eprint!("Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {}", "world"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:7:5 + | +LL | eprint!("Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {} {}", "world", "#2"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:8:5 + | +LL | eprint!("{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("{}", 1265); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:9:5 + | +LL | eprint!("/n"); + | ^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!(); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:28:5 + | +LL | eprint!("//n"); // should fail + | ^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/"); // should fail + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:35:5 + | +LL | / eprint!( +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | "" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:39:5 + | +LL | / eprint!( +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | r"" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:47:5 + | +LL | eprint!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/r"); //~ ERROR + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:48:5 + | +LL | eprint!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("foo/rbar") // ~ ERROR + | ^^^^^^^^ -- + +error: aborting due to 10 previous errors + -- cgit 1.4.1-3-g733a5 From e58c7dd1685cb19e2ec87568cbc2cbd0aba3e2c7 Mon Sep 17 00:00:00 2001 From: Dobe Peter Date: Sat, 31 Oct 2020 20:31:34 +0100 Subject: panic_in_result_fn: Extend to also check usages of [debug_]assert* macros Also, the macro-finding logic has been moved to the util module, for use by future lints. --- clippy_lints/src/panic_in_result_fn.rs | 68 ++++++++++------------ clippy_lints/src/utils/mod.rs | 33 ++++++++++- tests/ui/panic_in_result_fn.stderr | 24 ++++---- tests/ui/panic_in_result_fn_assertions.rs | 48 +++++++++++++++ tests/ui/panic_in_result_fn_assertions.stderr | 57 ++++++++++++++++++ tests/ui/panic_in_result_fn_debug_assertions.rs | 48 +++++++++++++++ .../ui/panic_in_result_fn_debug_assertions.stderr | 57 ++++++++++++++++++ 7 files changed, 285 insertions(+), 50 deletions(-) create mode 100644 tests/ui/panic_in_result_fn_assertions.rs create mode 100644 tests/ui/panic_in_result_fn_assertions.stderr create mode 100644 tests/ui/panic_in_result_fn_debug_assertions.rs create mode 100644 tests/ui/panic_in_result_fn_debug_assertions.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 72dfccc1089..cdabb0d0dd6 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,18 +1,16 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then}; use rustc_hir as hir; -use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::Expr; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. /// - /// **Known problems:** None. + /// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked. /// /// **Example:** /// @@ -22,9 +20,15 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` + /// Use instead: + /// ```rust + /// fn result_without_panic() -> Result { + /// Err(String::from("error")) + /// } + /// ``` pub PANIC_IN_RESULT_FN, restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion" } declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); @@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } } -struct FindPanicUnimplementedUnreachable { - result: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if ["unimplemented", "unreachable", "panic", "todo"] - .iter() - .any(|fun| is_expn_of(expr.span, fun).is_some()) - { - self.result.push(expr.span); - } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; - panics.visit_expr(&body.value); - if !panics.result.is_empty() { + let panics = find_macro_calls( + vec![ + "unimplemented", + "unreachable", + "panic", + "todo", + "assert", + "assert_eq", + "assert_ne", + "debug_assert", + "debug_assert_eq", + "debug_assert_ne", + ], + body, + ); + if !panics.is_empty() { span_lint_and_then( cx, PANIC_IN_RESULT_FN, impl_span, - "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`", move |diag| { diag.help( - "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", ); - diag.span_note(panics.result, "return Err() instead of panicking"); + diag.span_note(panics, "return Err() instead of panicking"); }, ); } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 007e72d129f..e47d71aac99 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -41,7 +41,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, @@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { visitor.found } +struct FindMacroCalls<'a> { + names: Vec<&'a str>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) { + self.result.push(expr.span); + } + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Finds calls of the specified macros in a function body. +pub fn find_macro_calls(names: Vec<&str>, body: &'tcx Body<'tcx>) -> Vec { + let mut fmc = FindMacroCalls { + names, + result: Vec::new(), + }; + fmc.visit_expr(&body.value); + fmc.result +} + /// Converts a span to a code snippet if available, otherwise use default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index ca73ac5a411..eb744b0c198 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,4 +1,4 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:7:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:9:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:12:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:14:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:17:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:19:9 | @@ -50,7 +50,7 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:22:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,7 +59,7 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:24:9 | @@ -67,7 +67,7 @@ LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:53:1 | LL | / fn function_result_with_panic() -> Result // should emit lint @@ -76,7 +76,7 @@ LL | | panic!("error"); LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:55:5 | @@ -84,7 +84,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:68:1 | LL | / fn main() -> Result<(), String> { @@ -93,7 +93,7 @@ LL | | Ok(()) LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:69:5 | diff --git a/tests/ui/panic_in_result_fn_assertions.rs b/tests/ui/panic_in_result_fn_assertions.rs new file mode 100644 index 00000000000..ffdf8288adc --- /dev/null +++ b/tests/ui/panic_in_result_fn_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_assert_with_message(x: i32) -> Result // should emit lint + { + assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_assert_eq(x: i32) -> Result // should emit lint + { + assert_eq!(x, 5); + Ok(true) + } + + fn result_with_assert_ne(x: i32) -> Result // should emit lint + { + assert_ne!(x, 1); + Ok(true) + } + + fn other_with_assert_with_message(x: i32) // should not emit lint + { + assert!(x == 5, "wrong argument"); + } + + fn other_with_assert_eq(x: i32) // should not emit lint + { + assert_eq!(x, 5); + } + + fn other_with_assert_ne(x: i32) // should not emit lint + { + assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let assert = "assert!"; + println!("No {}", assert); + Ok(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr new file mode 100644 index 00000000000..a17f043737d --- /dev/null +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:7:5 + | +LL | / fn result_with_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:9:9 + | +LL | assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:13:5 + | +LL | / fn result_with_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:15:9 + | +LL | assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:19:5 + | +LL | / fn result_with_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:21:9 + | +LL | assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs new file mode 100644 index 00000000000..b60c79f97c8 --- /dev/null +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint + { + debug_assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint + { + debug_assert_eq!(x, 5); + Ok(true) + } + + fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint + { + debug_assert_ne!(x, 1); + Ok(true) + } + + fn other_with_debug_assert_with_message(x: i32) // should not emit lint + { + debug_assert!(x == 5, "wrong argument"); + } + + fn other_with_debug_assert_eq(x: i32) // should not emit lint + { + debug_assert_eq!(x, 5); + } + + fn other_with_debug_assert_ne(x: i32) // should not emit lint + { + debug_assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let debug_assert = "debug_assert!"; + println!("No {}", debug_assert); + Ok(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result_fn_debug_assertions.stderr b/tests/ui/panic_in_result_fn_debug_assertions.stderr new file mode 100644 index 00000000000..ec18e89698c --- /dev/null +++ b/tests/ui/panic_in_result_fn_debug_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 + | +LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 + | +LL | debug_assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5 + | +LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9 + | +LL | debug_assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5 + | +LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9 + | +LL | debug_assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From bdad7900f4558672ca02b24a0a079b05391acaf9 Mon Sep 17 00:00:00 2001 From: dp304 <34493835+dp304@users.noreply.github.com> Date: Thu, 3 Dec 2020 23:07:24 +0100 Subject: Apply suggestions from code review Use array slice instead of `Vec` in `find_macro_calls` as suggested by @ebroto Co-authored-by: Eduardo Broto --- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/utils/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index cdabb0d0dd6..37e2b50def1 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let panics = find_macro_calls( - vec![ + &[ "unimplemented", "unreachable", "panic", diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e47d71aac99..e83371f8b99 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -603,12 +603,12 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { visitor.found } -struct FindMacroCalls<'a> { - names: Vec<&'a str>, +struct FindMacroCalls<'a, 'b> { + names: &'a [&'b str], result: Vec, } -impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { +impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { @@ -625,7 +625,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { } /// Finds calls of the specified macros in a function body. -pub fn find_macro_calls(names: Vec<&str>, body: &'tcx Body<'tcx>) -> Vec { +pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec { let mut fmc = FindMacroCalls { names, result: Vec::new(), -- cgit 1.4.1-3-g733a5 From 3187cad8ec0e9c32272341028526d6f1e208a704 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 8 Dec 2020 23:17:12 +0100 Subject: Factor out some code in write.rs Get rid of the too-many-lines error. --- clippy_lints/src/write.rs | 110 ++++++++++++++++------------------- tests/ui/println_empty_string.fixed | 7 +++ tests/ui/println_empty_string.rs | 7 +++ tests/ui/println_empty_string.stderr | 14 ++++- 4 files changed, 77 insertions(+), 61 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0fd56f78572..337f7a229b9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -262,71 +262,22 @@ impl EarlyLintPass for Write { .map_or(false, |crate_name| crate_name == "build_script_build") } - if mac.path == sym!(println) { + if mac.path == sym!(print) { if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + 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 fmt_str.symbol == Symbol::intern("") { - span_lint_and_sugg( - cx, - PRINTLN_EMPTY_STRING, - mac.span(), - "using `println!(\"\")`", - "replace it with", - "println!()".to_string(), - Applicability::MachineApplicable, - ); - } + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(println) { + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } - } else if mac.path == sym!(eprintln) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `eprint!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `eprintln!` instead", - vec![ - (mac.path.span, String::from("eprintln")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } - } - } else if mac.path == sym!(print) { - if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `print!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `println!` instead", - vec![ - (mac.path.span, String::from("println")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } - } + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { @@ -530,6 +481,45 @@ impl Write { } } } + + fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if fmt_str.symbol == Symbol::intern("") { + let name = mac.path.segments[0].ident.name; + span_lint_and_sugg( + cx, + PRINTLN_EMPTY_STRING, + mac.span(), + &format!("using `{}!(\"\")`", name), + "replace it with", + format!("{}!()", name), + Applicability::MachineApplicable, + ); + } + } + } + + fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + let name = mac.path.segments[0].ident.name; + let suggested = format!("{}ln", name); + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + &format!("using `{}!()` with a format string that ends in a single newline", name), + |err| { + err.multipart_suggestion( + &format!("use `{}!` instead", suggested), + vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + Applicability::MachineApplicable, + ); + }, + ); + } + } + } } /// Checks if the format string contains a single newline that terminates it. diff --git a/tests/ui/println_empty_string.fixed b/tests/ui/println_empty_string.fixed index 2b889b62ea9..9760680927a 100644 --- a/tests/ui/println_empty_string.fixed +++ b/tests/ui/println_empty_string.fixed @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(), } + + eprintln!(); + eprintln!(); + + match "a" { + _ => eprintln!(), + } } diff --git a/tests/ui/println_empty_string.rs b/tests/ui/println_empty_string.rs index 890f5f68476..80fdb3e6e21 100644 --- a/tests/ui/println_empty_string.rs +++ b/tests/ui/println_empty_string.rs @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(""), } + + eprintln!(); + eprintln!(""); + + match "a" { + _ => eprintln!(""), + } } diff --git a/tests/ui/println_empty_string.stderr b/tests/ui/println_empty_string.stderr index 23112b88168..17fe4ea7479 100644 --- a/tests/ui/println_empty_string.stderr +++ b/tests/ui/println_empty_string.stderr @@ -12,5 +12,17 @@ error: using `println!("")` LL | _ => println!(""), | ^^^^^^^^^^^^ help: replace it with: `println!()` -error: aborting due to 2 previous errors +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:13:5 + | +LL | eprintln!(""); + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:16:14 + | +LL | _ => eprintln!(""), + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From f77f1db35b1f263286962cfa5e5c08a55add83cb Mon Sep 17 00:00:00 2001 From: Korrat Date: Sat, 24 Oct 2020 16:48:10 +0200 Subject: Add a lint for maps with zero-sized values Co-authored-by: Eduardo Broto --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/zero_sized_map_values.rs | 82 ++++++++++++++++++++++ tests/ui/zero_sized_btreemap_values.rs | 68 ++++++++++++++++++ tests/ui/zero_sized_btreemap_values.stderr | 107 +++++++++++++++++++++++++++++ tests/ui/zero_sized_hashmap_values.rs | 68 ++++++++++++++++++ tests/ui/zero_sized_hashmap_values.stderr | 107 +++++++++++++++++++++++++++++ 7 files changed, 437 insertions(+) create mode 100644 clippy_lints/src/zero_sized_map_values.rs create mode 100644 tests/ui/zero_sized_btreemap_values.rs create mode 100644 tests/ui/zero_sized_btreemap_values.stderr create mode 100644 tests/ui/zero_sized_hashmap_values.rs create mode 100644 tests/ui/zero_sized_hashmap_values.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 82f2ad7ec4e..af3b1c1db2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2171,5 +2171,6 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a92ae9ed8d9..5fca088c9b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -345,6 +345,7 @@ mod wildcard_dependencies; mod wildcard_imports; mod write; mod zero_div_zero; +mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; @@ -944,6 +945,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &write::WRITE_LITERAL, &write::WRITE_WITH_NEWLINE, &zero_div_zero::ZERO_DIVIDED_BY_ZERO, + &zero_sized_map_values::ZERO_SIZED_MAP_VALUES, ]); // end register lints, do not remove this comment, it’s used in `update_lints` @@ -1204,6 +1206,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); + store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1336,6 +1339,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_self::UNUSED_SELF), LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), + LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), ]); #[cfg(feature = "internal-lints")] diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs new file mode 100644 index 00000000000..1d5fa8d06a9 --- /dev/null +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -0,0 +1,82 @@ +use if_chain::if_chain; +use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Adt, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_target::abi::LayoutOf as _; +use rustc_typeck::hir_ty_to_ty; + +use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. + /// + /// **Why is this bad?** Since there is only a single value for a zero-sized type, a map + /// containing zero sized values is effectively a set. Using a set in that case improves + /// readability and communicates intent more clearly. + /// + /// **Known problems:** + /// * A zero-sized type cannot be recovered later if it contains private fields. + /// * This lints the signature of public items + /// + /// **Example:** + /// + /// ```rust + /// # use std::collections::HashMap; + /// fn unique_words(text: &str) -> HashMap<&str, ()> { + /// todo!(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::collections::HashSet; + /// fn unique_words(text: &str) -> HashSet<&str> { + /// todo!(); + /// } + /// ``` + pub ZERO_SIZED_MAP_VALUES, + pedantic, + "usage of map with zero-sized value type" +} + +declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); + +impl LateLintPass<'_> for ZeroSizedMapValues { + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + if_chain! { + if !hir_ty.span.from_expansion(); + if !in_trait_impl(cx, hir_ty.hir_id); + let ty = ty_from_hir_ty(cx, hir_ty); + if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); + if let Adt(_, ref substs) = ty.kind(); + let ty = substs.type_at(1); + if let Ok(layout) = cx.layout_of(ty); + if layout.is_zst(); + then { + span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead"); + } + } + } +} + +fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let parent_id = cx.tcx.hir().get_parent_item(hir_id); + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(parent_id)) { + if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return true; + } + } + false +} + +fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { + cx.maybe_typeck_results() + .and_then(|results| { + if results.hir_owner == hir_ty.hir_id.owner { + results.node_type_opt(hir_ty.hir_id) + } else { + None + } + }) + .unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty)) +} diff --git a/tests/ui/zero_sized_btreemap_values.rs b/tests/ui/zero_sized_btreemap_values.rs new file mode 100644 index 00000000000..5cd254787d8 --- /dev/null +++ b/tests/ui/zero_sized_btreemap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::BTreeMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = BTreeMap; +type NotOkMap = BTreeMap; + +enum TestEnum { + Ok(BTreeMap), + NotOk(BTreeMap), +} + +struct Test { + ok: BTreeMap, + not_ok: BTreeMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: BTreeMap); +} + +impl Test { + fn ok(&self) -> BTreeMap { + todo!() + } + + fn not_ok(&self) -> BTreeMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = BTreeMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: BTreeMap) { + todo!(); + } +} + +fn test(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn test2(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn main() { + let _: BTreeMap = BTreeMap::new(); + let _: BTreeMap = BTreeMap::new(); + + let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/tests/ui/zero_sized_btreemap_values.stderr b/tests/ui/zero_sized_btreemap_values.stderr new file mode 100644 index 00000000000..334d921a9af --- /dev/null +++ b/tests/ui/zero_sized_btreemap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:11:17 + | +LL | type NotOkMap = BTreeMap; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:15:11 + | +LL | NotOk(BTreeMap), + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:20:13 + | +LL | not_ok: BTreeMap, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:30:30 + | +LL | fn weird_map(&self, map: BTreeMap); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:38:25 + | +LL | fn not_ok(&self) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:14 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:50 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:35 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:12 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:67:12 + | +LL | let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + diff --git a/tests/ui/zero_sized_hashmap_values.rs b/tests/ui/zero_sized_hashmap_values.rs new file mode 100644 index 00000000000..a1608d863fb --- /dev/null +++ b/tests/ui/zero_sized_hashmap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::HashMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = HashMap; +type NotOkMap = HashMap; + +enum TestEnum { + Ok(HashMap), + NotOk(HashMap), +} + +struct Test { + ok: HashMap, + not_ok: HashMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: HashMap); +} + +impl Test { + fn ok(&self) -> HashMap { + todo!() + } + + fn not_ok(&self) -> HashMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = HashMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: HashMap) { + todo!(); + } +} + +fn test(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn test2(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn main() { + let _: HashMap = HashMap::new(); + let _: HashMap = HashMap::new(); + + let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/tests/ui/zero_sized_hashmap_values.stderr b/tests/ui/zero_sized_hashmap_values.stderr new file mode 100644 index 00000000000..43987b3d01d --- /dev/null +++ b/tests/ui/zero_sized_hashmap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:11:17 + | +LL | type NotOkMap = HashMap; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:15:11 + | +LL | NotOk(HashMap), + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:20:13 + | +LL | not_ok: HashMap, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:30:30 + | +LL | fn weird_map(&self, map: HashMap); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:38:25 + | +LL | fn not_ok(&self) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:14 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:49 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:34 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:12 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:67:12 + | +LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From a6bb9276f7964b96899d04d680bc04bf99c8bf47 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 9 Nov 2020 23:38:08 +0100 Subject: Lint wrong self convention in trait also --- clippy_lints/src/methods/mod.rs | 49 ++++++++++++++++++++++++++++++----- tests/ui/wrong_self_convention.rs | 26 +++++++++++++++++++ tests/ui/wrong_self_convention.stderr | 34 +++++++++++++++++++++++- 3 files changed, 101 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8002c27a5e9..3c89c1b6ed2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -22,6 +22,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; @@ -1623,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let def_id = cx.tcx.hir().local_def_id(item.hir_id); let self_ty = cx.tcx.type_of(def_id); + + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); - if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind; let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); let method_sig = cx.tcx.fn_sig(method_def_id); @@ -1697,11 +1703,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { - return; - } - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1735,8 +1736,42 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + if_chain! { + if let TraitItemKind::Fn(ref sig, _) = item.kind; + if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + let first_arg_span = first_arg_ty.span; + let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + + then { + if let Some((ref conv, self_kinds)) = &CONVENTIONS + .iter() + .find(|(ref conv, _)| conv.check(&item.ident.name.as_str())) + { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + WRONG_PUB_SELF_CONVENTION, + first_arg_span, + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } + } + } + if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index f44305d7e48..275866b8248 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -88,3 +88,29 @@ mod issue4037 { } } } + +// Lint also in trait definition (see #6307) +mod issue6307{ + trait T: Sized { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(&self) {} + fn into_u32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn to_u32(&self) {} + fn from_i32(self) {} + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_pub_self_convention)] + fn from_cake(self) {} + + // test for false positives + fn as_(self) {} + fn into_(&self) {} + fn is_(self) {} + fn to_(self) {} + fn from_(self) {} + fn to_mut(&mut self) {} + } +} \ No newline at end of file diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index ef3ad73ebc7..64aa957fed6 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -72,5 +72,37 @@ error: methods called `from_*` usually take no self; consider choosing a less am LL | pub fn from_i64(self) {} | ^^^^ -error: aborting due to 12 previous errors +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:95:19 + | +LL | fn as_i32(self) {} + | ^^^^ + | + = note: `-D clippy::wrong-pub-self-convention` implied by `-D warnings` + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:97:21 + | +LL | fn into_i32(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:99:19 + | +LL | fn is_i32(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:101:19 + | +LL | fn to_i32(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:103:21 + | +LL | fn from_i32(self) {} + | ^^^^ + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 4af9382bec3f49d9c7e0f03e9cfb5e8f1c70fdbe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 10 Nov 2020 08:56:17 +0100 Subject: Common function to lint wrong self convention from impl and trait def --- clippy_lints/src/methods/mod.rs | 88 +++++++++++++++++------------------ tests/ui/wrong_self_convention.rs | 6 +-- tests/ui/wrong_self_convention.stderr | 2 - 3 files changed, 45 insertions(+), 51 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c89c1b6ed2..9a082a89229 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1674,32 +1674,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&name)) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - let lint = if item.vis.node.is_pub() { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; - - span_lint( - cx, - lint, - first_arg.pat.span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span + ); } } @@ -1748,26 +1730,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); then { - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&item.ident.name.as_str())) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( - cx, - WRONG_PUB_SELF_CONVENTION, - first_arg_span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span); } } @@ -1792,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } +fn lint_wrong_self_convention<'tcx>( + cx: &LateContext<'tcx>, + item_name: &str, + is_pub: bool, + self_ty: &'tcx TyS<'tcx>, + first_arg_ty: &'tcx TyS<'tcx>, + first_arg_span: Span, +) { + let lint = if is_pub { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + lint, + first_arg_span, + &format!( + "methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } +} + /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 275866b8248..795ba77274c 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -90,7 +90,7 @@ mod issue4037 { } // Lint also in trait definition (see #6307) -mod issue6307{ +mod issue6307 { trait T: Sized { fn as_i32(self) {} fn as_u32(&self) {} @@ -102,7 +102,7 @@ mod issue6307{ fn to_u32(&self) {} fn from_i32(self) {} // check whether the lint can be allowed at the function level - #[allow(clippy::wrong_pub_self_convention)] + #[allow(clippy::wrong_self_convention)] fn from_cake(self) {} // test for false positives @@ -113,4 +113,4 @@ mod issue6307{ fn from_(self) {} fn to_mut(&mut self) {} } -} \ No newline at end of file +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 64aa957fed6..289da6f059e 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -77,8 +77,6 @@ error: methods called `as_*` usually take self by reference or self by mutable r | LL | fn as_i32(self) {} | ^^^^ - | - = note: `-D clippy::wrong-pub-self-convention` implied by `-D warnings` error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name --> $DIR/wrong_self_convention.rs:97:21 -- cgit 1.4.1-3-g733a5 From a7cfffef263a8b5a5dadfad0b7f56acd83d02b9b Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sat, 5 Dec 2020 04:59:22 -0800 Subject: add MSRV to more lints specified in #6097 update tests --- clippy_lints/src/checked_conversions.rs | 26 +++++++-- clippy_lints/src/lib.rs | 15 +++--- clippy_lints/src/mem_replace.rs | 27 ++++++++-- clippy_lints/src/methods/mod.rs | 23 ++++++-- clippy_lints/src/missing_const_for_fn.rs | 29 ++++++++-- clippy_lints/src/ranges.rs | 29 +++++++--- clippy_lints/src/redundant_field_names.rs | 25 +++++++-- clippy_lints/src/redundant_static_lifetimes.rs | 26 +++++++-- clippy_lints/src/use_self.rs | 25 +++++++-- tests/ui/min_rust_version_attr.rs | 74 +++++++++++++++++++++++++- tests/ui/min_rust_version_attr.stderr | 51 ++++++++---------- 11 files changed, 282 insertions(+), 68 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 28c1a54d2c5..54bc69e058b 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -6,9 +6,12 @@ use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; + +const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); declare_clippy_lint! { /// **What it does:** Checks for explicit bounds checking when casting. @@ -39,10 +42,25 @@ declare_clippy_lint! { "`try_from` could replace manual bounds checking when casting" } -declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); +pub struct CheckedConversions { + msrv: Option, +} + +impl CheckedConversions { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) { + return; + } + let result = if_chain! { if !in_external_macro(cx.sess(), item.span); if let ExprKind::Binary(op, ref left, ref right) = &item.kind; @@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { } } } + + extract_msrv_attr!(LateContext); } /// Searches for a single check from unsigned to _ is done diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a92ae9ed8d9..38a25d22aa2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1003,6 +1003,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); + store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); + store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); + store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); + store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box use_self::UseSelf::new(msrv)); + store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); @@ -1013,7 +1021,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box ranges::Ranges); store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); @@ -1058,7 +1065,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_multiply::NegMultiply); store.register_late_pass(|| box mem_discriminant::MemDiscriminant); store.register_late_pass(|| box mem_forget::MemForget); - store.register_late_pass(|| box mem_replace::MemReplace); store.register_late_pass(|| box arithmetic::Arithmetic::default()); store.register_late_pass(|| box assign_ops::AssignOps); store.register_late_pass(|| box let_if_seq::LetIfSeq); @@ -1080,7 +1086,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); - store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); @@ -1106,10 +1111,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); - store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); - store.register_late_pass(|| box checked_conversions::CheckedConversions); store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box inherent_to_string::InherentToString); let max_trait_bounds = conf.max_trait_bounds; @@ -1138,7 +1141,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); - store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); @@ -1178,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); - store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index bb0acecc5a9..19087b02077 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,13 +1,14 @@ use crate::utils::{ - in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help, + in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -94,7 +95,7 @@ declare_clippy_lint! { "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`" } -declare_lint_pass!(MemReplace => +impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { @@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } } +const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); + +pub struct MemReplace { + msrv: Option, +} + +impl MemReplace { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { @@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - check_replace_with_default(cx, src, dest, expr.span); + if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) { + check_replace_with_default(cx, src, dest, expr.span); + } } } } + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8002c27a5e9..5133f31e0e7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1487,7 +1487,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { - if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, @@ -1509,7 +1509,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), - ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), + ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2733,6 +2733,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s /// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( @@ -2740,7 +2742,11 @@ fn lint_map_unwrap_or_else<'tcx>( expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, ) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); @@ -2923,9 +2929,20 @@ fn lint_filter_map<'tcx>( } } +const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); + /// lint use of `filter_map().next()` for `Iterators` -fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { +fn lint_filter_map_next<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) { if match_trait_method(cx, expr, &paths::ITERATOR) { + if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + return; + } + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 38e2ce563ee..6ebeaced62a 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,14 +1,19 @@ use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; +use crate::utils::{ + fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method, +}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; +const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + declare_clippy_lint! { /// **What it does:** /// @@ -69,7 +74,18 @@ declare_clippy_lint! { "Lint functions definitions that could be made `const fn`" } -declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); +impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); + +pub struct MissingConstForFn { + msrv: Option, +} + +impl MissingConstForFn { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { fn check_fn( @@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { + if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) { + return; + } + let def_id = cx.tcx.hir().local_def_id(hir_id); if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) { @@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); } } + extract_msrv_attr!(LateContext); } /// Returns true if any of the method parameters is a type that implements `Drop`. The method diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4b514bbd42c..f9173808089 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -3,9 +3,10 @@ use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::sym; use rustc_span::symbol::Ident; @@ -13,8 +14,8 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, - span_lint, span_lint_and_sugg, span_lint_and_then, + get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -160,7 +161,20 @@ declare_clippy_lint! { "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" } -declare_lint_pass!(Ranges => [ +const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); + +pub struct Ranges { + msrv: Option, +} + +impl Ranges { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, @@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, ref l, ref r) => { - check_possible_range_contains(cx, op.node, l, r, expr.span); + if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { + check_possible_range_contains(cx, op.node, l, r, expr.span); + } }, _ => {}, } @@ -184,6 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_inclusive_range_minus_one(cx, expr); check_reversed_empty_range(cx, expr); } + extract_msrv_attr!(LateContext); } fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 2a81170e49e..38dcf7a192c 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,9 +1,12 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{meets_msrv, span_lint_and_sugg}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands @@ -33,10 +36,25 @@ declare_clippy_lint! { "checks for fields in struct literals where shorthands could be used" } -declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); +pub struct RedundantFieldNames { + msrv: Option, +} + +impl RedundantFieldNames { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) { + return; + } + if in_external_macro(cx.sess, expr.span) { return; } @@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames { } } } + extract_msrv_attr!(EarlyContext); } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 7bbcc67aa2d..fcfa3c12755 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,8 +1,11 @@ -use crate::utils::{snippet, span_lint_and_then}; +use crate::utils::{meets_msrv, snippet, span_lint_and_then}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. @@ -29,7 +32,18 @@ declare_clippy_lint! { "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them." } -declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); +pub struct RedundantStaticLifetimes { + msrv: Option, +} + +impl RedundantStaticLifetimes { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); impl RedundantStaticLifetimes { // Recursively visit types @@ -84,6 +98,10 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) { + return; + } + if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); @@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes { } } } + + extract_msrv_attr!(EarlyContext); } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 5ac4797680b..3b23f885e08 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -12,11 +12,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_middle::ty::{DefIdTree, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{differing_macro_contexts, span_lint_and_sugg}; +use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unnecessary repetition of structure name when a @@ -53,7 +54,7 @@ declare_clippy_lint! { "unnecessary structure name repetition whereas `Self` is applicable" } -declare_lint_pass!(UseSelf => [USE_SELF]); +impl_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; @@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>( } } +const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + +pub struct UseSelf { + msrv: Option, +} + +impl UseSelf { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + return; + } + if in_external_macro(cx.sess(), item.span) { return; } @@ -204,6 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } } + extract_msrv_attr!(LateContext); } struct UseSelfVisitor<'a, 'tcx> { diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 1026cc40d3b..ac75f5e46c3 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![clippy::msrv = "1.0.0"] -use std::ops::Deref; +use std::ops::{Deref, RangeFrom}; fn option_as_ref_deref() { let mut opt = Some(String::from("123")); @@ -42,12 +42,84 @@ pub fn manual_strip_msrv() { } } +pub fn redundant_fieldnames() { + let start = 0; + let _ = RangeFrom { start: start }; +} + +pub fn redundant_static_lifetime() { + const VAR_ONE: &'static str = "Test constant #1"; +} + +pub fn checked_conversion() { + let value: i64 = 42; + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +pub fn filter_map_next() { + let a = ["1", "lol", "3", "NaN", "5"]; + + #[rustfmt::skip] + let _: Option = vec![1, 2, 3, 4, 5, 6] + .into_iter() + .filter_map(|x| { + if x == 2 { + Some(x * 2) + } else { + None + } + }) + .next(); +} + +pub fn manual_range_contains() { + x >= 8 && x < 12; +} + +pub fn use_self() { + struct Foo {} + + impl Foo { + fn new() -> Foo { + Foo {} + } + fn test() -> Foo { + Foo::new() + } + } +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::replace(s, String::default()); +} + +fn map_unwrap_or() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt + .map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); +} + fn main() { + filter_map_next(); + checked_conversion(); + redundant_fieldnames(); + redundant_static_lifetime(); option_as_ref_deref(); match_like_matches(); match_same_arms(); match_same_arms2(); manual_strip_msrv(); + manual_range_contains(); + use_self(); + replace_with_default(); + map_unwrap_or(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 3e1af046e7a..d3eafe7312f 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,28 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:60:24 - | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::manual-strip` implied by `-D warnings` -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:59:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method - | -LL | if let Some() = s.strip_prefix("hello, ") { -LL | assert_eq!(.to_uppercase(), "WORLD!"); +error[E0425]: cannot find value `x` in this scope + --> $DIR/min_rust_version_attr.rs:77:5 | +LL | x >= 8 && x < 12; + | ^ not found in this scope -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:72:24 +error[E0425]: cannot find value `x` in this scope + --> $DIR/min_rust_version_attr.rs:77:15 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:71:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method +LL | x >= 8 && x < 12; + | ^ not found in this scope + +error[E0308]: mismatched types + --> $DIR/min_rust_version_attr.rs:95:31 | -LL | if let Some() = s.strip_prefix("hello, ") { -LL | assert_eq!(.to_uppercase(), "WORLD!"); +LL | let _ = std::mem::replace(s, String::default()); + | ^ + | | + | expected `&mut _`, found struct `std::string::String` + | help: consider mutably borrowing here: `&mut s` | + = note: expected mutable reference `&mut _` + found struct `std::string::String` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 26c61c7e49b173c8ae7c54c3a4c90b60cd9f71b8 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 25 Nov 2020 17:07:50 +0900 Subject: Fix FP of `manual_range_contains` in `const fn` --- clippy_lints/src/ranges.rs | 11 ++++++++--- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index f9173808089..3e454eecd97 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -14,7 +14,7 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { }, ExprKind::Binary(ref op, ref l, ref r) => { if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { - check_possible_range_contains(cx, op.node, l, r, expr.span); + check_possible_range_contains(cx, op.node, l, r, expr); } }, _ => {}, @@ -203,7 +203,12 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { + if in_constant(cx, expr.hir_id) { + return; + } + + let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 048874a7f82..47c974e614b 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -44,3 +44,8 @@ fn main() { (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 60ad259f404..835deced5e4 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -44,3 +44,8 @@ fn main() { y >= 0. && y < 1.; y < 0. || y > 1.; } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} -- cgit 1.4.1-3-g733a5 From 0b145d688b293a92cd855000f249d83acae53f9d Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 12 Dec 2020 01:09:30 +0100 Subject: clone_double_ref: print reference type in lint message changelog: clone_double_ref: print the type of the reference in lint message --- clippy_lints/src/methods/mod.rs | 7 +++++-- tests/ui/unnecessary_clone.stderr | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..ce234e01a1b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2100,8 +2100,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp cx, CLONE_DOUBLE_REF, expr.span, - "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + &format!( + "using `clone` on a double-reference; \ + this will copy the reference of type `{}` instead of cloning the inner type", + ty + ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..b908d0ce9c1 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -44,7 +44,7 @@ error: using `clone` on a `Copy` type LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -66,7 +66,7 @@ error: using `clone` on a `Copy` type LL | let _: E = a.clone(); | ^^^^^^^^^ help: try dereferencing it: `*****a` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:89:22 | LL | let _ = &mut encoded.clone(); @@ -81,7 +81,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &mut <&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:90:18 | LL | let _ = &encoded.clone(); -- cgit 1.4.1-3-g733a5 From b2cb6ffbe3735ef8f137c9a6c1290c4a078793ef Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 12 Dec 2020 01:23:28 +0100 Subject: clone_on_copy: show the type in the lint message changelog: clone_on_copy: show the type in the lint message --- clippy_lints/src/methods/mod.rs | 16 +++++++++++----- tests/ui/clone_on_copy.stderr | 10 +++++----- tests/ui/unnecessary_clone.stderr | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..03eaee35fc4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2174,11 +2174,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp } else { snip = None; } - span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }); + span_lint_and_then( + cx, + CLONE_ON_COPY, + expr.span, + &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + |diag| { + if let Some((text, snip)) = snip { + diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); + } + }, + ); } } diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index ec2faf4ab40..14a700886a7 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,4 +1,4 @@ -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:22:5 | LL | 42.clone(); @@ -6,25 +6,25 @@ LL | 42.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:26:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:29:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` -error: using `clone` on a `Copy` type +error: using `clone` on type `char` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:35:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:39:14 | LL | vec.push(42.clone()); diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..bb2dd998f27 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -30,7 +30,7 @@ error: using `.clone()` on a ref-counted pointer LL | let _: Arc = x.clone(); | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` -error: using `clone` on a `Copy` type +error: using `clone` on type `T` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:40:5 | LL | t.clone(); @@ -38,7 +38,7 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `std::option::Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); @@ -60,7 +60,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a `Copy` type +error: using `clone` on type `many_derefs::E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); -- cgit 1.4.1-3-g733a5 From 3af09b8cf1229fb05a549a13b144aca6b60784c7 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:32:41 +0200 Subject: New internal lint: interning_defined_symbol --- clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/utils/internal_lints.rs | 80 +++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 6 ++ tests/ui-internal/interning_defined_symbol.fixed | 33 ++++++++++ tests/ui-internal/interning_defined_symbol.rs | 33 ++++++++++ tests/ui-internal/interning_defined_symbol.stderr | 27 ++++++++ 6 files changed, 183 insertions(+) create mode 100644 tests/ui-internal/interning_defined_symbol.fixed create mode 100644 tests/ui-internal/interning_defined_symbol.rs create mode 100644 tests/ui-internal/interning_defined_symbol.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f06926fa91d..97018599b05 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -513,6 +513,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] + &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, @@ -958,6 +960,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); @@ -1350,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), LintId::of(&utils::internal_lints::INVALID_PATHS), + LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 8b59a9541a7..0de87fab528 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; @@ -247,6 +248,30 @@ declare_clippy_lint! { "invalid path" } +declare_clippy_lint! { + /// **What it does:** + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// **Why is this bad?** + /// It's faster and easier to use the symbol constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -840,3 +865,58 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { } } } + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol to the constant name. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + // SAFETY: We're converting the raw bytes of the symbol value back + // into a Symbol instance. + let symbol = unsafe { std::mem::transmute::(value) }; + self.symbol_map.insert(symbol.to_string(), item.ident.to_string()); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + if let Some(symbol_const) = self.symbol_map.get(&arg); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + format!("rustc_span::symbol::sym::{}", symbol_const), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 6fdc7b4587f..2080a49a11c 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; +#[cfg(feature = "internal-lints")] +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; +#[cfg(feature = "internal-lints")] +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed new file mode 100644 index 00000000000..c6b84d2ef65 --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = rustc_span::symbol::sym::f32; + + // Using a sym macro + let _ = rustc_span::symbol::sym::f32; + + // Correct suggestion when symbol isn't stringified constant name + let _ = rustc_span::symbol::sym::proc_dash_macro; + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs new file mode 100644 index 00000000000..9ec82d4ad0b --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = Symbol::intern("f32"); + + // Using a sym macro + let _ = sym!(f32); + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr new file mode 100644 index 00000000000..74b906c8a57 --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -0,0 +1,27 @@ +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:17:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | +note: the lint level is defined here + --> $DIR/interning_defined_symbol.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:20:13 + | +LL | let _ = sym!(f32); + | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:23:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From a6aa0acbeaeb74a50e08bfa2b18df4e22dbd9894 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:32:41 +0200 Subject: Fix dogfood errors --- clippy_lints/src/manual_ok_or.rs | 3 ++- clippy_lints/src/methods/mod.rs | 10 +++++----- clippy_lints/src/ref_option_ref.rs | 3 ++- clippy_lints/src/strings.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 5 +++-- 5 files changed, 13 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index c99d2e35b94..b97d97ea1a5 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -8,6 +8,7 @@ use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** @@ -51,7 +52,7 @@ impl LateLintPass<'_> for ManualOkOr { if args.len() == 3; let method_receiver = &args[0]; let ty = cx.typeck_results().expr_ty(method_receiver); - if is_type_diagnostic_item(cx, ty, sym!(option_type)); + if is_type_diagnostic_item(cx, ty, sym::option_type); let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..c5eab2a97fe 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1568,7 +1568,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); - if args.len() == 1 && method_call.ident.name == sym!(clone) { + if args.len() == 1 && method_call.ident.name == sym::clone { lint_clone_on_copy(cx, expr, &args[0], self_ty); lint_clone_on_ref_ptr(cx, expr, &args[0]); } @@ -1592,7 +1592,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } }, - ty::Ref(..) if method_call.ident.name == sym!(into_iter) => { + ty::Ref(..) if method_call.ident.name == sym::into_iter => { lint_into_iter(cx, expr, self_ty, *method_span); }, _ => (), @@ -2638,9 +2638,9 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((EXPECT_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { Some((EXPECT_USED, "a Result", "Err")) } else { None @@ -3133,7 +3133,7 @@ fn lint_search_is_some<'tcx>( else if search_method == "find" { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, self_ty, sym::string_type) { true } else { *self_ty.kind() == ty::Str diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index a914a77d48b..803ebada54b 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -2,6 +2,7 @@ use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use if_chain::if_chain; use rustc_errors::Applicability; @@ -41,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(res) = last.res; if let Some(def_id) = res.opt_def_id(); - if cx.tcx.is_diagnostic_item(sym!(option_type), def_id); + if cx.tcx.is_diagnostic_item(sym::option_type, def_id); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 77e79073378..31dd5965473 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -372,7 +372,7 @@ impl LateLintPass<'_> for StringToString { if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; if path.ident.name == sym!(to_string); let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(string_type)); + if is_type_diagnostic_item(cx, ty, sym::string_type); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index e763da593d4..5b9a80f92db 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -9,6 +9,7 @@ use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use rustc_span::Span; declare_clippy_lint! { @@ -82,9 +83,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } } - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) { ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { ("Result", &paths::RESULT_OK) } else { return; -- cgit 1.4.1-3-g733a5 From 64e630c28018972479394a2fbdcc9f7d8856bb91 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:46:08 +0200 Subject: Run 'cargo dev update_lints' --- clippy_lints/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97018599b05..0498d469c00 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -511,10 +511,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] &utils::internal_lints::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal-lints")] + &utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, @@ -1352,8 +1352,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), - LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), + LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), -- cgit 1.4.1-3-g733a5 From cd2a62cb0cf89f5b4105c1c40651cf0eeaa85b14 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 13 Dec 2020 15:17:47 +0100 Subject: needless_borrow: print the type in the lint message changelog: needless_borrow: print type in lint message --- clippy_lints/src/needless_borrow.rs | 7 +++++-- tests/ui/eta.stderr | 2 +- tests/ui/needless_borrow.stderr | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 405c21d608d..bff53eb8cca 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { kind: Adjust::Deref(_), .. @@ -62,8 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { cx, NEEDLESS_BORROW, e.span, - "this expression borrows a reference that is immediately dereferenced \ + &format!( + "this expression borrows a reference (`&{}`) that is immediately dereferenced \ by the compiler", + ty + ), |diag| { if let Some(snippet) = snippet_opt(cx, inner.span) { diag.span_suggestion( diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index c4713ca8083..16aa1b07733 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -12,7 +12,7 @@ error: redundant closure found LL | meta(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler --> $DIR/eta.rs:24:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 0bfeda7914d..bea4b41b803 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,4 +1,4 @@ -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:14:15 | LL | let c = x(&&a); @@ -12,7 +12,7 @@ error: this pattern creates a reference to a reference LL | if let Some(ref cake) = Some(&5) {} | ^^^^^^^^ help: change this to: `cake` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:28:15 | LL | 46 => &&a, -- cgit 1.4.1-3-g733a5 From a37af06fea574f3d9f4d3cca58a70cd545523486 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 9 Dec 2020 12:25:45 +0000 Subject: Removing false positive for the match_single_binding lint * Applying suggested changes from the PR --- clippy_lints/src/matches.rs | 22 ++++++++++++++++++++-- tests/ui/match_single_binding.fixed | 28 ++++++++++++++++++++++++++++ tests/ui/match_single_binding.rs | 33 +++++++++++++++++++++++++++++++++ tests/ui/match_single_binding.stderr | 13 ++++++++++++- 4 files changed, 93 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2a1a73f98ee..04b35835c6b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -4,8 +4,8 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, - snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -1237,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } + + // HACK: + // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here + // to prevent false positives as there is currently no better way to detect if code was excluded by + // a macro. See PR #6435 + if_chain! { + if let Some(match_snippet) = snippet_opt(cx, expr.span); + if let Some(arm_snippet) = snippet_opt(cx, arms[0].span); + if let Some(ex_snippet) = snippet_opt(cx, ex.span); + let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, ""); + if rest_snippet.contains("=>"); + then { + // The code it self contains another thick arrow "=>" + // -> Either another arm or a comment + return; + } + } + let matched_vars = ex.span; let bind_names = arms[0].pat.span; let match_body = remove_blocks(&arms[0].body); diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index f3627902eec..526e94b10bd 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -87,4 +87,32 @@ fn main() { unwrapped }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + println!("Single branch"); + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 8c182148ae1..6a2ca7c5e93 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -99,4 +99,37 @@ fn main() { unwrapped => unwrapped, }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 795c8c3e24d..cbbf5d29c02 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -167,5 +167,16 @@ LL | unwrapped LL | }) | -error: aborting due to 11 previous errors +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:112:5 + | +LL | / match match y { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("Single branch");` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 39bcf8e55438016fb86427f9b8a78b6a32a84126 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 15 Dec 2020 23:18:03 +0100 Subject: Handle fatal errors when parsing doctests --- clippy_lints/src/doc.rs | 116 ++++++++++++++++++++++-------------------- clippy_lints/src/lib.rs | 1 + tests/ui/needless_doc_main.rs | 6 +++ 3 files changed, 68 insertions(+), 55 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 55e4755c278..77203a286dd 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -451,66 +451,72 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - fn has_needless_main(code: &str) -> bool { - let filename = FileName::anon_source_code(code); - - 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; + fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool { + rustc_driver::catch_fatal_errors(|| { + rustc_span::with_session_globals(cx.tcx.sess.edition(), || { + let filename = FileName::anon_source_code(code); + + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); } + return false; }, - // Another function was found; this case is ignored too - ItemKind::Fn(..) => return false, - _ => {}, - }, - Ok(None) => break, - Err(mut e) => { - e.cancel(); - return false; - }, - } - } + }; + + 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 + relevant_main_found + }) + }) + .ok() + .unwrap_or_default() } - if has_needless_main(text) { + if has_needless_main(cx, text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f06926fa91d..651b9e71131 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -27,6 +27,7 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_pretty; diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 883683e08a2..52c84089fa8 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -128,6 +128,12 @@ fn bad_doctests() {} /// ``` fn no_false_positives() {} +/// Yields a parse error when interpreted as rust code: +/// ``` +/// r#"hi" +/// ``` +fn issue_6022() {} + fn main() { bad_doctests(); no_false_positives(); -- cgit 1.4.1-3-g733a5 From 41b5ebebfdc583d9e3702dd61c8d897999d1e7f5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 16 Dec 2020 00:14:47 +0100 Subject: needless_doctest_main: add edition support --- clippy_lints/src/doc.rs | 26 ++++++++++++++++++-------- tests/ui/needless_doc_main.rs | 4 ++-- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 77203a286dd..08134cc16c0 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -14,6 +14,7 @@ use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::edition::Edition; use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; use std::io; @@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs check_doc(cx, valid_idents, events, &spans) } -const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"]; +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, @@ -400,13 +401,21 @@ fn check_doc<'a, Events: Iterator, Range { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { - is_rust = - lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i)); + let infos = lang.split(',').collect::>(); + is_rust = !infos.iter().any(|&i| i == "ignore") + && infos + .iter() + .any(|i| i.is_empty() || i.starts_with("edition") || RUST_CODE.contains(&i)); + edition = infos + .iter() + .find_map(|i| i.starts_with("edition").then(|| i[7..].parse::().ok())) + .flatten(); } }, End(CodeBlock(_)) => { @@ -436,7 +445,8 @@ fn check_doc<'a, Events: Iterator, Range, Range, text: &str, span: Span) { - fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool { +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { + fn has_needless_main(code: &str, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { - rustc_span::with_session_globals(cx.tcx.sess.edition(), || { + rustc_span::with_session_globals(edition, || { let filename = FileName::anon_source_code(code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); @@ -516,7 +526,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { .unwrap_or_default() } - if has_needless_main(cx, text) { + if has_needless_main(text, edition) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 52c84089fa8..83e9bbaa3af 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// ``` /// /// With an explicit return type it should lint too -/// ``` +/// ```edition2015 /// fn main() -> () { /// unimplemented!(); /// } @@ -39,7 +39,7 @@ fn bad_doctests() {} /// ``` /// /// This shouldn't lint either, because main is async: -/// ``` +/// ```edition2018 /// async fn main() { /// assert_eq!(42, ANSWER); /// } -- cgit 1.4.1-3-g733a5 From f732cc5cd6aeb06e07bd478d78fccaa625daa685 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Wed, 16 Dec 2020 06:05:25 +0200 Subject: Remove unsafe code --- clippy_lints/src/utils/internal_lints.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0de87fab528..9ba39f73ee8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -868,8 +868,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { #[derive(Default)] pub struct InterningDefinedSymbol { - // Maps the symbol to the constant name. - symbol_map: FxHashMap, + // Maps the symbol value to the constant name. + symbol_map: FxHashMap, } impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); @@ -889,10 +889,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); if let Ok(value) = value.to_u32(); then { - // SAFETY: We're converting the raw bytes of the symbol value back - // into a Symbol instance. - let symbol = unsafe { std::mem::transmute::(value) }; - self.symbol_map.insert(symbol.to_string(), item.ident.to_string()); + self.symbol_map.insert(value, item.ident.to_string()); } } } @@ -905,7 +902,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - if let Some(symbol_const) = self.symbol_map.get(&arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(symbol_const) = self.symbol_map.get(&value); then { span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From 1d6fac616767d84156e018e2a78582b7164241fb Mon Sep 17 00:00:00 2001 From: Naughty Date: Wed, 16 Dec 2020 20:48:03 -0300 Subject: Typo: std::fs::crate_dir -> std::fs::create_dir --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 4002fb655a5..200b6a565cc 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. /// - /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. /// /// **Known problems:** None. /// -- cgit 1.4.1-3-g733a5 From bb68ec6cfc76a6ec69d21c0f64dbc4aa99158958 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 17 Dec 2020 15:24:44 +0100 Subject: Apply suggestion from PR review --- clippy_lints/src/doc.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 08134cc16c0..aba65532795 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -407,15 +407,18 @@ fn check_doc<'a, Events: Iterator, Range { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { - let infos = lang.split(',').collect::>(); - is_rust = !infos.iter().any(|&i| i == "ignore") - && infos - .iter() - .any(|i| i.is_empty() || i.starts_with("edition") || RUST_CODE.contains(&i)); - edition = infos - .iter() - .find_map(|i| i.starts_with("edition").then(|| i[7..].parse::().ok())) - .flatten(); + for item in lang.split(',') { + if item == "ignore" { + is_rust = false; + break; + } + if let Some(stripped) = item.strip_prefix("edition") { + is_rust = true; + edition = stripped.parse::().ok(); + } else if item.is_empty() || RUST_CODE.contains(&item) { + is_rust = true; + } + } } }, End(CodeBlock(_)) => { -- cgit 1.4.1-3-g733a5 From 1eb7608a2e7284eff2d0ba80705f1cf28a1117e5 Mon Sep 17 00:00:00 2001 From: Andrew Houts Date: Thu, 17 Dec 2020 21:09:55 -0600 Subject: make needless_update ignore non_exhaustive structs --- clippy_lints/src/needless_update.rs | 21 +++++++++++++++++++-- tests/ui/needless_update.rs | 11 +++++++++++ tests/ui/needless_update.stderr | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 98e9078094a..1e387b518c3 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -21,7 +21,14 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// + /// # + /// # #[non_exhaustive] + /// # struct Options { + /// # a: bool, + /// # b: i32, + /// # } + /// # let default_options = Options { a: false, b: 0 }; + /// # /// // Bad /// Point { /// x: 1, @@ -36,6 +43,14 @@ declare_clippy_lint! { /// y: 1, /// ..zero_point /// }; + /// + /// // this lint is not applied to structs marked with [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html) + /// // Ok + /// Options { + /// a: true, + /// b: 321, + /// ..default_options + /// }; /// ``` pub NEEDLESS_UPDATE, complexity, @@ -49,7 +64,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() { + if fields.len() == def.non_enum_variant().fields.len() + && !def.variants[0_usize.into()].is_field_list_non_exhaustive() + { span_lint( cx, NEEDLESS_UPDATE, diff --git a/tests/ui/needless_update.rs b/tests/ui/needless_update.rs index bfa005a19f9..b93ff048a62 100644 --- a/tests/ui/needless_update.rs +++ b/tests/ui/needless_update.rs @@ -6,9 +6,20 @@ struct S { pub b: i32, } +#[non_exhaustive] +struct T { + pub x: i32, + pub y: i32, +} + fn main() { let base = S { a: 0, b: 0 }; S { ..base }; // no error S { a: 1, ..base }; // no error S { a: 1, b: 1, ..base }; + + let base = T { x: 0, y: 0 }; + T { ..base }; // no error + T { x: 1, ..base }; // no error + T { x: 1, y: 1, ..base }; // no error } diff --git a/tests/ui/needless_update.stderr b/tests/ui/needless_update.stderr index 133c834880d..b154b3b306d 100644 --- a/tests/ui/needless_update.stderr +++ b/tests/ui/needless_update.stderr @@ -1,5 +1,5 @@ error: struct update has no effect, all the fields in the struct have already been specified - --> $DIR/needless_update.rs:13:23 + --> $DIR/needless_update.rs:19:23 | LL | S { a: 1, b: 1, ..base }; | ^^^^ -- cgit 1.4.1-3-g733a5 From 920c9a4aaefbc4bade7032e32ec099bff1c7b58a Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Fri, 18 Dec 2020 10:55:09 +0530 Subject: add more lints to msrv docs --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 6403ff6dad1..32d7840a451 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), -- cgit 1.4.1-3-g733a5 From a24c6f14fa89dafc879ad76a185f43bc50f0bbb9 Mon Sep 17 00:00:00 2001 From: Andrew Houts Date: Fri, 18 Dec 2020 19:15:05 -0600 Subject: remove example --- clippy_lints/src/needless_update.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 1e387b518c3..41cf541ecf5 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -8,6 +8,9 @@ declare_clippy_lint! { /// **What it does:** Checks for needlessly including a base struct on update /// when all fields are changed anyway. /// + /// This lint is not applied to structs marked with + /// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html). + /// /// **Why is this bad?** This will cost resources (because the base has to be /// somewhere), and make the code less readable. /// @@ -21,14 +24,7 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// # - /// # #[non_exhaustive] - /// # struct Options { - /// # a: bool, - /// # b: i32, - /// # } - /// # let default_options = Options { a: false, b: 0 }; - /// # + /// /// // Bad /// Point { /// x: 1, @@ -43,14 +39,6 @@ declare_clippy_lint! { /// y: 1, /// ..zero_point /// }; - /// - /// // this lint is not applied to structs marked with [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html) - /// // Ok - /// Options { - /// a: true, - /// b: 321, - /// ..default_options - /// }; /// ``` pub NEEDLESS_UPDATE, complexity, -- cgit 1.4.1-3-g733a5 From a451b2af30c173b081f5d6150e558a65062a1dc8 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sat, 19 Dec 2020 15:49:42 +0100 Subject: Added from_over_into lint --- CHANGELOG.md | 1 + clippy_lints/src/from_over_into.rs | 63 ++++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 +++ tests/ui/from_over_into.rs | 21 +++++++++++++ tests/ui/from_over_into.stderr | 15 +++++++++ tests/ui/unused_unit.fixed | 1 + tests/ui/unused_unit.rs | 1 + tests/ui/unused_unit.stderr | 38 +++++++++++------------ 8 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 clippy_lints/src/from_over_into.rs create mode 100644 tests/ui/from_over_into.rs create mode 100644 tests/ui/from_over_into.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index af3b1c1db2a..de8da99cdee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1841,6 +1841,7 @@ Released 2018-09-13 [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs new file mode 100644 index 00000000000..fe7120b0f9a --- /dev/null +++ b/clippy_lints/src/from_over_into.rs @@ -0,0 +1,63 @@ +use crate::utils::paths::INTO; +use crate::utils::{match_def_path, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. + /// + /// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct StringWrapper(String); + /// + /// impl Into for String { + /// fn into(self) -> StringWrapper { + /// StringWrapper(self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct StringWrapper(String); + /// + /// impl From for StringWrapper { + /// fn from(s: String) -> StringWrapper { + /// StringWrapper(s) + /// } + /// } + /// ``` + pub FROM_OVER_INTO, + style, + "Warns on implementations of `Into<..>` to use `From<..>`" +} + +declare_lint_pass!(FromOverInto => [FROM_OVER_INTO]); + +impl LateLintPass<'_> for FromOverInto { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); + if_chain! { + if let hir::ItemKind::Impl{ .. } = &item.kind; + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id); + if match_def_path(cx, impl_trait_ref.def_id, &INTO); + + then { + span_lint_and_help( + cx, + FROM_OVER_INTO, + item.span, + "An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true.", + None, + "consider implement From instead", + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 02ba422a2f5..58587481922 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -207,6 +207,7 @@ mod float_literal; mod floating_point_arithmetic; mod format; mod formatting; +mod from_over_into; mod functions; mod future_not_send; mod get_last_with_len; @@ -614,6 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, &formatting::SUSPICIOUS_ELSE_FORMATTING, &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + &from_over_into::FROM_OVER_INTO, &functions::DOUBLE_MUST_USE, &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, @@ -1203,6 +1205,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box from_over_into::FromOverInto); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); @@ -1417,6 +1420,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(&from_over_into::FROM_OVER_INTO), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), @@ -1663,6 +1667,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(&from_over_into::FROM_OVER_INTO), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::RESULT_UNIT_ERR), diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs new file mode 100644 index 00000000000..292d0924fb1 --- /dev/null +++ b/tests/ui/from_over_into.rs @@ -0,0 +1,21 @@ +#![warn(clippy::from_over_into)] + +// this should throw an error +struct StringWrapper(String); + +impl Into for String { + fn into(self) -> StringWrapper { + StringWrapper(self) + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr new file mode 100644 index 00000000000..17f30fa837e --- /dev/null +++ b/tests/ui/from_over_into.stderr @@ -0,0 +1,15 @@ +error: An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true. + --> $DIR/from_over_into.rs:6:1 + | +LL | / impl Into for String { +LL | | fn into(self) -> StringWrapper { +LL | | StringWrapper(self) +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::from-over-into` implied by `-D warnings` + = help: consider implement From instead + +error: aborting due to previous error + diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 7afc5361356..a192ebde3eb 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -11,6 +11,7 @@ #![deny(clippy::unused_unit)] #![allow(dead_code)] +#![allow(clippy::from_over_into)] struct Unitter; impl Unitter { diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 96cef1ed5a5..96041a7dd85 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -11,6 +11,7 @@ #![deny(clippy::unused_unit)] #![allow(dead_code)] +#![allow(clippy::from_over_into)] struct Unitter; impl Unitter { diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index c45634c2b6d..02038b5fb6b 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,5 +1,5 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:28 + --> $DIR/unused_unit.rs:19:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` @@ -11,109 +11,109 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:18 + --> $DIR/unused_unit.rs:20:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:58 + --> $DIR/unused_unit.rs:19:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:26 + --> $DIR/unused_unit.rs:21:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:18 + --> $DIR/unused_unit.rs:28:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:28:9 + --> $DIR/unused_unit.rs:29:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:29 + --> $DIR/unused_unit.rs:34:29 | 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:19 + --> $DIR/unused_unit.rs:36:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:16 + --> $DIR/unused_unit.rs:37:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:29 + --> $DIR/unused_unit.rs:41:29 | 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:19 + --> $DIR/unused_unit.rs:43:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:16 + --> $DIR/unused_unit.rs:44:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:17 + --> $DIR/unused_unit.rs:47:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:46:26 + --> $DIR/unused_unit.rs:47:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:56:14 + --> $DIR/unused_unit.rs:57:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:58:11 + --> $DIR/unused_unit.rs:59:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:75:10 + --> $DIR/unused_unit.rs:76:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:78:11 + --> $DIR/unused_unit.rs:79:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:81:11 + --> $DIR/unused_unit.rs:82:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` -- cgit 1.4.1-3-g733a5 From dd005c17e72a48f8579cd26a7165c960b04f6aa3 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sun, 20 Dec 2020 13:00:17 +0100 Subject: Added MSRV and fixed typo --- clippy_lints/src/from_over_into.rs | 30 +++++++++++++++++++++++++----- clippy_lints/src/lib.rs | 2 +- tests/ui/from_over_into.stderr | 2 +- tests/ui/min_rust_version_attr.rs | 8 ++++++++ tests/ui/min_rust_version_attr.stderr | 8 ++++---- 5 files changed, 39 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index fe7120b0f9a..c7988d6f01f 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,9 +1,12 @@ use crate::utils::paths::INTO; -use crate::utils::{match_def_path, span_lint_and_help}; +use crate::utils::{match_def_path, meets_msrv, span_lint_and_help}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); declare_clippy_lint! { /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. @@ -38,10 +41,25 @@ declare_clippy_lint! { "Warns on implementations of `Into<..>` to use `From<..>`" } -declare_lint_pass!(FromOverInto => [FROM_OVER_INTO]); +pub struct FromOverInto { + msrv: Option, +} + +impl FromOverInto { + #[must_use] + pub fn new(msrv: Option) -> Self { + FromOverInto { msrv } + } +} + +impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl LateLintPass<'_> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) { + return; + } + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); if_chain! { if let hir::ItemKind::Impl{ .. } = &item.kind; @@ -55,9 +73,11 @@ impl LateLintPass<'_> for FromOverInto { item.span, "An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true.", None, - "consider implement From instead", + "consider to implement From instead", ); } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58587481922..35b057d7b6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1016,6 +1016,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); @@ -1205,7 +1206,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); - store.register_late_pass(|| box from_over_into::FromOverInto); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 17f30fa837e..c9c8e7c4b53 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -9,7 +9,7 @@ LL | | } | |_^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider implement From instead + = help: consider to implement From instead error: aborting due to previous error diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 3848bca3207..0f47f1cbc40 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -57,6 +57,14 @@ pub fn checked_conversion() { let _ = value <= (u32::MAX as i64) && value >= 0; } +pub struct FromOverInto(String); + +impl Into for String { + fn into(self) -> FromOverInto { + FromOverInto(self) + } +} + pub fn filter_map_next() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 34805263104..e3e3b335cbe 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:142:24 + --> $DIR/min_rust_version_attr.rs:150:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:141:9 + --> $DIR/min_rust_version_attr.rs:149:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:154:24 + --> $DIR/min_rust_version_attr.rs:162:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:153:9 + --> $DIR/min_rust_version_attr.rs:161:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 7e641c8be774bba047d7d9ee57455957fd4e2a3b Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sun, 20 Dec 2020 20:10:00 +0100 Subject: Fixed error messages --- clippy_lints/src/from_over_into.rs | 4 ++-- tests/ui/from_over_into.stderr | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index c7988d6f01f..083101ab1c6 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -71,9 +71,9 @@ impl LateLintPass<'_> for FromOverInto { cx, FROM_OVER_INTO, item.span, - "An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true.", + "An implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true.", None, - "consider to implement From instead", + "consider to implement `From` instead", ); } } diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index c9c8e7c4b53..0825d47add2 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,4 +1,4 @@ -error: An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true. +error: An implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true. --> $DIR/from_over_into.rs:6:1 | LL | / impl Into for String { @@ -9,7 +9,7 @@ LL | | } | |_^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider to implement From instead + = help: consider to implement `From` instead error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 0d6c128bfd41b61f246f603cfcd9ee4fc97ca3a0 Mon Sep 17 00:00:00 2001 From: llogiq Date: Mon, 21 Dec 2020 09:18:30 +0100 Subject: Update clippy_lints/src/from_over_into.rs Co-authored-by: Takayuki Nakata --- clippy_lints/src/from_over_into.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 083101ab1c6..1e7e5f53cc2 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -71,7 +71,7 @@ impl LateLintPass<'_> for FromOverInto { cx, FROM_OVER_INTO, item.span, - "An implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true.", + "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, "consider to implement `From` instead", ); -- cgit 1.4.1-3-g733a5 From 12bd244caa4547b4b84108d892e85bf953b1a408 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 21 Dec 2020 11:09:49 +0100 Subject: Don't trigger large_enum_variant in external macros --- clippy_lints/src/large_enum_variant.rs | 4 ++++ tests/ui/auxiliary/macro_rules.rs | 10 ++++++++++ tests/ui/large_enum_variant.rs | 9 ++++++++- tests/ui/large_enum_variant.stderr | 18 +++++++++--------- 4 files changed, 31 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 3c7880d74ee..ad9b4f357a7 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -4,6 +4,7 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_target::abi::LayoutOf; @@ -58,6 +59,9 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(did); diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index f985a15eda2..18324823468 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -84,3 +84,13 @@ macro_rules! as_conv { 0u32 as u64 }; } + +#[macro_export] +macro_rules! large_enum_variant { + () => { + enum LargeEnumInMacro { + A(i32), + B([i32; 8000]), + } + }; +} diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index 852ef5fec0e..d22fee3f27b 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -1,7 +1,12 @@ +// aux-build:macro_rules.rs + #![allow(dead_code)] #![allow(unused_variables)] #![warn(clippy::large_enum_variant)] +#[macro_use] +extern crate macro_rules; + enum LargeEnum { A(i32), B([i32; 8000]), @@ -51,4 +56,6 @@ enum LargeEnumOk { LargeB([i32; 8001]), } -fn main() {} +fn main() { + large_enum_variant!(); +} diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index 8ce641a81f2..d39a4d462aa 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -1,12 +1,12 @@ error: large size difference between variants - --> $DIR/large_enum_variant.rs:7:5 + --> $DIR/large_enum_variant.rs:12:5 | LL | B([i32; 8000]), | ^^^^^^^^^^^^^^ this variant is 32000 bytes | = note: `-D clippy::large-enum-variant` implied by `-D warnings` note: and the second-largest variant is 4 bytes: - --> $DIR/large_enum_variant.rs:6:5 + --> $DIR/large_enum_variant.rs:11:5 | LL | A(i32), | ^^^^^^ @@ -16,13 +16,13 @@ LL | B(Box<[i32; 8000]>), | ^^^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:31:5 + --> $DIR/large_enum_variant.rs:36:5 | LL | ContainingLargeEnum(LargeEnum), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:30:5 + --> $DIR/large_enum_variant.rs:35:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ @@ -32,30 +32,30 @@ LL | ContainingLargeEnum(Box), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:41:5 + --> $DIR/large_enum_variant.rs:46:5 | LL | StructLikeLarge { x: [i32; 8000], y: i32 }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:40:5 + --> $DIR/large_enum_variant.rs:45:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:41:5 + --> $DIR/large_enum_variant.rs:46:5 | LL | StructLikeLarge { x: [i32; 8000], y: i32 }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:46:5 + --> $DIR/large_enum_variant.rs:51:5 | LL | StructLikeLarge2 { x: [i32; 8000] }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:45:5 + --> $DIR/large_enum_variant.rs:50:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From af22613a25ad866a600bec13995ec16c993e5aac Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 22 Dec 2020 03:11:35 +0100 Subject: remove clone in manual_async_fn lint --- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/utils/mod.rs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 7b3b450ef93..29439e52c48 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = position_before_rarrow(header_snip.clone()); + if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index f61fd2ecd73..a31cd5fda84 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -120,7 +120,7 @@ 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())) { - position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { ( #[allow(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 424856090f2..1c68e837c4a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -788,8 +788,7 @@ pub fn indent_of(cx: &T, span: Span) -> Option { /// fn into3(self) -> () {} /// ^ /// ``` -#[allow(clippy::needless_pass_by_value)] -pub fn position_before_rarrow(s: String) -> Option { +pub fn position_before_rarrow(s: &str) -> Option { s.rfind("->").map(|rpos| { let mut rpos = rpos; let chars: Vec = s.chars().collect(); -- cgit 1.4.1-3-g733a5 From 8bdf34e10cfa47b6e0cfccd5d0f8e6a5c079bc30 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Nov 2020 12:26:35 -0600 Subject: Fix default initialized fields in suggestion The default value for a field type does not necessarily match the default value for that field in the struct Default. --- clippy_lints/src/default.rs | 6 ------ tests/ui/field_reassign_with_default.stderr | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index f69f6f1412a..adcc17266d2 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -165,12 +165,6 @@ impl LateLintPass<'_> for Default { let stmt = &block.stmts[stmt_idx]; if let StmtKind::Local(preceding_local) = &stmt.kind { - // filter out fields like `= Default::default()`, because the FRU already covers them - let assigned_fields = assigned_fields - .into_iter() - .filter(|(_, rhs)| !is_expr_default(rhs, cx)) - .collect::)>>(); - // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. let ext_with_default = !fields_of_type(binding_type) .iter() diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index c788ebae552..9a2bc778c3f 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -53,7 +53,7 @@ error: field assignment outside of initializer for an instance created with Defa LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A::default()` and removing relevant reassignments +note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments --> $DIR/field_reassign_with_default.rs:90:5 | LL | let mut a: A = Default::default(); @@ -65,7 +65,7 @@ error: field assignment outside of initializer for an instance created with Defa LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments +note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments --> $DIR/field_reassign_with_default.rs:94:5 | LL | let mut a: A = Default::default(); -- cgit 1.4.1-3-g733a5 From 73ce34a1970e860d14111127dd11d9ba56c41c3a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Nov 2020 13:20:53 -0600 Subject: Remove redundant shadow check There is already an assertion that consecutive lines assign to a struct field. --- clippy_lints/src/default.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index adcc17266d2..c3fe77f6250 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -122,15 +122,8 @@ impl LateLintPass<'_> for Default { let mut assigned_fields = Vec::new(); let mut cancel_lint = false; for consecutive_statement in &block.stmts[stmt_idx + 1..] { - // interrupt if the statement is a let binding (`Local`) that shadows the original - // binding - if stmt_shadows_binding(consecutive_statement, binding_name) { - break; - } // find out if and which field was set by this `consecutive_statement` - else if let Some((field_ident, assign_rhs)) = - field_reassigned_by_stmt(consecutive_statement, binding_name) - { + if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { // interrupt and cancel lint if assign_rhs references the original binding if contains_name(binding_name, assign_rhs) { cancel_lint = true; @@ -152,7 +145,7 @@ impl LateLintPass<'_> for Default { first_assign = Some(consecutive_statement); } } - // interrupt also if no field was assigned, since we only want to look at consecutive statements + // interrupt if no field was assigned, since we only want to look at consecutive statements else { break; } @@ -256,15 +249,6 @@ fn enumerate_bindings_using_default<'tcx>( .collect() } -fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool { - if let StmtKind::Local(local) = &this.kind { - if let PatKind::Binding(_, _, ident, _) = local.pat.kind { - return ident.name == shadowed; - } - } - false -} - /// Returns the reassigned field and the assigning expression (right-hand side of assign). fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { -- cgit 1.4.1-3-g733a5 From c6450c70ddb5354bc2218bff20a325ded9682613 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Nov 2020 09:23:24 -0600 Subject: Fix field_reassign_with_default for private fields --- clippy_lints/src/default.rs | 162 ++++++++++++++------------------ tests/ui/field_reassign_with_default.rs | 12 +++ 2 files changed, 81 insertions(+), 93 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index c3fe77f6250..b0d7c7b3baa 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Adt, Ty}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default { } fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { - // find all binding statements like `let mut _ = T::default()` where `T::default()` is the - // `default` method of the `Default` trait, and store statement index in current block being - // checked and the name of the bound variable - let binding_statements_using_default = enumerate_bindings_using_default(cx, block); - // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding - for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { - // the last statement of a block cannot trigger the lint - if stmt_idx == block.stmts.len() - 1 { - break; - } + let stmts_head = match block.stmts { + // Skip the last statement since there cannot possibly be any following statements that re-assign fields. + [head @ .., _] if !head.is_empty() => head, + _ => return, + }; + for (stmt_idx, stmt) in stmts_head.iter().enumerate() { + // find all binding statements like `let mut _ = T::default()` where `T::default()` is the + // `default` method of the `Default` trait, and store statement index in current block being + // checked and the name of the bound variable + let (local, variant, binding_name, binding_type, span) = if_chain! { + // only take `let ...` statements + if let StmtKind::Local(local) = stmt.kind; + if let Some(expr) = local.init; + // only take bindings to identifiers + if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; + // only when assigning `... = Default::default()` + if is_expr_default(expr, cx); + let binding_type = cx.typeck_results().node_type(binding_id); + if let Some(adt) = binding_type.ty_adt_def(); + if adt.is_struct(); + let variant = adt.non_enum_variant(); + if adt.did.is_local() || !variant.is_field_list_non_exhaustive(); + let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id(); + if variant + .fields + .iter() + .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); + then { + (local, variant, ident.name, binding_type, expr.span) + } else { + continue; + } + }; // find all "later statement"'s where the fields of the binding set as // Default::default() get reassigned, unless the reassignment refers to the original binding @@ -154,49 +177,45 @@ impl LateLintPass<'_> for Default { // if there are incorrectly assigned fields, do a span_lint_and_note to suggest // construction using `Ty { fields, ..Default::default() }` if !assigned_fields.is_empty() && !cancel_lint { - // take the original assignment as span - let stmt = &block.stmts[stmt_idx]; - - if let StmtKind::Local(preceding_local) = &stmt.kind { - // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. - let ext_with_default = !fields_of_type(binding_type) - .iter() - .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. + let ext_with_default = !variant + .fields + .iter() + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name)); - let field_list = assigned_fields - .into_iter() - .map(|(field, rhs)| { - // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); - format!("{}: {}", field, value_snippet) - }) - .collect::>() - .join(", "); + let field_list = assigned_fields + .into_iter() + .map(|(field, rhs)| { + // extract and store the assigned value for help message + let value_snippet = snippet(cx, rhs.span, ".."); + format!("{}: {}", field, value_snippet) + }) + .collect::>() + .join(", "); - let sugg = if ext_with_default { - if field_list.is_empty() { - format!("{}::default()", binding_type) - } else { - format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) - } + let sugg = if ext_with_default { + if field_list.is_empty() { + format!("{}::default()", binding_type) } else { - format!("{} {{ {} }}", binding_type, field_list) - }; + format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + } + } else { + format!("{} {{ {} }}", binding_type, field_list) + }; - // span lint once per statement that binds default - span_lint_and_note( - cx, - FIELD_REASSIGN_WITH_DEFAULT, - first_assign.unwrap().span, - "field assignment outside of initializer for an instance created with Default::default()", - Some(preceding_local.span), - &format!( - "consider initializing the variable with `{}` and removing relevant reassignments", - sugg - ), - ); - self.reassigned_linted.insert(span); - } + // span lint once per statement that binds default + span_lint_and_note( + cx, + FIELD_REASSIGN_WITH_DEFAULT, + first_assign.unwrap().span, + "field assignment outside of initializer for an instance created with Default::default()", + Some(local.span), + &format!( + "consider initializing the variable with `{}` and removing relevant reassignments", + sugg + ), + ); + self.reassigned_linted.insert(span); } } } @@ -217,38 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool } } -/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except -/// for when the pattern type is a tuple. -fn enumerate_bindings_using_default<'tcx>( - cx: &LateContext<'tcx>, - block: &Block<'tcx>, -) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { - block - .stmts - .iter() - .enumerate() - .filter_map(|(idx, stmt)| { - if_chain! { - // only take `let ...` statements - if let StmtKind::Local(ref local) = stmt.kind; - // only take bindings to identifiers - if let PatKind::Binding(_, _, ident, _) = local.pat.kind; - // that are not tuples - let ty = cx.typeck_results().pat_ty(local.pat); - if !matches!(ty.kind(), ty::Tuple(_)); - // only when assigning `... = Default::default()` - if let Some(ref expr) = local.init; - if is_expr_default(expr, cx); - then { - Some((idx, ident.name, ty, expr.span)) - } else { - None - } - } - }) - .collect() -} - /// Returns the reassigned field and the assigning expression (right-hand side of assign). fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { @@ -268,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op } } } - -/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. -fn fields_of_type(ty: Ty<'_>) -> Vec { - if let Adt(adt, _) = ty.kind() { - if adt.is_struct() { - let variant = &adt.non_enum_variant(); - return variant.fields.iter().map(|f| f.ident).collect(); - } - } - vec![] -} diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 79a30c22f95..3e0921022b4 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -107,4 +107,16 @@ fn main() { x.i = side_effect.next(); x.j = 2; x.i = side_effect.next(); + + // don't lint - some private fields + let mut x = m::F::default(); + x.a = 1; +} + +mod m { + #[derive(Default)] + pub struct F { + pub a: u64, + b: u64, + } } -- cgit 1.4.1-3-g733a5 From e1743ef5250d67260a2a1b0126708d969f80909f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 25 Dec 2020 08:59:34 +0900 Subject: Fix a style of texts in `map_err_ignore` --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index f3c0515b9bc..76fe8e776ea 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 contain and report the cause of the 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. /// @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { body_span, "`map_err(|_|...` wildcard pattern discards the original error", None, - "Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", ); } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8ee2941790d..37e87e64de2 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) + = help: consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From dfaea9c9677f97656ad75f6bacd78f8f87e1d339 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Fri, 25 Dec 2020 14:45:04 +0300 Subject: lint &PathBuf instead of &Path in PTR_ARG - extract get_only_generic_arg_snippet to improve readability --- clippy_dev/src/ra_setup.rs | 4 ++-- clippy_lints/src/ptr.rs | 60 ++++++++++++++++++++++++++++++++++------------ tests/ui/ptr_arg.rs | 22 +++++++++++++++++ tests/ui/ptr_arg.stderr | 45 +++++++++++++++++++++++++++------- 4 files changed, 106 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index 40bf4a9505a..5f5048e79e7 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -3,7 +3,7 @@ use std::fs; use std::fs::File; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::{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. @@ -44,7 +44,7 @@ pub fn run(rustc_path: Option<&str>) { } fn inject_deps_into_manifest( - rustc_source_dir: &PathBuf, + rustc_source_dir: &Path, manifest_path: &str, cargo_toml: &str, lib_rs: &str, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index dcb643a28ae..c494a713631 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -182,20 +182,6 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym::vec_type) { - let mut ty_snippet = None; - if_chain! { - if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); - then { - let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).collect(); - if types.len() == 1 { - ty_snippet = snippet_opt(cx, types[0].span); - } - } - }; if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) { span_lint_and_then( cx, @@ -204,7 +190,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ with non-Vec-based slices.", |diag| { - if let Some(ref snippet) = ty_snippet { + if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { diag.span_suggestion( arg.span, "change this to", @@ -247,6 +233,33 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } + } else if match_type(cx, ty, &paths::PATH_BUF) { + if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) { + span_lint_and_then( + cx, + PTR_ARG, + arg.span, + "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.", + |diag| { + diag.span_suggestion( + arg.span, + "change this to", + "&Path".into(), + Applicability::Unspecified, + ); + for (clonespan, suggestion) in spans { + diag.span_suggestion_short( + clonespan, + &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { + Cow::Owned(format!("change `{}` to", x)) + }), + suggestion.into(), + Applicability::Unspecified, + ); + } + }, + ); + } } else if match_type(cx, ty, &paths::COW) { if_chain! { if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; @@ -309,6 +322,23 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } +fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).collect(); + if types.len() == 1; + then { + snippet_opt(cx, types[0].span) + } else { + None + } + } +} + fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(ref lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 541225e6351..e8854fb73d9 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -2,6 +2,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; +use std::path::PathBuf; fn do_vec(x: &Vec) { //Nothing here @@ -21,6 +22,15 @@ fn do_str_mut(x: &mut String) { //Nothing here either } +fn do_path(x: &PathBuf) { + //Nothing here either +} + +fn do_path_mut(x: &mut PathBuf) { + // no error here + //Nothing here either +} + fn main() {} trait Foo { @@ -55,6 +65,14 @@ fn str_cloned(x: &String) -> String { x.clone() } +fn path_cloned(x: &PathBuf) -> PathBuf { + let a = x.clone(); + let b = x.clone(); + let c = b.clone(); + let d = a.clone().clone().clone(); + x.clone() +} + fn false_positive_capacity(x: &Vec, y: &String) { let a = x.capacity(); let b = y.clone(); @@ -87,10 +105,12 @@ impl Foo2 for String { // Check that the allow attribute on parameters is honored mod issue_5644 { use std::borrow::Cow; + use std::path::PathBuf; fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } @@ -100,6 +120,7 @@ mod issue_5644 { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } @@ -109,6 +130,7 @@ mod issue_5644 { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 314f23497f9..70d1b2f5258 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -1,5 +1,5 @@ error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:6:14 + --> $DIR/ptr_arg.rs:7:14 | LL | fn do_vec(x: &Vec) { | ^^^^^^^^^ help: change this to: `&[i64]` @@ -7,19 +7,25 @@ LL | fn do_vec(x: &Vec) { = note: `-D clippy::ptr-arg` implied by `-D warnings` error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:15:14 + --> $DIR/ptr_arg.rs:16:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:25:15 + | +LL | fn do_path(x: &PathBuf) { + | ^^^^^^^^ help: change this to: `&Path` + error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:28:18 + --> $DIR/ptr_arg.rs:38:18 | LL | fn do_vec(x: &Vec); | ^^^^^^^^^ help: change this to: `&[i64]` error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:41:14 + --> $DIR/ptr_arg.rs:51:14 | LL | fn cloned(x: &Vec) -> Vec { | ^^^^^^^^ @@ -38,7 +44,7 @@ LL | x.to_owned() | error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:50:18 + --> $DIR/ptr_arg.rs:60:18 | LL | fn str_cloned(x: &String) -> String { | ^^^^^^^ @@ -60,8 +66,31 @@ help: change `x.clone()` to LL | x.to_string() | +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:68:19 + | +LL | fn path_cloned(x: &PathBuf) -> PathBuf { + | ^^^^^^^^ + | +help: change this to + | +LL | fn path_cloned(x: &Path) -> PathBuf { + | ^^^^^ +help: change `x.clone()` to + | +LL | let a = x.to_path_buf(); + | ^^^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | let b = x.to_path_buf(); + | ^^^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_path_buf() + | + error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:58:44 + --> $DIR/ptr_arg.rs:76:44 | LL | fn false_positive_capacity(x: &Vec, y: &String) { | ^^^^^^^ @@ -80,10 +109,10 @@ LL | let c = y; | ^ error: using a reference to `Cow` is not recommended. - --> $DIR/ptr_arg.rs:72:25 + --> $DIR/ptr_arg.rs:90:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 203715aa4ecebb167868100fe14b0eaa05133718 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Mon, 28 Dec 2020 01:09:04 +0300 Subject: don't ignore expression after first not matched method call in PtrCloneVisitor --- clippy_lints/src/utils/ptr.rs | 1 - tests/ui/ptr_arg.rs | 19 ++++++++++++++ tests/ui/ptr_arg.stderr | 59 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/ptr.rs b/clippy_lints/src/utils/ptr.rs index bd2c619f000..b330f3d890e 100644 --- a/clippy_lints/src/utils/ptr.rs +++ b/clippy_lints/src/utils/ptr.rs @@ -72,7 +72,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { } } } - return; } walk_expr(self, expr); } diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index e8854fb73d9..06370dfce65 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -136,3 +136,22 @@ mod issue_5644 { } } } + +mod issue6509 { + use std::path::PathBuf; + + fn foo_vec(vec: &Vec) { + let _ = vec.clone().pop(); + let _ = vec.clone().clone(); + } + + fn foo_path(path: &PathBuf) { + let _ = path.clone().pop(); + let _ = path.clone().clone(); + } + + fn foo_str(str: &PathBuf) { + let _ = str.clone().pop(); + let _ = str.clone().clone(); + } +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 70d1b2f5258..708318bbe29 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -114,5 +114,62 @@ error: using a reference to `Cow` is not recommended. LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: aborting due to 9 previous errors +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:143:21 + | +LL | fn foo_vec(vec: &Vec) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_vec(vec: &[u8]) { + | ^^^^^ +help: change `vec.clone()` to + | +LL | let _ = vec.to_owned().pop(); + | ^^^^^^^^^^^^^^ +help: change `vec.clone()` to + | +LL | let _ = vec.to_owned().clone(); + | ^^^^^^^^^^^^^^ + +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:148:23 + | +LL | fn foo_path(path: &PathBuf) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_path(path: &Path) { + | ^^^^^ +help: change `path.clone()` to + | +LL | let _ = path.to_path_buf().pop(); + | ^^^^^^^^^^^^^^^^^^ +help: change `path.clone()` to + | +LL | let _ = path.to_path_buf().clone(); + | ^^^^^^^^^^^^^^^^^^ + +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:153:21 + | +LL | fn foo_str(str: &PathBuf) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_str(str: &Path) { + | ^^^^^ +help: change `str.clone()` to + | +LL | let _ = str.to_path_buf().pop(); + | ^^^^^^^^^^^^^^^^^ +help: change `str.clone()` to + | +LL | let _ = str.to_path_buf().clone(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 469281c0dd6abb13b0b0067e0ed977e70145183c Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 28 Dec 2020 18:59:35 +0100 Subject: Check if never type feature is enabled by TyCtxt before suggesting empty enum lint --- clippy_lints/src/empty_enum.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index a249117d182..585709660a4 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -44,7 +44,9 @@ impl<'tcx> LateLintPass<'tcx> for EmptyEnum { if let ItemKind::Enum(..) = item.kind { let ty = cx.tcx.type_of(did); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - if adt.variants.is_empty() { + + // Only suggest the never type if the feature is enabled + if adt.variants.is_empty() && cx.tcx.features().never_type { span_lint_and_help( cx, EMPTY_ENUM, -- cgit 1.4.1-3-g733a5 From 275988cb73e5ad9e7628c7eb424be9cdcec57284 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 28 Dec 2020 20:44:21 +0100 Subject: Add additional lint doc to known problems section --- clippy_lints/src/empty_enum.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 585709660a4..557a7c6ba98 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -8,6 +8,10 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for `enum`s with no variants. /// + /// As of this writing, the never type is still a + /// nightly-only experimental API. Therefore, this lint is only triggered + /// if the never type is enabled + /// /// **Why is this bad?** If you want to introduce a type which /// can't be instantiated, you should use `!` (the never type), /// or a wrapper around it, because `!` has more extensive -- cgit 1.4.1-3-g733a5 From e5a1f22f486d70676620cc67cad45ecf359fb56e Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 1 Jan 2021 16:21:31 +0100 Subject: Initial support for Rust 2021. Clippy treated Rust 2021 as Rust 2015, because 2018 was checked with `==` instead of `>=`. This fixes that, such that 2018-specific things are also enabled for 2021. --- clippy_lints/src/macro_use.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index b4b4b3dc18d..bb52888883a 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -105,7 +105,7 @@ impl MacroUseImports { impl<'tcx> LateLintPass<'tcx> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if_chain! { - if cx.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 diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 35b38eca14d..1fc4ff5c2e6 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -40,7 +40,7 @@ impl EarlyLintPass for SingleComponentPathImports { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if_chain! { if !in_macro(item.span); - if cx.sess.opts.edition == Edition::Edition2018; + if cx.sess.opts.edition >= Edition::Edition2018; if !item.vis.kind.is_pub(); if let ItemKind::Use(use_tree) = &item.kind; if let segments = &use_tree.prefix.segments; -- cgit 1.4.1-3-g733a5 From 5d48b91b40b3da6c29e8b6eea617bf8ed4033dec Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 1 Jan 2021 16:59:59 +0100 Subject: field_reassign_with_default: don't expand macros in suggestion fixes #6522 changelog: field_reassign_with_default: don't expand macros in lint suggestion (#6522) --- clippy_lints/src/default.rs | 6 +++-- tests/ui/field_reassign_with_default.rs | 9 +++++++ tests/ui/field_reassign_with_default.stderr | 38 +++++++++++++++++++---------- 3 files changed, 38 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index b0d7c7b3baa..9fa06d7cde9 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,4 +1,6 @@ -use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{ + any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite, +}; use crate::utils::{span_lint_and_note, span_lint_and_sugg}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -187,7 +189,7 @@ impl LateLintPass<'_> for Default { .into_iter() .map(|(field, rhs)| { // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); + let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); format!("{}: {}", field, value_snippet) }) .collect::>() diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 3e0921022b4..2990397c03e 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -11,6 +11,11 @@ struct B { j: i64, } +#[derive(Default)] +struct C { + i: Vec, + j: i64, +} /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -111,6 +116,10 @@ fn main() { // don't lint - some private fields let mut x = m::F::default(); x.a = 1; + + // don't expand macros in the suggestion (#6522) + let mut a: C = C::default(); + a.i = vec![1]; } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 9a2bc778c3f..59d2ac8ed69 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,75 +1,87 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:30:5 + --> $DIR/field_reassign_with_default.rs:35:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:29:5 + --> $DIR/field_reassign_with_default.rs:34:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:70:5 + --> $DIR/field_reassign_with_default.rs:75:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:69:5 + --> $DIR/field_reassign_with_default.rs:74:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:75:5 + --> $DIR/field_reassign_with_default.rs:80:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:74:5 + --> $DIR/field_reassign_with_default.rs:79:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:81:5 + --> $DIR/field_reassign_with_default.rs:86:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:80:5 + --> $DIR/field_reassign_with_default.rs:85:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:91:5 + --> $DIR/field_reassign_with_default.rs:96:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:90:5 + --> $DIR/field_reassign_with_default.rs:95:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:95:5 + --> $DIR/field_reassign_with_default.rs:100:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:94:5 + --> $DIR/field_reassign_with_default.rs:99:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:122:5 + | +LL | a.i = vec![1]; + | ^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:121:5 + | +LL | let mut a: C = C::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 1853f8b228298d5b91e3d03ee677da91574554b4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 2 Jan 2021 11:08:56 -0500 Subject: Add lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/vec_init_then_push.rs | 186 +++++++++++++++++++++++++++++++++ tests/ui/vec_init_then_push.rs | 21 ++++ tests/ui/vec_init_then_push.stderr | 34 ++++++ 5 files changed, 247 insertions(+) create mode 100644 clippy_lints/src/vec_init_then_push.rs create mode 100644 tests/ui/vec_init_then_push.rs create mode 100644 tests/ui/vec_init_then_push.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index de8da99cdee..572dd8c0c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2152,6 +2152,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_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a..6d84026d093 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -341,6 +341,7 @@ mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; +mod vec_init_then_push; mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; @@ -935,6 +936,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &vec::USELESS_VEC, + &vec_init_then_push::VEC_INIT_THEN_PUSH, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, &wildcard_dependencies::WILDCARD_DEPENDENCIES, @@ -1215,6 +1217,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); + store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1636,6 +1639,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_init_then_push::VEC_INIT_THEN_PUSH), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), @@ -1935,6 +1939,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), ]); store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs new file mode 100644 index 00000000000..0aadb453444 --- /dev/null +++ b/clippy_lints/src/vec_init_then_push.rs @@ -0,0 +1,186 @@ +use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span, Symbol}; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`. + /// + /// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than + /// multiple `push` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut v: Vec = Vec::new(); + /// v.push(0); + /// ``` + /// Use instead: + /// ```rust + /// let v: Vec = vec![0]; + /// ``` + pub VEC_INIT_THEN_PUSH, + perf, + "`push` immediately after `Vec` creation" +} + +impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]); + +#[derive(Default)] +pub struct VecInitThenPush { + searcher: Option, +} + +#[derive(Clone, Copy)] +enum VecInitKind { + New, + WithCapacity(u64), +} +struct VecPushSearcher { + init: VecInitKind, + name: Symbol, + lhs_is_local: bool, + lhs_span: Span, + err_span: Span, + found: u64, +} +impl VecPushSearcher { + fn display_err(&self, cx: &LateContext<'_>) { + match self.init { + _ if self.found == 0 => return, + VecInitKind::WithCapacity(x) if x > self.found => return, + _ => (), + }; + + let mut s = if self.lhs_is_local { + String::from("let ") + } else { + String::new() + }; + s.push_str(&snippet(cx, self.lhs_span, "..")); + s.push_str(" = vec![..];"); + + span_lint_and_sugg( + cx, + VEC_INIT_THEN_PUSH, + self.err_span, + "calls to `push` immediately after creation", + "consider using the `vec![]` macro", + s, + Applicability::HasPlaceholders, + ); + } +} + +impl LateLintPass<'_> for VecInitThenPush { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + self.searcher = None; + if_chain! { + if !in_external_macro(cx.sess(), local.span); + if let Some(init) = local.init; + if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind; + if let Some(init_kind) = get_vec_init_kind(cx, init); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: ident.name, + lhs_is_local: true, + lhs_span: local.ty.map(|t| local.pat.span.to(t.span)).unwrap_or(local.pat.span), + err_span: local.span, + found: 0, + }); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if self.searcher.is_none() { + if_chain! { + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Assign(left, right, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; + if let Some(name) = path.segments.get(0); + if let Some(init_kind) = get_vec_init_kind(cx, right); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: name.ident.name, + lhs_is_local: false, + lhs_span: left.span, + err_span: expr.span, + found: 0, + }); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let Some(searcher) = self.searcher.take() { + if_chain! { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind; + if path.ident.name.as_str() == "push"; + if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind; + if let [self_name] = self_path.segments; + if self_name.ident.name == searcher.name; + then { + self.searcher = Some(VecPushSearcher { + found: searcher.found + 1, + err_span: searcher.err_span.to(stmt.span), + .. searcher + }); + } else { + searcher.display_err(cx); + } + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + if let Some(searcher) = self.searcher.take() { + searcher.display_err(cx); + } + } +} + +fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + if let ExprKind::Call(func, args) = expr.kind { + match func.kind { + ExprKind::Path(QPath::TypeRelative(ty, name)) + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) => + { + if name.ident.name.as_str() == "new" { + return Some(VecInitKind::New); + } else if name.ident.name.as_str() == "with_capacity" { + return args.get(0).and_then(|arg| { + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + Some(VecInitKind::WithCapacity(num as u64)) + } else { + None + } + } + }); + } + } + ExprKind::Path(QPath::Resolved(_, path)) + if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) => + { + return Some(VecInitKind::New); + } + _ => (), + } + } + None +} diff --git a/tests/ui/vec_init_then_push.rs b/tests/ui/vec_init_then_push.rs new file mode 100644 index 00000000000..642ce504009 --- /dev/null +++ b/tests/ui/vec_init_then_push.rs @@ -0,0 +1,21 @@ +#![allow(unused_variables)] +#![warn(clippy::vec_init_then_push)] + +fn main() { + let mut def_err: Vec = Default::default(); + def_err.push(0); + + let mut new_err = Vec::::new(); + new_err.push(1); + + let mut cap_err = Vec::with_capacity(2); + cap_err.push(0); + cap_err.push(1); + cap_err.push(2); + + let mut cap_ok = Vec::with_capacity(10); + cap_ok.push(0); + + new_err = Vec::new(); + new_err.push(0); +} diff --git a/tests/ui/vec_init_then_push.stderr b/tests/ui/vec_init_then_push.stderr new file mode 100644 index 00000000000..819ed47d099 --- /dev/null +++ b/tests/ui/vec_init_then_push.stderr @@ -0,0 +1,34 @@ +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:5:5 + | +LL | / let mut def_err: Vec = Default::default(); +LL | | def_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | + = note: `-D clippy::vec-init-then-push` implied by `-D warnings` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:8:5 + | +LL | / let mut new_err = Vec::::new(); +LL | | new_err.push(1); + | |____________________^ help: consider using the `vec![]` macro: `let mut new_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:11:5 + | +LL | / let mut cap_err = Vec::with_capacity(2); +LL | | cap_err.push(0); +LL | | cap_err.push(1); +LL | | cap_err.push(2); + | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:19:5 + | +LL | / new_err = Vec::new(); +LL | | new_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From d37ee6ffa0642a0812cbda4592be6650aa4eda08 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 2 Jan 2021 14:31:21 -0500 Subject: Fix lint errors --- clippy_lints/src/vec_init_then_push.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 0aadb453444..e2cbcc9108c 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span, Symbol}; +use std::convert::TryInto; declare_clippy_lint! { /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`. @@ -92,7 +93,7 @@ impl LateLintPass<'_> for VecInitThenPush { init: init_kind, name: ident.name, lhs_is_local: true, - lhs_span: local.ty.map(|t| local.pat.span.to(t.span)).unwrap_or(local.pat.span), + lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), err_span: local.span, found: 0, }); @@ -165,7 +166,7 @@ fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Op if let ExprKind::Lit(lit) = &arg.kind; if let LitKind::Int(num, _) = lit.node; then { - Some(VecInitKind::WithCapacity(num as u64)) + Some(VecInitKind::WithCapacity(num.try_into().ok()?)) } else { None } -- cgit 1.4.1-3-g733a5 From 7b5f54954aa18991edd6e22edfd9af6bc9bae24b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 3 Jan 2021 14:04:05 -0500 Subject: Fix docs: use type inference --- clippy_lints/src/vec_init_then_push.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index e2cbcc9108c..6249d7e867b 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -20,12 +20,12 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// let mut v: Vec = Vec::new(); + /// let mut v = Vec::new(); /// v.push(0); /// ``` /// Use instead: /// ```rust - /// let v: Vec = vec![0]; + /// let v = vec![0]; /// ``` pub VEC_INIT_THEN_PUSH, perf, -- cgit 1.4.1-3-g733a5 From 39f39d5405b7e55eb08cb927e78686a5ce7377d4 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 1 Jan 2021 17:43:24 +0100 Subject: match_like_matches_macro: strip refs in suggestion fixes #6503 changelog: match_like_matches_macro: strip refs in suggestion (#6503) --- clippy_lints/src/matches.rs | 10 +++- tests/ui/match_expr_like_matches_macro.fixed | 47 +++++++++++++++ tests/ui/match_expr_like_matches_macro.rs | 62 +++++++++++++++++++ tests/ui/match_expr_like_matches_macro.stderr | 85 ++++++++++++++++++++++++++- 4 files changed, 202 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 04b35835c6b..6372cb86616 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1185,6 +1185,14 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr } else { pat }; + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = ex; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { + if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() { + ex_new = ex_inner; + } + }; span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, @@ -1194,7 +1202,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr format!( "{}matches!({}, {})", if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex.span, "..", &mut applicability), + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), pat_and_guard, ), applicability, diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 84981a52597..319299862a7 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -99,4 +99,51 @@ fn main() { _ => false, }; } + + { + // should print "z" in suggestion (#6503) + let z = &Some(3); + let _z = matches!(z, Some(3)); + } + + { + // this could also print "z" in suggestion..? + let z = Some(3); + let _z = matches!(&z, Some(3)); + } + + { + enum AnEnum { + X, + Y, + } + + fn foo(_x: AnEnum) {} + + fn main() { + let z = AnEnum::X; + // we can't remove the reference here! + let _ = matches!(&z, AnEnum::X); + foo(z); + } + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + // we need the reference here because later val is consumed by fun() + let _res = matches!(&val, &Some(ref _a)); + fun(val); + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + let _res = matches!(&val, &Some(ref _a)); + fun(val); + } } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 94c7c3cadac..2ef6cf42387 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -119,4 +119,66 @@ fn main() { _ => false, }; } + + { + // should print "z" in suggestion (#6503) + let z = &Some(3); + let _z = match &z { + Some(3) => true, + _ => false, + }; + } + + { + // this could also print "z" in suggestion..? + let z = Some(3); + let _z = match &z { + Some(3) => true, + _ => false, + }; + } + + { + enum AnEnum { + X, + Y, + } + + fn foo(_x: AnEnum) {} + + fn main() { + let z = AnEnum::X; + // we can't remove the reference here! + let _ = match &z { + AnEnum::X => true, + _ => false, + }; + foo(z); + } + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + // we need the reference here because later val is consumed by fun() + let _res = match &val { + &Some(ref _a) => true, + _ => false, + }; + fun(val); + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + let _res = match &val { + &Some(ref _a) => true, + _ => false, + }; + fun(val); + } } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index c52e41c7889..f27b4e9cb20 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -70,5 +70,88 @@ LL | | _ => true, LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` -error: aborting due to 7 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:126:18 + | +LL | let _z = match &z { + | __________________^ +LL | | Some(3) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(z, Some(3))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:135:18 + | +LL | let _z = match &z { + | __________________^ +LL | | Some(3) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(&z, Some(3))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:152:21 + | +LL | let _ = match &z { + | _____________________^ +LL | | AnEnum::X => true, +LL | | _ => false, +LL | | }; + | |_____________^ help: try this: `matches!(&z, AnEnum::X)` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:166:20 + | +LL | let _res = match &val { + | ____________________^ +LL | | &Some(ref _a) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(&val, &Some(ref _a))` + +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/match_expr_like_matches_macro.rs:166:20 + | +LL | let _res = match &val { + | ____________________^ +LL | | &Some(ref _a) => true, +LL | | _ => false, +LL | | }; + | |_________^ + | + = note: `-D clippy::match-ref-pats` implied by `-D warnings` +help: try + | +LL | let _res = match val { +LL | Some(ref _a) => true, + | + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:178:20 + | +LL | let _res = match &val { + | ____________________^ +LL | | &Some(ref _a) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(&val, &Some(ref _a))` + +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/match_expr_like_matches_macro.rs:178:20 + | +LL | let _res = match &val { + | ____________________^ +LL | | &Some(ref _a) => true, +LL | | _ => false, +LL | | }; + | |_________^ + | +help: try + | +LL | let _res = match val { +LL | Some(ref _a) => true, + | + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 6dcec6ae863930b7056a68744759d2bfb3481f92 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 4 Jan 2021 08:25:38 +0100 Subject: collapsible_if: split collapsible_else_if into its own lint so we can enable/disable it particularly This splits up clippy::collapsible_if into collapsible_if for if x { if y { } } => if x && y { } and collapsible_else_if for if x { } else { if y { } } => if x { } else if y { } so that we can lint for only the latter but not the first if we desire. changelog: collapsible_if: split up linting for if x {} else { if y {} } into collapsible_else_if lint --- CHANGELOG.md | 1 + clippy_lints/src/collapsible_if.rs | 44 +++++++++++++++++++++++++------------ clippy_lints/src/lib.rs | 3 +++ tests/ui/collapsible_else_if.fixed | 2 ++ tests/ui/collapsible_else_if.rs | 2 ++ tests/ui/collapsible_else_if.stderr | 16 +++++++------- tests/ui/if_same_then_else2.rs | 1 + tests/ui/if_same_then_else2.stderr | 24 ++++++++++---------- 8 files changed, 59 insertions(+), 34 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index ec424068a54..b643627c2e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1896,6 +1896,7 @@ Released 2018-09-13 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 42bff564de0..93ccc76d0c9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -23,9 +23,7 @@ use rustc_errors::Applicability; declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed - /// by `&&`-combining their conditions and for `else { if ... }` expressions - /// that - /// can be collapsed to `else if ...`. + /// by `&&`-combining their conditions. /// /// **Why is this bad?** Each `if`-statement adds one level of nesting, which /// makes code look more complex than it really is. @@ -40,7 +38,31 @@ declare_clippy_lint! { /// } /// } /// - /// // or + /// ``` + /// + /// Should be written: + /// + /// ```rust.ignore + /// if x && y { + /// … + /// } + /// ``` + pub COLLAPSIBLE_IF, + style, + "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for collapsible `else { if ... }` expressions + /// that can be collapsed to `else if ...`. + /// + /// **Why is this bad?** Each `if`-statement adds one level of nesting, which + /// makes code look more complex than it really is. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore /// /// if x { /// … @@ -54,24 +76,18 @@ declare_clippy_lint! { /// Should be written: /// /// ```rust.ignore - /// if x && y { - /// … - /// } - /// - /// // or - /// /// if x { /// … /// } else if y { /// … /// } /// ``` - pub COLLAPSIBLE_IF, + pub COLLAPSIBLE_ELSE_IF, style, - "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)" + "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]); +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { @@ -112,7 +128,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - COLLAPSIBLE_IF, + COLLAPSIBLE_ELSE_IF, block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a..4f41b6c77e2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -556,6 +556,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cargo_common_metadata::CARGO_COMMON_METADATA, &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, + &collapsible_if::COLLAPSIBLE_ELSE_IF, &collapsible_if::COLLAPSIBLE_IF, &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, @@ -1384,6 +1385,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), + LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), @@ -1653,6 +1655,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index ce2a1c28c8a..fa4bc30e933 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 99c40b8d38e..bf6c1d1f894 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 3d1c458879e..ee3e11ae565 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -1,5 +1,5 @@ error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:12:12 + --> $DIR/collapsible_else_if.rs:14:12 | LL | } else { | ____________^ @@ -9,7 +9,7 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::collapsible-if` implied by `-D warnings` + = note: `-D clippy::collapsible-else-if` implied by `-D warnings` help: collapse nested if block | LL | } else if y == "world" { @@ -18,7 +18,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:20:12 + --> $DIR/collapsible_else_if.rs:22:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:28:12 + --> $DIR/collapsible_else_if.rs:30:12 | LL | } else { | ____________^ @@ -59,7 +59,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:39:12 + --> $DIR/collapsible_else_if.rs:41:12 | LL | } else { | ____________^ @@ -82,7 +82,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:50:12 + --> $DIR/collapsible_else_if.rs:52:12 | LL | } else { | ____________^ @@ -105,7 +105,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:61:12 + --> $DIR/collapsible_else_if.rs:63:12 | LL | } else { | ____________^ @@ -128,7 +128,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:72:12 + --> $DIR/collapsible_else_if.rs:74:12 | LL | } else { | ____________^ diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 8d54f75b5d1..e83ce47e563 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -1,6 +1,7 @@ #![warn(clippy::if_same_then_else)] #![allow( clippy::blacklisted_name, + clippy::collapsible_else_if, clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index da2be6c8aa5..f98e30fa376 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:20:12 + --> $DIR/if_same_then_else2.rs:21: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:11:13 + --> $DIR/if_same_then_else2.rs:12:13 | LL | if true { | _____________^ @@ -26,7 +26,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:34:12 + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:32:13 + --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { | _____________^ @@ -45,7 +45,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:41:12 + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ @@ -55,7 +55,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:39:13 + --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { | _____________^ @@ -64,7 +64,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:91:12 + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ @@ -74,7 +74,7 @@ LL | | }; | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:89:21 + --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { | _____________________^ @@ -83,7 +83,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:98:12 + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ @@ -93,7 +93,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:96:13 + --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { | _____________^ @@ -102,7 +102,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:123:12 + --> $DIR/if_same_then_else2.rs:124:12 | LL | } else { | ____________^ @@ -112,7 +112,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:120:20 + --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { | ____________________^ -- cgit 1.4.1-3-g733a5 From ba87acb44090412f5ace0a5ca655e8298d82b874 Mon Sep 17 00:00:00 2001 From: Benjamin Sparks Date: Sat, 26 Dec 2020 18:02:46 +0100 Subject: Implemented needless question mark lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/needless_question_mark.rs | 232 +++++++++++++++++++++++++++++ tests/ui/needless_question_mark.fixed | 163 ++++++++++++++++++++ tests/ui/needless_question_mark.rs | 163 ++++++++++++++++++++ tests/ui/needless_question_mark.stderr | 88 +++++++++++ tests/ui/try_err.fixed | 2 +- tests/ui/try_err.rs | 2 +- tests/ui/unit_arg.rs | 3 +- tests/ui/unit_arg.stderr | 20 +-- 10 files changed, 666 insertions(+), 13 deletions(-) create mode 100644 clippy_lints/src/needless_question_mark.rs create mode 100644 tests/ui/needless_question_mark.fixed create mode 100644 tests/ui/needless_question_mark.rs create mode 100644 tests/ui/needless_question_mark.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index de8da99cdee..41a92dd4c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1973,6 +1973,7 @@ Released 2018-09-13 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`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_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a..0299f8e12fa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -271,6 +271,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; +mod needless_question_mark; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -799,6 +800,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_question_mark::NEEDLESS_QUESTION_MARK, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -1019,6 +1021,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); @@ -1545,6 +1548,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_question_mark::NEEDLESS_QUESTION_MARK), 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), @@ -1803,6 +1807,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_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&no_effect::NO_EFFECT), diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs new file mode 100644 index 00000000000..783e6b716d4 --- /dev/null +++ b/clippy_lints/src/needless_question_mark.rs @@ -0,0 +1,232 @@ +use rustc_errors::Applicability; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +use crate::utils; +use if_chain::if_chain; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests alternatives for useless applications of `?` in terminating expressions + /// + /// **Why is this bad?** There's no reason to use ? to short-circuit when execution of the body will end there anyway. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// Some(to.magic?) + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| Ok(t.magic?)) + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// to.magic + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| t.magic) + /// } + /// ``` + pub NEEDLESS_QUESTION_MARK, + complexity, + "Suggest value.inner_option instead of Some(value.inner_option?). The same goes for Result." +} + +const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); +const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); + +pub struct NeedlessQuestionMark { + msrv: Option, +} + +impl NeedlessQuestionMark { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); + +#[derive(Debug)] +enum SomeOkCall<'a> { + SomeCall(&'a Expr<'a>, &'a Expr<'a>), + OkCall(&'a Expr<'a>, &'a Expr<'a>), +} + +impl LateLintPass<'_> for NeedlessQuestionMark { + /* + * The question mark operator is compatible with both Result and Option, + * from Rust 1.13 and 1.22 respectively. + */ + + /* + * What do we match: + * Expressions that look like this: + * Some(option?), Ok(result?) + * + * Where do we match: + * Last expression of a body + * Return statement + * A body's value (single line closure) + * + * What do we not match: + * Implicit calls to `from(..)` on the error value + */ + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + let e = match &expr.kind { + ExprKind::Ret(Some(e)) => e, + _ => return, + }; + + if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) { + emit_lint(cx, &ok_some_call); + } + } + + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + // Function / Closure block + let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind { + block.expr + } else { + // Single line closure + Some(&body.value) + }; + + if_chain! { + if let Some(expr) = expr_opt; + if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr); + then { + emit_lint(cx, &ok_some_call); + } + }; + } + + extract_msrv_attr!(LateContext); +} + +fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { + let (entire_expr, inner_expr) = match expr { + SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), + }; + + utils::span_lint_and_sugg( + cx, + NEEDLESS_QUESTION_MARK, + entire_expr.span, + "Question mark operator is useless here", + "try", + format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), + Applicability::MachineApplicable, + ); +} + +fn is_some_or_ok_call<'a>( + nqml: &NeedlessQuestionMark, + cx: &'a LateContext<'_>, + expr: &'a Expr<'_>, +) -> Option> { + if_chain! { + // Check outer expression matches CALL_IDENT(ARGUMENT) format + if let ExprKind::Call(path, args) = &expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + + // Extract inner expression from ARGUMENT + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; + if let ExprKind::Call(called, args) = &inner_expr_with_q.kind; + if args.len() == 1; + + if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; + then { + // Extract inner expr type from match argument generated by + // question mark operator + let inner_expr = &args[0]; + + let inner_ty = cx.typeck_results().expr_ty(inner_expr); + let outer_ty = cx.typeck_results().expr_ty(expr); + + // Check if outer and inner type are Option + let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type); + let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type); + + // Check for Option MSRV + let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); + if outer_is_some && inner_is_some && meets_option_msrv { + return Some(SomeOkCall::SomeCall(expr, inner_expr)); + } + + // Check if outer and inner type are Result + let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type); + let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type); + + // Additional check: if the error type of the Result can be converted + // via the From trait, then don't match + let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); + + // Must meet Result MSRV + let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); + if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + return Some(SomeOkCall::OkCall(expr, inner_expr)); + } + } + } + + None +} + +fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { + return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); +} + +fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed new file mode 100644 index 00000000000..70218f3f041 --- /dev/null +++ b/tests/ui/needless_question_mark.fixed @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return to.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return to.magic +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + to.magic +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + t.magic + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return tr.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return tr.magic +} + +fn simple_result_bad3(tr: TR) -> Result { + tr.magic +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + t.magic + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return t.magic; + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + to.magic // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs new file mode 100644 index 00000000000..60ac2c8d72e --- /dev/null +++ b/tests/ui/needless_question_mark.rs @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return Some(to.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return Some(to.magic?) +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + Some(to.magic?) +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| Some(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + Some(t.magic?) + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return Ok(tr.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return Ok(tr.magic?) +} + +fn simple_result_bad3(tr: TR) -> Result { + Ok(tr.magic?) +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| Ok(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + Ok(t.magic?) + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return Ok(t.magic?); + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr new file mode 100644 index 00000000000..b4eb21882ec --- /dev/null +++ b/tests/ui/needless_question_mark.stderr @@ -0,0 +1,88 @@ +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:17:12 + | +LL | return Some(to.magic?); + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + | + = note: `-D clippy::needless-question-mark` implied by `-D warnings` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:25:12 + | +LL | return Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:30:5 + | +LL | Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:35:21 + | +LL | to.and_then(|t| Some(t.magic?)) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:44:9 + | +LL | Some(t.magic?) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:49:12 + | +LL | return Ok(tr.magic?); + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:56:12 + | +LL | return Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:60:5 + | +LL | Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:64:21 + | +LL | tr.and_then(|t| Ok(t.magic?)) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:72:9 + | +LL | Ok(t.magic?) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:79:16 + | +LL | return Ok(t.magic?); + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:132:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:148:9 + | +LL | Some(to.magic?) // should be triggered + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:156:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: aborting due to 14 previous errors + diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 652b611208b..5b96bb59c5f 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 6bd479657b7..f220d697d2c 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 9ad16d36509..b6a7bc5a1cc 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -5,7 +5,8 @@ unused_variables, clippy::unused_unit, clippy::unnecessary_wraps, - clippy::or_fun_call + clippy::or_fun_call, + clippy::needless_question_mark )] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index c3a839a9bf8..094cff8c985 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:30:5 + --> $DIR/unit_arg.rs:31:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:34:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 + --> $DIR/unit_arg.rs:35:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:39:5 + --> $DIR/unit_arg.rs:40:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:43:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:43:5 + --> $DIR/unit_arg.rs:44:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:47:5 + --> $DIR/unit_arg.rs:48:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:58:13 + --> $DIR/unit_arg.rs:59:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:61:5 + --> $DIR/unit_arg.rs:62:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:95:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From bc97f5d2156e2fc42ff1a69ccbad9adb2b4568fb Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 4 Jan 2021 17:47:59 +0100 Subject: Address flip1995's review comments --- clippy_lints/src/empty_enum.rs | 15 +++++++++------ tests/ui/empty_enum_without_never_type.rs | 7 +++++++ 2 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 tests/ui/empty_enum_without_never_type.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 557a7c6ba98..4533d6447a7 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -8,12 +8,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for `enum`s with no variants. /// - /// As of this writing, the never type is still a + /// As of this writing, the `never_type` is still a /// nightly-only experimental API. Therefore, this lint is only triggered - /// if the never type is enabled + /// if the `never_type` is enabled. /// /// **Why is this bad?** If you want to introduce a type which - /// can't be instantiated, you should use `!` (the never type), + /// can't be instantiated, you should use `!` (the primitive type never), /// or a wrapper around it, because `!` has more extensive /// compiler support (type inference, etc...) and wrappers /// around it are the conventional way to define an uninhabited type. @@ -44,13 +44,16 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Only suggest the `never_type` if the feature is enabled + if !cx.tcx.features().never_type { + return; + } + let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(..) = item.kind { let ty = cx.tcx.type_of(did); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - - // Only suggest the never type if the feature is enabled - if adt.variants.is_empty() && cx.tcx.features().never_type { + if adt.variants.is_empty() { span_lint_and_help( cx, EMPTY_ENUM, diff --git a/tests/ui/empty_enum_without_never_type.rs b/tests/ui/empty_enum_without_never_type.rs new file mode 100644 index 00000000000..4cbdfc47910 --- /dev/null +++ b/tests/ui/empty_enum_without_never_type.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![warn(clippy::empty_enum)] + +// `never_type` is not enabled; this test has no stderr file +enum Empty {} + +fn main() {} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From a8d47b4b78a6e6a23dc3320ad3c9e6308f6d1934 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 4 Jan 2021 18:41:42 +0100 Subject: Run cargo dev fmt --- clippy_lints/src/empty_enum.rs | 2 +- tests/ui/empty_enum_without_never_type.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 4533d6447a7..853b3afdc3a 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// if the `never_type` is enabled. /// /// **Why is this bad?** If you want to introduce a type which - /// can't be instantiated, you should use `!` (the primitive type never), + /// can't be instantiated, you should use `!` (the primitive type "never"), /// or a wrapper around it, because `!` has more extensive /// compiler support (type inference, etc...) and wrappers /// around it are the conventional way to define an uninhabited type. diff --git a/tests/ui/empty_enum_without_never_type.rs b/tests/ui/empty_enum_without_never_type.rs index 4cbdfc47910..386677352e2 100644 --- a/tests/ui/empty_enum_without_never_type.rs +++ b/tests/ui/empty_enum_without_never_type.rs @@ -4,4 +4,4 @@ // `never_type` is not enabled; this test has no stderr file enum Empty {} -fn main() {} \ No newline at end of file +fn main() {} -- cgit 1.4.1-3-g733a5 From 4b478a5731956de7d19db9ac76fed81b2ae9db1c Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 4 Jan 2021 10:34:11 +1300 Subject: Add a new lint `ptr_as_ptr`, which checks for `as` casts between raw pointers without changing its mutability and suggest replacing it with `pointer::cast`. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/types.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++-- tests/ui/ptr_as_ptr.fixed | 30 ++++++++++++++ tests/ui/ptr_as_ptr.rs | 30 ++++++++++++++ tests/ui/ptr_as_ptr.stderr | 34 ++++++++++++++++ 6 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 tests/ui/ptr_as_ptr.fixed create mode 100644 tests/ui/ptr_as_ptr.rs create mode 100644 tests/ui/ptr_as_ptr.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c943fec00..64864c2e278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2141,6 +2141,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_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f57c6bd6324..37a56bc20c8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -912,6 +912,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::PTR_AS_PTR, &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, @@ -1222,6 +1223,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); + store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1348,6 +1350,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::LET_UNIT_VALUE), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), + LintId::of(&types::PTR_AS_PTR), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index fd74783335d..d9cf26ad4b3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -19,7 +19,8 @@ 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_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeAndMut, TypeckResults}; +use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; @@ -30,11 +31,13 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; +use crate::utils::sugg::Sugg; 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, 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, + last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, multispan_sugg, + numeric_literal::NumericLiteral, 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! { @@ -2878,3 +2881,91 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut { } } } + +const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); + +declare_clippy_lint! { + /// **What it does:** + /// Checks for `as` casts between raw pointers without changing its mutability, + /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// + /// **Why is this bad?** + /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr as *const i32; + /// let _ = mut_ptr as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr.cast::(); + /// let _ = mut_ptr.cast::(); + /// ``` + pub PTR_AS_PTR, + pedantic, + "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" +} + +pub struct PtrAsPtr { + msrv: Option, +} + +impl PtrAsPtr { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(PtrAsPtr => [PTR_AS_PTR]); + +impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &PTR_AS_PTR_MSRV) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + // The `U` in `pointer::cast` have to be `Sized` + // as explained here: https://github.com/rust-lang/rust/issues/60602. + if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env); + then { + let mut applicability = Applicability::MachineApplicable; + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => Cow::Borrowed(""), + TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), + _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + }; + span_lint_and_sugg( + cx, + PTR_AS_PTR, + expr.span, + "`as` casting between raw pointers without changing its mutability", + "try `pointer::cast`, a safer alternative", + format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), + applicability, + ); + } + } + } +} diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed new file mode 100644 index 00000000000..e0b79004cbd --- /dev/null +++ b/tests/ui/ptr_as_ptr.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = (*ptr_ptr).cast::(); + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr.cast(); + let _: *mut i32 = mut_ptr.cast(); +} diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs new file mode 100644 index 00000000000..f31940dbd1f --- /dev/null +++ b/tests/ui/ptr_as_ptr.rs @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = *ptr_ptr as *const i32; + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr as *const _; + let _: *mut i32 = mut_ptr as _; +} diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr new file mode 100644 index 00000000000..22217e18c54 --- /dev/null +++ b/tests/ui/ptr_as_ptr.stderr @@ -0,0 +1,34 @@ +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:9:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + | + = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:10:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:15:17 + | +LL | let _ = *ptr_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:28:25 + | +LL | let _: *const i32 = ptr as *const _; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:29:23 + | +LL | let _: *mut i32 = mut_ptr as _; + | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From dfa5d7e818c32ea48bc141799276f700ab9d8fb7 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 5 Jan 2021 10:17:31 +1300 Subject: Fix the MSRV and add the tests for MSRV --- clippy_lints/src/types.rs | 2 ++ tests/ui/ptr_as_ptr.fixed | 20 ++++++++++++++++++++ tests/ui/ptr_as_ptr.rs | 20 ++++++++++++++++++++ tests/ui/ptr_as_ptr.stderr | 24 ++++++++++++++++++------ 4 files changed, 60 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index d9cf26ad4b3..b21f81bd517 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2968,4 +2968,6 @@ impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { } } } + + extract_msrv_attr!(LateContext); } diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index e0b79004cbd..8346a9454f4 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] fn main() { let ptr: *const u32 = &42_u32; @@ -28,3 +29,22 @@ fn main() { let _: *const i32 = ptr.cast(); let _: *mut i32 = mut_ptr.cast(); } + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); +} diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index f31940dbd1f..b68d4bc0aac 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] fn main() { let ptr: *const u32 = &42_u32; @@ -28,3 +29,22 @@ fn main() { let _: *const i32 = ptr as *const _; let _: *mut i32 = mut_ptr as _; } + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index 22217e18c54..854906dc111 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:9:13 + --> $DIR/ptr_as_ptr.rs:10:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` @@ -7,28 +7,40 @@ LL | let _ = ptr as *const i32; = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:10:13 + --> $DIR/ptr_as_ptr.rs:11:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:15:17 + --> $DIR/ptr_as_ptr.rs:16:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:28:25 + --> $DIR/ptr_as_ptr.rs:29:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:29:23 + --> $DIR/ptr_as_ptr.rs:30:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` -error: aborting due to 5 previous errors +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:48:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:49:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 61f3d9d46b5cbfdb56069a95e5839abe8fda9acf Mon Sep 17 00:00:00 2001 From: Javier Alvarez Date: Wed, 23 Dec 2020 12:37:37 +0100 Subject: Add case_sensitive_file_extensions lint Closes #6425 Looks for ends_with methods calls with case sensitive extensions. --- CHANGELOG.md | 1 + clippy_lints/Cargo.toml | 2 + .../case_sensitive_file_extension_comparisons.rs | 88 ++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + .../case_sensitive_file_extension_comparisons.rs | 44 +++++++++++ ...ase_sensitive_file_extension_comparisons.stderr | 43 +++++++++++ 6 files changed, 182 insertions(+) create mode 100644 clippy_lints/src/case_sensitive_file_extension_comparisons.rs create mode 100644 tests/ui/case_sensitive_file_extension_comparisons.rs create mode 100644 tests/ui/case_sensitive_file_extension_comparisons.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 64864c2e278..b0e9ad55b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1878,6 +1878,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index a9516560a61..38098f8a14c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -34,6 +34,8 @@ rustc-semver="1.1.0" url = { version = "2.1.0", features = ["serde"] } quote = "1" syn = { version = "1", features = ["full"] } +regex = "1.4" +lazy_static = "1.4" [features] deny-warnings = [] diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs new file mode 100644 index 00000000000..b227e9a981a --- /dev/null +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -0,0 +1,88 @@ +use crate::utils::paths::STRING; +use crate::utils::{match_def_path, span_lint_and_help}; +use if_chain::if_chain; +use lazy_static::lazy_static; +use regex::Regex; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind, PathSegment}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{source_map::Spanned, Span}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for calls to `ends_with` with possible file extensions + /// and suggests to use a case-insensitive approach instead. + /// + /// **Why is this bad?** + /// `ends_with` is case-sensitive and may not detect files with a valid extension. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn is_rust_file(filename: &str) -> bool { + /// filename.ends_with(".rs") + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn is_rust_file(filename: &str) -> bool { + /// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true) + /// } + /// ``` + pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + pedantic, + "default lint description" +} + +declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]); + +fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + lazy_static! { + static ref RE: Regex = Regex::new(r"^\.([a-z0-9]{1,5}|[A-Z0-9]{1,5})$").unwrap(); + } + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident, .. }, _, [obj, extension, ..], span) = expr.kind; + if ident.as_str() == "ends_with"; + if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; + if RE.is_match(&ext_literal.as_str()); + then { + let mut ty = ctx.typeck_results().expr_ty(obj); + ty = match ty.kind() { + ty::Ref(_, ty, ..) => ty, + _ => ty + }; + + match ty.kind() { + ty::Str => { + return Some(span); + }, + ty::Adt(&ty::AdtDef { did, .. }, _) => { + if match_def_path(ctx, did, &STRING) { + return Some(span); + } + }, + _ => { return None; } + } + } + } + None +} + +impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { + fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { + span_lint_and_help( + ctx, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + span, + "case-sensitive file extension comparison", + None, + "consider using a case-insensitive comparison instead", + ); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 37a56bc20c8..ec433acf038 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -170,6 +170,7 @@ mod blocks_in_if_conditions; mod booleans; mod bytecount; mod cargo_common_metadata; +mod case_sensitive_file_extension_comparisons; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; @@ -556,6 +557,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, &cargo_common_metadata::CARGO_COMMON_METADATA, + &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_ELSE_IF, @@ -1224,6 +1226,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); + store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1281,6 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs new file mode 100644 index 00000000000..68719c2bc6d --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -0,0 +1,44 @@ +#![warn(clippy::case_sensitive_file_extension_comparisons)] + +use std::string::String; + +struct TestStruct {} + +impl TestStruct { + fn ends_with(self, arg: &str) {} +} + +fn is_rust_file(filename: &str) -> bool { + filename.ends_with(".rs") +} + +fn main() { + // std::string::String and &str should trigger the lint failure with .ext12 + let _ = String::from("").ends_with(".ext12"); + let _ = "str".ends_with(".ext12"); + + // The test struct should not trigger the lint failure with .ext12 + TestStruct {}.ends_with(".ext12"); + + // std::string::String and &str should trigger the lint failure with .EXT12 + let _ = String::from("").ends_with(".EXT12"); + let _ = "str".ends_with(".EXT12"); + + // The test struct should not trigger the lint failure with .EXT12 + TestStruct {}.ends_with(".EXT12"); + + // Should not trigger the lint failure with .eXT12 + let _ = String::from("").ends_with(".eXT12"); + let _ = "str".ends_with(".eXT12"); + TestStruct {}.ends_with(".eXT12"); + + // Should not trigger the lint failure with .EXT123 (too long) + let _ = String::from("").ends_with(".EXT123"); + let _ = "str".ends_with(".EXT123"); + TestStruct {}.ends_with(".EXT123"); + + // Shouldn't fail if it doesn't start with a dot + let _ = String::from("").ends_with("a.ext"); + let _ = "str".ends_with("a.extA"); + TestStruct {}.ends_with("a.ext"); +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr new file mode 100644 index 00000000000..05b98169f2d --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -0,0 +1,43 @@ +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14 + | +LL | filename.ends_with(".rs") + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings` + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30 + | +LL | let _ = String::from("").ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19 + | +LL | let _ = "str".ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30 + | +LL | let _ = String::from("").ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19 + | +LL | let _ = "str".ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From e56973a8545d384d6dcb5271b54c90c584a58065 Mon Sep 17 00:00:00 2001 From: Javier Alvarez Date: Wed, 23 Dec 2020 16:59:17 +0100 Subject: Remove default lint description This was left as default and caused a CI failure for the case_sensitive_file_extension_comparison lint. --- clippy_lints/src/case_sensitive_file_extension_comparisons.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index b227e9a981a..d5347ce6ed7 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, pedantic, - "default lint description" + "Checks for calls to ends_with with case-sensitive file extensions" } declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]); -- cgit 1.4.1-3-g733a5 From ea885d90ad3d041cbd7918ab25b4fb2d5f7bc5e6 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 5 Jan 2021 16:03:39 +0100 Subject: Tiny Symbol cleanup * Renames `sym.rs` to `sym_helper.rs` so that the `sym as rustc_sym` is no longer needed. * Removes one needless `symbol` from a path --- clippy_lints/src/utils/mod.rs | 10 +++++----- clippy_lints/src/utils/sym.rs | 6 ------ clippy_lints/src/utils/sym_helper.rs | 7 +++++++ 3 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 clippy_lints/src/utils/sym.rs create mode 100644 clippy_lints/src/utils/sym_helper.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 1c68e837c4a..8f54cad7728 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ #[macro_use] -pub mod sym; +pub mod sym_helper; #[allow(clippy::module_name_repetitions)] pub mod ast_utils; @@ -56,8 +56,8 @@ use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; -use rustc_span::sym as rustc_sym; -use rustc_span::symbol::{self, kw, Symbol}; +use rustc_span::sym; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -1121,7 +1121,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived)) + attrs.iter().any(|attr| attr.has_name(sym::automatically_derived)) } /// Remove blocks around an expression. @@ -1514,7 +1514,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { krate.item.attrs.iter().any(|attr| { if let ast::AttrKind::Normal(ref attr, _) = attr.kind { - attr.path == symbol::sym::no_std + attr.path == sym::no_std } else { false } diff --git a/clippy_lints/src/utils/sym.rs b/clippy_lints/src/utils/sym.rs deleted file mode 100644 index 273288c3d52..00000000000 --- a/clippy_lints/src/utils/sym.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[macro_export] -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} diff --git a/clippy_lints/src/utils/sym_helper.rs b/clippy_lints/src/utils/sym_helper.rs new file mode 100644 index 00000000000..f47dc80ebad --- /dev/null +++ b/clippy_lints/src/utils/sym_helper.rs @@ -0,0 +1,7 @@ +#[macro_export] +/// Convenience wrapper around rustc's `Symbol::intern` +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} -- cgit 1.4.1-3-g733a5 From a8825e9af067e10406e13ff7fbe9bbd5c7699a41 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 5 Jan 2021 16:31:08 +0100 Subject: Use existing 'is_automatically_derived' helper --- clippy_lints/src/needless_borrow.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index bff53eb8cca..f1c06692e30 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -2,7 +2,7 @@ //! //! This lint is **warn** by default -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind}; @@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -116,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) { + if is_automatically_derived(item.attrs) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.hir_id); } -- cgit 1.4.1-3-g733a5 From 92f2bbbe06b92ad9fc984804307958d8566cd4ed Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 5 Jan 2021 20:11:37 +0100 Subject: Fix macro issues with field_reassign_with_default --- clippy_lints/src/default.rs | 3 +++ tests/ui/auxiliary/macro_rules.rs | 16 ++++++++++++ tests/ui/auxiliary/proc_macro_derive.rs | 18 +++++++++++++ tests/ui/field_reassign_with_default.rs | 16 ++++++++++++ tests/ui/field_reassign_with_default.stderr | 40 ++++++++++++++--------------- 5 files changed, 73 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 9fa06d7cde9..f7224811e6e 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; @@ -120,6 +121,8 @@ impl LateLintPass<'_> for Default { // only take `let ...` statements if let StmtKind::Local(local) = stmt.kind; if let Some(expr) = local.init; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if !in_external_macro(cx.tcx.sess, expr.span); // only take bindings to identifiers if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; // only when assigning `... = Default::default()` diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 18324823468..d6ecd8568ce 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -94,3 +94,19 @@ macro_rules! large_enum_variant { } }; } + +#[macro_export] +macro_rules! field_reassign_with_default { + () => { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + }; +} diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 7c4e4a14551..24891682d36 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -4,6 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] #![allow(incomplete_features)] +#![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] extern crate proc_macro; @@ -23,3 +24,20 @@ pub fn derive(_: TokenStream) -> TokenStream { }; output } + +#[proc_macro_derive(FieldReassignWithDefault)] +pub fn derive_foo(_input: TokenStream) -> TokenStream { + quote! { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + #[automatically_derived] + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + } +} diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 2990397c03e..9fc208f5332 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -1,5 +1,18 @@ +// aux-build:proc_macro_derive.rs +// aux-build:macro_rules.rs + #![warn(clippy::field_reassign_with_default)] +#[macro_use] +extern crate proc_macro_derive; +#[macro_use] +extern crate macro_rules; + +// Don't lint on derives that derive `Default` +// See https://github.com/rust-lang/rust-clippy/issues/6545 +#[derive(FieldReassignWithDefault)] +struct DerivedStruct; + #[derive(Default)] struct A { i: i32, @@ -120,6 +133,9 @@ fn main() { // don't expand macros in the suggestion (#6522) let mut a: C = C::default(); a.i = vec![1]; + + // Don't lint in external macros + field_reassign_with_default!(); } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 59d2ac8ed69..2f0f28f7bb7 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,84 +1,84 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:35:5 + --> $DIR/field_reassign_with_default.rs:48:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:34:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:47:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:75:5 + --> $DIR/field_reassign_with_default.rs:88:5 | LL | a.j = 43; | ^^^^^^^^^ | -note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:74:5 +note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:87:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:80:5 + --> $DIR/field_reassign_with_default.rs:93:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:79:5 +note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:92:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:86:5 + --> $DIR/field_reassign_with_default.rs:99:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:85:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:98:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:96:5 + --> $DIR/field_reassign_with_default.rs:109:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:95:5 +note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:108:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:100:5 + --> $DIR/field_reassign_with_default.rs:113:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:99:5 +note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:112:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:122:5 + --> $DIR/field_reassign_with_default.rs:135:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:121:5 + --> $DIR/field_reassign_with_default.rs:134:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 8a45ffa11dc528749eec50d81e14d993e59b466b Mon Sep 17 00:00:00 2001 From: jekto_vatimeliju Date: Wed, 6 Jan 2021 20:02:50 +0900 Subject: Fix typo: `which which can be` -> `which can be` --- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6a17d654ac9..c9d9e02717c 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { /// /// **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. + /// `memcpy`, which can be expensive. /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From 15d5ac6b2fb411370a4d116a2b3a11dc1b54fb42 Mon Sep 17 00:00:00 2001 From: Stanislav Tkach Date: Wed, 6 Jan 2021 13:26:35 +0200 Subject: Remove duplication in the manual_ok_or lint example --- clippy_lints/src/manual_ok_or.rs | 3 --- 1 file changed, 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index b97d97ea1a5..8c77e155b70 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -23,9 +23,6 @@ declare_clippy_lint! { /// ```rust /// let foo: Option = None; /// foo.map_or(Err("error"), |v| Ok(v)); - /// - /// let foo: Option = None; - /// foo.map_or(Err("error"), |v| Ok(v)); /// ``` /// /// Use instead: -- cgit 1.4.1-3-g733a5 From f50ded059272ed3c060ea5df353966384d7df427 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 7 Jan 2021 16:41:15 +1300 Subject: Catch `pointer::cast` too in `cast_ptr_alignment` --- clippy_lints/src/types.rs | 35 +++++++++++++++++++++++++++++------ tests/ui/cast_alignment.rs | 4 ++++ tests/ui/cast_alignment.stderr | 14 +++++++++++++- 3 files changed, 46 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b21f81bd517..2d256b38b0d 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1637,12 +1637,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } if let ExprKind::Cast(ref ex, cast_to) = expr.kind { - if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { - if let Res::Def(_, def_id) = path.res { - if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { - return; - } - } + if is_hir_ty_cfg_dependant(cx, cast_to) { + return; } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); @@ -1691,6 +1687,21 @@ impl<'tcx> LateLintPass<'tcx> for Casts { lint_numeric_casts(cx, expr, ex, cast_from, cast_to); } + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + if method_path.ident.name != sym!(cast) { + return; + } + if_chain! { + if let Some(generic_args) = method_path.args; + if let [GenericArg::Type(cast_to)] = generic_args.args; + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + if is_hir_ty_cfg_dependant(cx, cast_to); + then { + return; + } + } + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } @@ -1714,6 +1725,18 @@ fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { } } +fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; + if let Res::Def(_, def_id) = path.res; + then { + cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) + } else { + false + } + } +} + 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( diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index 4c08935639f..d011e84b115 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -12,6 +12,10 @@ fn main() { (&1u8 as *const u8) as *const u16; (&mut 1u8 as *mut u8) as *mut u16; + // cast to more-strictly-aligned type, but with the `pointer::cast` function. + (&1u8 as *const u8).cast::(); + (&mut 1u8 as *mut u8).cast::(); + /* These should be ok */ // not a pointer type diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index 79219f86155..97e31c130a9 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_alignment.stderr @@ -12,5 +12,17 @@ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:15:5 + | +LL | (&1u8 as *const u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:16:5 + | +LL | (&mut 1u8 as *mut u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 2b3c0ade6d34d26698806842eb8d4ad64b64a0ef Mon Sep 17 00:00:00 2001 From: Stanislav Tkach Date: Thu, 7 Jan 2021 13:59:55 +0200 Subject: Fix typo: `irrevelent` -> `irrelevant` --- clippy_lints/src/ref_option_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 803ebada54b..8cd6692ce03 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// **Why is this bad?** Since `&` is Copy, it's useless to have a /// reference on `Option<&T>`. /// - /// **Known problems:** It may be irrevelent to use this lint on + /// **Known problems:** It may be irrelevant to use this lint on /// public API code as it will make a breaking change to apply it. /// /// **Example:** -- cgit 1.4.1-3-g733a5 From aa9adbf244624a31d3fe7bfbd34c57377fafa682 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 8 Jan 2021 08:45:15 +0900 Subject: Small fixes of doc in `needless_question_mark` --- clippy_lints/src/needless_question_mark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 783e6b716d4..9e9b79ee1cf 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -14,7 +14,7 @@ declare_clippy_lint! { /// **What it does:** /// Suggests alternatives for useless applications of `?` in terminating expressions /// - /// **Why is this bad?** There's no reason to use ? to short-circuit when execution of the body will end there anyway. + /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway. /// /// **Known problems:** None. /// @@ -58,7 +58,7 @@ declare_clippy_lint! { /// ``` pub NEEDLESS_QUESTION_MARK, complexity, - "Suggest value.inner_option instead of Some(value.inner_option?). The same goes for Result." + "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." } const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); -- cgit 1.4.1-3-g733a5 From 0e14a7550632f9bbe4e0f0b5589a8c38b55d649b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 8 Jan 2021 08:37:57 +0900 Subject: Reduce the span in `from_over_into` to impl header --- clippy_lints/src/from_over_into.rs | 2 +- tests/ui/from_over_into.stderr | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 1e7e5f53cc2..b010abda24d 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for FromOverInto { span_lint_and_help( cx, FROM_OVER_INTO, - item.span, + cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, "consider to implement `From` instead", diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 18f56f85432..b101d2704fb 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,12 +1,8 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> $DIR/from_over_into.rs:6:1 | -LL | / impl Into for String { -LL | | fn into(self) -> StringWrapper { -LL | | StringWrapper(self) -LL | | } -LL | | } - | |_^ +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` = help: consider to implement `From` instead -- cgit 1.4.1-3-g733a5 From ee9b47dae61cd34ee7cbcb013a72b5f162148e4a Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 8 Jan 2021 14:15:12 +1300 Subject: Move `is_hir_ty_cfg_dependant` to `util`, add stuff on pointer::cast` to the document for `cast_ptr_alignment` and fix line numbers in the test. --- clippy_lints/src/types.rs | 26 ++++++++------------------ clippy_lints/src/utils/mod.rs | 12 ++++++++++++ tests/ui/cast_alignment.stderr | 4 ++-- 3 files changed, 22 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 2d256b38b0d..765a0569007 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -8,7 +8,6 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -33,9 +32,9 @@ use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::sugg::Sugg; 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, meets_msrv, method_chain_args, multispan_sugg, - numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, + is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, + multispan_sugg, numeric_literal::NumericLiteral, 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, }; @@ -1282,8 +1281,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a - /// more-strictly-aligned pointer + /// **What it does:** Checks for casts, using `as` or `pointer::cast`, + /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer /// /// **Why is this bad?** Dereferencing the resulting pointer may be undefined /// behavior. @@ -1296,6 +1295,9 @@ declare_clippy_lint! { /// ```rust /// let _ = (&1u8 as *const u8) as *const u16; /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// + /// (&1u8 as *const u8).cast::(); + /// (&mut 1u8 as *mut u8).cast::(); /// ``` pub CAST_PTR_ALIGNMENT, pedantic, @@ -1725,18 +1727,6 @@ fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { } } -fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - if let Res::Def(_, def_id) = path.res; - then { - cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) - } else { - false - } - } -} - 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( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad7728..27f4cb2b254 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1686,6 +1686,18 @@ macro_rules! unwrap_cargo_metadata { }}; } +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; + if let Res::Def(_, def_id) = path.res; + then { + cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) + } else { + false + } + } +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index 97e31c130a9..7998b787b91 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_alignment.stderr @@ -13,13 +13,13 @@ LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:15:5 + --> $DIR/cast_alignment.rs:16:5 | LL | (&1u8 as *const u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:16:5 + --> $DIR/cast_alignment.rs:17:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 8490862cc37d7518c79d09d7911d86bfca2cea89 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 6 Jan 2021 15:14:01 -0600 Subject: Fix path_to_res for enum inherent items --- clippy_lints/src/utils/mod.rs | 96 +++++++++++++++----------------------- tests/ui-internal/invalid_paths.rs | 3 ++ 2 files changed, 40 insertions(+), 59 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad7728..b2c9d4f8739 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -31,7 +31,6 @@ 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}; @@ -40,7 +39,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ @@ -49,6 +48,7 @@ use rustc_hir::{ }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; @@ -309,65 +309,43 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { } /// Gets the definition associated to a path. -pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { - let crates = cx.tcx.crates(); - let krate = crates - .iter() - .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]); - if let Some(krate) = krate { - let krate = DefId { - 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(); - - loop { - let segment = match path_it.next() { - Some(segment) => segment, - 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 { - if path_it.peek().is_none() { - return Some(item.res); - } - - current_item = Some(item); - items = cx.tcx.item_children(item.res.def_id()); - break; - } - } +#[allow(clippy::shadow_unrelated)] // false positive #6563 +pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { + fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { + tcx.item_children(def_id) + .iter() + .find(|item| item.ident.name.as_str() == name) + } - // 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); - } + let (krate, first, path) = match *path { + [krate, first, ref path @ ..] => (krate, first, path), + _ => return None, + }; + let tcx = cx.tcx; + let crates = tcx.crates(); + let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?; + let first = item_child_by_name(tcx, krate.as_def_id(), first)?; + let last = path + .iter() + .copied() + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + .filter(|segment| !segment.is_empty()) + // for each segment, find the child item + .try_fold(first, |item, segment| { + let def_id = item.res.def_id(); + if let Some(item) = item_child_by_name(tcx, def_id, segment) { + Some(item) + } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { + // it is not a child item so check inherent impl items + tcx.inherent_impls(def_id) + .iter() + .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment)) + } else { + None } - } - } else { - None - } + })?; + Some(last.res) } pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index 01e28ae5e9d..a3b19c2e394 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -18,6 +18,9 @@ mod paths { // Path with bad module pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + + // Path to method on an enum inherent impl + pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; } fn main() {} -- cgit 1.4.1-3-g733a5 From 24c700b5d70f063521435c450ae1b48720b5f991 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 30 Dec 2020 15:38:21 -0600 Subject: Use DefId in interning defined symbol lint --- clippy_lints/src/utils/internal_lints.rs | 11 ++++++----- tests/ui-internal/interning_defined_symbol.fixed | 6 +++--- tests/ui-internal/interning_defined_symbol.stderr | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 9ba39f73ee8..945aaa4668c 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -10,6 +10,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; @@ -868,8 +869,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { #[derive(Default)] pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant name. - symbol_map: FxHashMap, + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, } impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); @@ -889,7 +890,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); if let Ok(value) = value.to_u32(); then { - self.symbol_map.insert(value, item.ident.to_string()); + self.symbol_map.insert(value, item_def_id); } } } @@ -903,7 +904,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); let value = Symbol::intern(&arg).as_u32(); - if let Some(symbol_const) = self.symbol_map.get(&value); + if let Some(&def_id) = self.symbol_map.get(&value); then { span_lint_and_sugg( cx, @@ -911,7 +912,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { is_expn_of(expr.span, "sym").unwrap_or(expr.span), "interning a defined symbol", "try", - format!("rustc_span::symbol::sym::{}", symbol_const), + cx.tcx.def_path_str(def_id), Applicability::MachineApplicable, ); } diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index c6b84d2ef65..2af362b8f99 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -14,13 +14,13 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Using a sym macro - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::symbol::sym::proc_dash_macro; + let _ = rustc_span::sym::proc_dash_macro; // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr index 74b906c8a57..d7e1d62d51a 100644 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -2,7 +2,7 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:17:13 | LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` | note: the lint level is defined here --> $DIR/interning_defined_symbol.rs:2:9 @@ -15,13 +15,13 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:20:13 | LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^ help: try: `rustc_span::sym::f32` error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:23:13 | LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 121c65f0cf2bf488128c60dc6c20a947bb1bb1ca Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 29 Dec 2020 15:40:55 -0600 Subject: Add keywords to interning defined symbol lint --- clippy_lints/src/utils/internal_lints.rs | 22 ++++++++++++---------- clippy_lints/src/utils/paths.rs | 2 ++ tests/ui-internal/interning_defined_symbol.fixed | 3 +++ tests/ui-internal/interning_defined_symbol.rs | 3 +++ tests/ui-internal/interning_defined_symbol.stderr | 8 +++++++- 5 files changed, 27 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 945aaa4668c..c0b6688fa15 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -881,16 +881,18 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { return; } - if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { - for item in cx.tcx.item_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } } } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2080a49a11c..3179be6af2a 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -65,6 +65,8 @@ pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; #[cfg(feature = "internal-lints")] +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; #[cfg(feature = "internal-lints")] diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 2af362b8f99..9ab845a573a 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -22,6 +22,9 @@ fn main() { // Correct suggestion when symbol isn't stringified constant name let _ = rustc_span::sym::proc_dash_macro; + // interning a keyword + let _ = rustc_span::symbol::kw::SelfLower; + // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); let _ = sym!(xyz123); diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 9ec82d4ad0b..a58e182971d 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -22,6 +22,9 @@ fn main() { // Correct suggestion when symbol isn't stringified constant name let _ = Symbol::intern("proc-macro"); + // interning a keyword + let _ = Symbol::intern("self"); + // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); let _ = sym!(xyz123); diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr index d7e1d62d51a..50c1c268eb1 100644 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -23,5 +23,11 @@ error: interning a defined symbol LL | let _ = Symbol::intern("proc-macro"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` -error: aborting due to 3 previous errors +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:26:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 76ccfb4ae24e1b6f3398e5cca350e41e76723fb1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 29 Dec 2020 15:43:18 -0600 Subject: Fix unnecessary keyword intern dogfood --- clippy_lints/src/write.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 337f7a229b9..af324f831df 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -10,7 +10,8 @@ use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Span, Symbol}; +use rustc_span::symbol::kw; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -301,7 +302,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 fmt_str.symbol == Symbol::intern("") { + if fmt_str.symbol == kw::Empty { let mut applicability = Applicability::MachineApplicable; // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed #[allow(clippy::option_if_let_else)] @@ -484,7 +485,7 @@ impl Write { fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == Symbol::intern("") { + if fmt_str.symbol == kw::Empty { let name = mac.path.segments[0].ident.name; span_lint_and_sugg( cx, -- cgit 1.4.1-3-g733a5 From cc26919b4dcfbb31a0eb902e8cf3e009a93e5ac8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 30 Dec 2020 15:52:15 -0600 Subject: Add unnecessary symbol string lint --- clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 153 +++++++++++++++++++++++- clippy_lints/src/utils/paths.rs | 8 ++ tests/ui-internal/unnecessary_symbol_str.fixed | 16 +++ tests/ui-internal/unnecessary_symbol_str.rs | 16 +++ tests/ui-internal/unnecessary_symbol_str.stderr | 39 ++++++ 6 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 tests/ui-internal/unnecessary_symbol_str.fixed create mode 100644 tests/ui-internal/unnecessary_symbol_str.rs create mode 100644 tests/ui-internal/unnecessary_symbol_str.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 37a56bc20c8..f12994c7a60 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -526,6 +526,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal-lints")] &utils::internal_lints::PRODUCE_ICE, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::UNNECESSARY_SYMBOL_STR, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -1372,6 +1374,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), + LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index c0b6688fa15..59a1852aba9 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -13,7 +13,9 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{ + BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, +}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; @@ -273,6 +275,28 @@ declare_clippy_lint! { "interning a symbol that is pre-interned and defined as a constant" } +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary conversion from Symbol to a string. + /// + /// **Why is this bad?** It's faster use symbols directly intead of strings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -873,7 +897,7 @@ pub struct InterningDefinedSymbol { symbol_map: FxHashMap, } -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { @@ -919,5 +943,130 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { ); } } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + // SymbolStr might be de-referenced: `&*symbol.as_str()` + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::UnDeref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, _, [item], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 3179be6af2a..c0b203b5388 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -54,6 +54,10 @@ 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"]; +#[cfg(feature = "internal-lints")] +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; +#[cfg(feature = "internal-lints")] +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; @@ -150,8 +154,12 @@ pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_wit #[cfg(feature = "internal-lints")] pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; +#[cfg(feature = "internal-lints")] pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; +#[cfg(feature = "internal-lints")] pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; #[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed new file mode 100644 index 00000000000..2ec0efe4c10 --- /dev/null +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +fn main() { + Symbol::intern("foo") == rustc_span::sym::clippy; + Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower; + Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper; + Ident::invalid().name == rustc_span::sym::clippy; + rustc_span::sym::clippy == Ident::invalid().name; +} diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs new file mode 100644 index 00000000000..87e1b3a2ee7 --- /dev/null +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +fn main() { + Symbol::intern("foo").as_str() == "clippy"; + Symbol::intern("foo").to_string() == "self"; + Symbol::intern("foo").to_ident_string() != "Self"; + &*Ident::invalid().as_str() == "clippy"; + "clippy" == Ident::invalid().to_string(); +} diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr new file mode 100644 index 00000000000..b1284b7c8ff --- /dev/null +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -0,0 +1,39 @@ +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:11:5 + | +LL | Symbol::intern("foo").as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` + | +note: the lint level is defined here + --> $DIR/unnecessary_symbol_str.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:12:5 + | +LL | Symbol::intern("foo").to_string() == "self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:13:5 + | +LL | Symbol::intern("foo").to_ident_string() != "Self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:14:5 + | +LL | &*Ident::invalid().as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::invalid().name == rustc_span::sym::clippy` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:15:5 + | +LL | "clippy" == Ident::invalid().to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::invalid().name` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 7871ebaab9af1fffc3e68bae9f95c9db1e3863e5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 29 Dec 2020 16:04:31 -0600 Subject: Fix symbol string comparison dogfood --- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/if_let_mutex.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 4 ++-- clippy_lints/src/map_clone.rs | 2 +- clippy_lints/src/map_identity.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/minmax.rs | 4 ++-- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 3 ++- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/swap.rs | 2 +- clippy_lints/src/unnecessary_sort_by.rs | 2 +- clippy_lints/src/useless_conversion.rs | 4 ++-- clippy_lints/src/utils/attrs.rs | 9 +++++---- clippy_lints/src/utils/hir_utils.rs | 13 ++++++------- clippy_lints/src/vec_init_then_push.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 7 ++++--- 18 files changed, 34 insertions(+), 32 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3edbe723922..9a00fc535fc 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -399,7 +399,7 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { 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"; + if tool_name.name == sym::clippy; let lint_name = meta_item.path.segments.last().unwrap().ident.name; then { return Some(lint_name.as_str()); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 2e55094d90c..58511c6d57c 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -145,7 +145,7 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn is_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.to_string() == "lock"; + if path.ident.as_str() == "lock"; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym!(mutex_type)); then { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 29439e52c48..89f5b2ff311 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -9,7 +9,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** It checks for manual implementations of `async` functions. @@ -137,7 +137,7 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if let Some(args) = segment.args; if args.bindings.len() == 1; let binding = &args.bindings[0]; - if binding.ident.as_str() == "Output"; + if binding.ident.name == sym::Output; if let TypeBindingKind::Equality{ty: output} = binding.kind; then { return Some(output) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 220240acb7a..1818836d5d5 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if_chain! { if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind; if args.len() == 2; - if method.ident.as_str() == "map"; + if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 6b782385a38..9f9c108a85a 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity { 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"; + if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if match_trait_method(cx, expr, &paths::ITERATOR) || is_type_diagnostic_item(cx, caller_ty, sym::result_type) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e99fe1b97ff..f13f2491d6e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3095,7 +3095,7 @@ fn lint_flat_map_identity<'tcx>( if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; if path.segments.len() == 1; - if path.segments[0].ident.as_str() == binding_ident.as_str(); + if path.segments[0].ident.name == binding_ident.name; then { apply_lint("called `flat_map(|x| x)` on an `Iterator`"); diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 004dd50a31b..8d0c3b8e0fe 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -89,9 +89,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons 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() { + if path.ident.name == sym!(max) { fetch_const(cx, args, MinMax::Max) - } else if path.ident.as_str() == sym!(min).as_str() { + } else if path.ident.name == sym!(min) { fetch_const(cx, args, MinMax::Min) } else { None diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 27f1074a0dd..0e49eaab436 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -63,7 +63,7 @@ impl MissingDoc { if let Some(meta) = list.get(0); if let Some(name) = meta.ident(); then { - name.as_str() == "include" + name.name == sym::include } else { false } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 5043b7b1fc3..1984942a914 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -13,6 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; @@ -153,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Ignore `self`s. if idx == 0 { if let PatKind::Binding(.., ident, _) = arg.pat.kind { - if ident.as_str() == "self" { + if ident.name == kw::SelfLower { continue; } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 681dbce9769..58c00541af7 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -66,7 +66,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 { - path.ident.name.to_ident_string() == "ok" + path.ident.name.as_str() == "ok" && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) } else { false diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index f8396592678..4ae22468f78 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -389,5 +389,5 @@ fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> 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() + !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 386987eb181..699fd51ccc1 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -91,7 +91,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; - if ident.as_str() == rhs2.segments[0].ident.as_str(); + if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); then { diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 0bccfc15678..9b45d38afd4 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -183,7 +183,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } ] = &closure_body.params; 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 method_path.ident.name == sym::cmp; then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( &cx, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index efa9c3fab4a..c5334853986 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -80,10 +80,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::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" { + if parent_name.ident.name != sym::into_iter { return; } } diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 24052a243af..8d28421d70d 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -1,6 +1,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_session::Session; +use rustc_span::sym; use std::str::FromStr; /// Deprecation status of attributes known by Clippy. @@ -64,11 +65,11 @@ pub fn get_attr<'a>( return false; }; let attr_segments = &attr.path.segments; - if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { + if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy { BUILTIN_ATTRIBUTES .iter() - .find_map(|(builtin_name, deprecation_status)| { - if *builtin_name == attr_segments[1].ident.to_string() { + .find_map(|&(builtin_name, ref deprecation_status)| { + if attr_segments[1].ident.name.as_str() == builtin_name { Some(deprecation_status) } else { None @@ -99,7 +100,7 @@ pub fn get_attr<'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].ident.to_string() == name + attr_segments[1].ident.name.as_str() == name }, } }, diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index a8fbb2ffaf0..8d8ad497be6 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { - both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) @@ -102,7 +102,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { - both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) && both(le, re, |l, r| self.eq_expr(l, r)) }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), @@ -121,7 +121,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => { - lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str()) + lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => { ls == rs @@ -188,7 +188,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); - li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp) + li.name == ri.name && self.eq_pat(lp, rp) } /// Checks whether two patterns are the same. @@ -202,7 +202,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { - lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r)) + lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r)) }, (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), @@ -263,8 +263,7 @@ 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 - left.ident.as_str() == right.ident.as_str() - && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) + left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 6249d7e867b..e632a7e57ee 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -158,7 +158,7 @@ fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Op ExprKind::Path(QPath::TypeRelative(ty, name)) if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) => { - if name.ident.name.as_str() == "new" { + if name.ident.name == sym::new { return Some(VecInitKind::New); } else if name.ident.name.as_str() == "with_capacity" { return args.get(0).and_then(|arg| { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 5683a71efea..10005a7fc81 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -7,7 +7,8 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::BytePos; +use rustc_span::symbol::kw; +use rustc_span::{sym, BytePos}; declare_clippy_lint! { /// **What it does:** Checks for `use Enum::*`. @@ -198,12 +199,12 @@ 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().any(|ps| ps.ident.as_str() == "prelude") + segments.iter().any(|ps| ps.ident.name == sym::prelude) } // Allow "super::*" imports in tests. fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { - segments.len() == 1 && segments[0].ident.as_str() == "super" + segments.len() == 1 && segments[0].ident.name == kw::Super } fn is_test_module_or_function(item: &Item<'_>) -> bool { -- cgit 1.4.1-3-g733a5 From 8a6fea4fb85adb8446797f698ab92a0869ccc9c9 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 Jan 2021 12:26:24 +0100 Subject: Fix FP for `boxed_local` lint in default trait fn impl --- clippy_lints/src/escape.rs | 33 +++++++++++++++++++++++++++++---- tests/ui/escape_analysis.rs | 20 ++++++++++++++++++++ tests/ui/escape_analysis.stderr | 14 +++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index d2dcb3e5c46..9fcd17a756a 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,15 +1,16 @@ use rustc_hir::intravisit; -use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node}; +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, ItemKind, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::symbol::kw; 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; +use crate::utils::{contains_ty, span_lint}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -51,6 +52,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool { struct EscapeDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, set: HirIdSet, + trait_self_ty: Option>, too_large_for_stack: u64, } @@ -72,19 +74,34 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } } - // 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); + let mut trait_self_ty = None; if let Some(Node::Item(item)) = parent_node { + // If the method is an impl for a trait, don't warn. if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { return; } + + // find `self` ty for this trait if relevant + if let ItemKind::Trait(_, _, _, _, items) = item.kind { + for trait_item in items { + if trait_item.id.hir_id == hir_id { + // be sure we have `self` parameter in this function + if let AssocItemKind::Fn { has_self: true } = trait_item.kind { + trait_self_ty = + Some(TraitRef::identity(cx.tcx, trait_item.id.hir_id.owner.to_def_id()).self_ty()); + } + } + } + } } let mut v = EscapeDelegate { cx, set: HirIdSet::default(), + trait_self_ty, too_large_for_stack: self.too_large_for_stack, }; @@ -153,6 +170,14 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { return; } + // skip if there is a `self` parameter binding to a type + // that contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = self.trait_self_ty { + if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) { + return; + } + } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { self.set.insert(cmt.hir_id); } diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 07004489610..d26f48fc68f 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -182,3 +182,23 @@ 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) -> () {} + +// Issue #4804 - default implementation in trait +mod issue4804 { + trait DefaultTraitImplTest { + // don't warn on `self` + fn default_impl(self: Box) -> u32 { + 5 + } + + // warn on `x: Box` + fn default_impl_x(self: Box, x: Box) -> u32 { + 4 + } + } + + trait WarnTrait { + // warn on `x: Box` + fn foo(x: Box) {} + } +} diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/escape_analysis.stderr index c86a769a3da..4a82b4419f9 100644 --- a/tests/ui/escape_analysis.stderr +++ b/tests/ui/escape_analysis.stderr @@ -12,5 +12,17 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:195:44 + | +LL | fn default_impl_x(self: Box, x: Box) -> u32 { + | ^ + +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:202:16 + | +LL | fn foo(x: Box) {} + | ^ + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 8d7417d8079b7f942e3a116ede6d36dc7a219e71 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 10 Jan 2021 23:32:23 -0500 Subject: Add: single_match will suggest using if .. == .. instead of if let when applicable --- clippy_lints/src/matches.rs | 79 ++++++++++++++++++++++++++++++++++++-------- tests/ui/single_match.rs | 43 ++++++++++++++++++++++++ tests/ui/single_match.stderr | 38 ++++++++++++++++++++- 3 files changed, 146 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 04b35835c6b..2239b519632 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,10 +2,10 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, - snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, - span_lint_and_sugg, span_lint_and_then, + expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, + is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, + remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, + span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -717,6 +717,28 @@ fn check_single_match_single_pattern( } } +fn peel_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { + fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { + if let PatKind::Ref(pat, _) = pat.kind { + peel(pat, count + 1) + } else { + (pat, count) + } + } + peel(pat, 0) +} + +fn peel_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { + if let ty::Ref(_, ty, _) = ty.kind() { + peel(ty, count + 1) + } else { + (ty, count) + } + } + peel(ty, 0) +} + fn report_single_match_single_pattern( cx: &LateContext<'_>, ex: &Expr<'_>, @@ -728,20 +750,51 @@ fn report_single_match_single_pattern( let els_str = els.map_or(String::new(), |els| { format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) }); + + let (msg, sugg) = if_chain! { + let (pat, pat_ref_count) = peel_pat_refs(arms[0].pat); + if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; + let (ty, ty_ref_count) = peel_ty_refs(cx.typeck_results().expr_ty(ex)); + if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); + if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); + then { + // scrutinee derives PartialEq and the pattern is a constant. + let pat_ref_count = match pat.kind { + // string literals are already a reference. + PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1, + _ => pat_ref_count, + }; + let msg = "you seem to be trying to use match for an equality check. Consider using `if`"; + let sugg = format!( + "if {} == {}{} {}{}", + snippet(cx, ex.span, ".."), + // PartialEq for different reference counts may not exist. + "&".repeat(ty_ref_count - pat_ref_count), + snippet(cx, arms[0].pat.span, ".."), + expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + els_str, + ); + (msg, sugg) + } else { + let msg = "you seem to be trying to use match for destructuring a single pattern. Consider using `if let`"; + let sugg = format!( + "if let {} = {} {}{}", + snippet(cx, arms[0].pat.span, ".."), + snippet(cx, ex.span, ".."), + expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + els_str, + ); + (msg, sugg) + } + }; + span_lint_and_sugg( cx, lint, expr.span, - "you seem to be trying to use match for destructuring a single pattern. Consider using `if \ - let`", + msg, "try this", - format!( - "if let {} = {} {}{}", - snippet(cx, arms[0].pat.span, ".."), - snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), - els_str, - ), + sugg, Applicability::HasPlaceholders, ); } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 1c55af5dfb6..02266308fba 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -81,6 +81,49 @@ fn single_match_know_enum() { } } +fn issue_173() { + let x = "test"; + match x { + "test" => println!(), + _ => (), + } + + #[derive(PartialEq, Eq)] + enum Foo { + A, + B, + C(u32), + } + + let x = Foo::A; + match x { + Foo::A => println!(), + _ => (), + } + + const FOO_C: Foo = Foo::C(0); + match x { + FOO_C => println!(), + _ => (), + } + enum Bar { + A, + B, + } + impl PartialEq for Bar { + fn eq(&self, rhs: &Self) -> bool { + matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B)) + } + } + impl Eq for Bar {} + + let x = Bar::A; + match x { + Bar::A => println!(), + _ => (), + } +} + macro_rules! single_match { ($num:literal) => { match $num { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index f69554d75f9..5eca07ab109 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -65,5 +65,41 @@ LL | | Cow::Owned(..) => (), LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` -error: aborting due to 6 previous errors +error: you seem to be trying to use match for an equality check. Consider using `if` + --> $DIR/single_match.rs:86:5 + | +LL | / match x { +LL | | "test" => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == "test" { println!() }` + +error: you seem to be trying to use match for an equality check. Consider using `if` + --> $DIR/single_match.rs:99:5 + | +LL | / match x { +LL | | Foo::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == Foo::A { println!() }` + +error: you seem to be trying to use match for an equality check. Consider using `if` + --> $DIR/single_match.rs:105:5 + | +LL | / match x { +LL | | FOO_C => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == FOO_C { println!() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:121:5 + | +LL | / match x { +LL | | Bar::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if let Bar::A = x { println!() }` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 53f87310cd2fd965daf22f155cb4a2536ef8c8c6 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 12 Jan 2021 10:06:41 +1300 Subject: Simplify `cast_ptr_alignment` `pointer::casr` case --- clippy_lints/src/types.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 765a0569007..75042d846d8 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1691,20 +1691,18 @@ impl<'tcx> LateLintPass<'tcx> for Casts { lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { - if method_path.ident.name != sym!(cast) { - return; - } if_chain! { - if let Some(generic_args) = method_path.args; - if let [GenericArg::Type(cast_to)] = generic_args.args; - // There probably is no obvious reason to do this, just to be consistent with `as` cases. - if is_hir_ty_cfg_dependant(cx, cast_to); - then { - return; - } + if method_path.ident.name == sym!(cast); + if let Some(generic_args) = method_path.args; + if let [GenericArg::Type(cast_to)] = generic_args.args; + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + if !is_hir_ty_cfg_dependant(cx, cast_to); + then { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } } - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } } -- cgit 1.4.1-3-g733a5 From f2d493504cb863a30f45e8a0d5717285823f931e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 28 Nov 2020 16:56:59 -0600 Subject: Similar names ignore underscore prefixed names --- clippy_lints/src/non_expressive_names.rs | 4 ++++ tests/ui/similar_names.rs | 5 +++++ 2 files changed, 9 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 446426b3e61..855529378e6 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -199,6 +199,10 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { ); return; } + if interned_name.starts_with('_') { + // these bindings are typically unused or represent an ignored portion of a destructuring pattern + return; + } let count = interned_name.chars().count(); if count < 3 { if count == 1 { diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 6796b15289e..5981980988b 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -101,3 +101,8 @@ pub(crate) struct DirSizes { pub(crate) numb_reg_cache_entries: u64, pub(crate) numb_reg_src_checkouts: u64, } + +fn ignore_underscore_prefix() { + let hello: (); + let _hello: (); +} -- cgit 1.4.1-3-g733a5 From feee45c87270a4e6cf1959315e2c4528e7da4ec7 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 13 Jan 2021 11:08:12 +1300 Subject: Fix the ICE 6539 It happened because `zero_sized_map_values` used `layout_of` with types from type aliases, which is essentially the same as the ICE 4968. --- clippy_lints/src/zero_sized_map_values.rs | 4 +++- tests/ui/crashes/ice-6539.rs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-6539.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 1d5fa8d06a9..5e9ffab7dfb 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -6,7 +6,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; +use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. @@ -50,6 +50,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues { if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); if let Adt(_, ref substs) = ty.kind(); let ty = substs.type_at(1); + // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. + if is_normalizable(cx, cx.param_env, ty); if let Ok(layout) = cx.layout_of(ty); if layout.is_zst(); then { diff --git a/tests/ui/crashes/ice-6539.rs b/tests/ui/crashes/ice-6539.rs new file mode 100644 index 00000000000..ac6c3e4aba0 --- /dev/null +++ b/tests/ui/crashes/ice-6539.rs @@ -0,0 +1,16 @@ +// The test for the ICE 6539: https://github.com/rust-lang/rust-clippy/issues/6539. +// The cause is that `zero_sized_map_values` used `layout_of` with types from type aliases, +// which is essentially the same as the ICE 4968. +// Note that only type aliases with associated types caused the crash this time, +// not others such as trait impls. + +use std::collections::{BTreeMap, HashMap}; + +pub trait Trait { + type Assoc; +} + +type TypeAlias = HashMap<(), ::Assoc>; +type TypeAlias2 = BTreeMap<(), ::Assoc>; + +fn main() {} -- cgit 1.4.1-3-g733a5 From ea028497eddf6ad3cc3ae748ccf3f2c211c3f929 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 13 Jan 2021 09:21:26 +0900 Subject: Make a reference a link in doc --- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/eta_reduction.rs | 7 ++++--- clippy_lints/src/loops.rs | 2 +- clippy_lints/src/types.rs | 2 +- clippy_lints/src/use_self.rs | 4 ++-- 5 files changed, 9 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index ae1143b2c50..90d31dece13 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// repetitive /// /// **Known problems:** The match statement may be slower due to the compiler - /// not inlining the call to cmp. See issue #5354 + /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354) /// /// **Example:** /// ```rust,ignore diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 53df3abbf54..1a722d39f73 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// **Known problems:** If creating the closure inside the closure has a side- /// effect then moving the closure creation out will change when that side- /// effect runs. - /// See rust-lang/rust-clippy#1439 for more details. + /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details. /// /// **Example:** /// ```rust,ignore @@ -45,8 +45,9 @@ declare_clippy_lint! { /// /// **Why is this bad?** It's unnecessary to create the closure. /// - /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002, - /// rust-lang/rust-clippy#3942 + /// **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071), + /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942), + /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002) /// /// /// **Example:** diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1bd96b2b4c8..a6413dc2662 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -218,7 +218,7 @@ declare_clippy_lint! { /// **Why is this bad?** The `while let` loop is usually shorter and more /// readable. /// - /// **Known problems:** Sometimes the wrong binding is displayed (#383). + /// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). /// /// **Example:** /// ```rust,no_run diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 75042d846d8..ef199c11e27 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -75,7 +75,7 @@ declare_clippy_lint! { /// **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. /// - /// **Known problems:** Vec> makes sense if T is a large type (see #3530, + /// **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), /// 1st comment). /// /// **Example:** diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 3b23f885e08..bb73d66daba 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -28,8 +28,8 @@ declare_clippy_lint! { /// feels inconsistent. /// /// **Known problems:** - /// - False positive when using associated types (#2843) - /// - False positives in some situations when using generics (#3410) + /// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843)) + /// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410)) /// /// **Example:** /// ```rust -- cgit 1.4.1-3-g733a5 From 02f99bea87d0a3fe665aa825bccdcce7a8d190e9 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Wed, 13 Jan 2021 16:08:15 -0500 Subject: Explicitly document false positives --- clippy_lints/src/await_holding_invalid.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index f136aa4572a..ae64c688744 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// other solution is to ensure the mutex is unlocked before calling await, /// either by introducing a scope or an explicit call to Drop::drop. /// - /// **Known problems:** None. + /// **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). /// /// **Example:** /// @@ -57,7 +57,7 @@ declare_clippy_lint! { /// 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. + /// **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From 85edd65bf69266dd7cec2ca6d7bb6941b6f85444 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 14 Jan 2021 14:26:26 -0500 Subject: Address review comments Add: attempt to remove address of expressions from the scrutinee expression before adding references to the pattern --- clippy_lints/src/matches.rs | 45 +++++++++++++----------------------- clippy_lints/src/utils/mod.rs | 38 +++++++++++++++++++++++++++++++ tests/ui/single_match.rs | 15 +++++++++++- tests/ui/single_match.stderr | 48 +++++++++++++++++++++++++++------------ tests/ui/single_match_else.stderr | 6 ++--- 5 files changed, 104 insertions(+), 48 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2239b519632..6ecd738d2f0 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -4,8 +4,8 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, - remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, - span_lint_and_note, span_lint_and_sugg, span_lint_and_then, + peel_hir_pat_refs, peel_mid_ty_refs, peeln_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt, + snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -717,28 +717,6 @@ fn check_single_match_single_pattern( } } -fn peel_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { - fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { - if let PatKind::Ref(pat, _) = pat.kind { - peel(pat, count + 1) - } else { - (pat, count) - } - } - peel(pat, 0) -} - -fn peel_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(ty, count + 1) - } else { - (ty, count) - } - } - peel(ty, 0) -} - fn report_single_match_single_pattern( cx: &LateContext<'_>, ex: &Expr<'_>, @@ -752,9 +730,9 @@ fn report_single_match_single_pattern( }); let (msg, sugg) = if_chain! { - let (pat, pat_ref_count) = peel_pat_refs(arms[0].pat); + let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; - let (ty, ty_ref_count) = peel_ty_refs(cx.typeck_results().expr_ty(ex)); + let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); then { @@ -764,19 +742,28 @@ fn report_single_match_single_pattern( PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1, _ => pat_ref_count, }; - let msg = "you seem to be trying to use match for an equality check. Consider using `if`"; + // References are only implicitly added to the pattern, so no overflow here. + // e.g. will work: match &Some(_) { Some(_) => () } + // will not: match Some(_) { &Some(_) => () } + let ref_count_diff = ty_ref_count - pat_ref_count; + + // Try to remove address of expressions first. + let (ex, removed) = peeln_hir_expr_refs(ex, ref_count_diff); + let ref_count_diff = ref_count_diff - removed; + + let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( "if {} == {}{} {}{}", snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. - "&".repeat(ty_ref_count - pat_ref_count), + "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) } else { - let msg = "you seem to be trying to use match for destructuring a single pattern. Consider using `if let`"; + let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( "if let {} = {} {}{}", snippet(cx, arms[0].pat.span, ".."), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad7728..8f8c681ecb7 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1668,6 +1668,44 @@ where match_expr_list } +/// Peels off all references on the pattern. Returns the underlying pattern and the number of +/// references removed. +pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { + fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { + if let PatKind::Ref(pat, _) = pat.kind { + peel(pat, count + 1) + } else { + (pat, count) + } + } + peel(pat, 0) +} + +/// Peels off up to the given number of references on the expression. Returns the underlying +/// expression and the number of references removed. +pub fn peeln_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { + fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) { + match expr.kind { + ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target), + _ => (expr, count), + } + } + f(expr, 0, count) +} + +/// Peels off all references on the type. Returns the underlying type and the number of references +/// removed. +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { + if let ty::Ref(_, ty, _) = ty.kind() { + peel(ty, count + 1) + } else { + (ty, count) + } + } + peel(ty, 0) +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 02266308fba..ca884b41c45 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -81,7 +81,8 @@ fn single_match_know_enum() { } } -fn issue_173() { +// issue #173 +fn if_suggestion() { let x = "test"; match x { "test" => println!(), @@ -106,6 +107,18 @@ fn issue_173() { FOO_C => println!(), _ => (), } + + match &&x { + Foo::A => println!(), + _ => (), + } + + let x = &x; + match &x { + Foo::A => println!(), + _ => (), + } + enum Bar { A, B, diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 5eca07ab109..7ea6955b740 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,4 +1,4 @@ -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:8:5 | LL | / match x { @@ -17,7 +17,7 @@ LL | println!("{:?}", y); LL | }; | -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:16:5 | LL | / match x { @@ -29,7 +29,7 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:25:5 | LL | / match z { @@ -38,7 +38,7 @@ LL | | _ => {}, LL | | }; | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:54:5 | LL | / match x { @@ -47,7 +47,7 @@ LL | | None => (), LL | | }; | |_____^ help: try this: `if let Some(y) = x { dummy() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:59:5 | LL | / match y { @@ -56,7 +56,7 @@ LL | | Err(..) => (), LL | | }; | |_____^ help: try this: `if let Ok(y) = y { dummy() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:66:5 | LL | / match c { @@ -65,8 +65,8 @@ LL | | Cow::Owned(..) => (), LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` -error: you seem to be trying to use match for an equality check. Consider using `if` - --> $DIR/single_match.rs:86:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:87:5 | LL | / match x { LL | | "test" => println!(), @@ -74,8 +74,8 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if x == "test" { println!() }` -error: you seem to be trying to use match for an equality check. Consider using `if` - --> $DIR/single_match.rs:99:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:100:5 | LL | / match x { LL | | Foo::A => println!(), @@ -83,8 +83,8 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` -error: you seem to be trying to use match for an equality check. Consider using `if` - --> $DIR/single_match.rs:105:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:106:5 | LL | / match x { LL | | FOO_C => println!(), @@ -92,8 +92,26 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if x == FOO_C { println!() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:121:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:111:5 + | +LL | / match &&x { +LL | | Foo::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == Foo::A { println!() }` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:117:5 + | +LL | / match &x { +LL | | Foo::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == &Foo::A { println!() }` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:134:5 | LL | / match x { LL | | Bar::A => println!(), @@ -101,5 +119,5 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 3a07c2ec542..20be4fa226c 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,4 +1,4 @@ -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match_else.rs:14:5 | LL | / match ExprNode::Butterflies { @@ -19,7 +19,7 @@ LL | None LL | } | -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +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) { @@ -39,7 +39,7 @@ LL | return LL | } | -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +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) { -- cgit 1.4.1-3-g733a5 From 36ff2f739c62f81d5ecc1850d9f3354d15de928d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 14 Jan 2021 22:02:04 -0500 Subject: Rename function --- clippy_lints/src/matches.rs | 4 ++-- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 6ecd738d2f0..02021b87369 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -4,7 +4,7 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, - peel_hir_pat_refs, peel_mid_ty_refs, peeln_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt, + peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; @@ -748,7 +748,7 @@ fn report_single_match_single_pattern( let ref_count_diff = ty_ref_count - pat_ref_count; // Try to remove address of expressions first. - let (ex, removed) = peeln_hir_expr_refs(ex, ref_count_diff); + let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); let ref_count_diff = ref_count_diff - removed; let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f8c681ecb7..f81bf088ec4 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1683,7 +1683,7 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { /// Peels off up to the given number of references on the expression. Returns the underlying /// expression and the number of references removed. -pub fn peeln_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { +pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) { match expr.kind { ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target), -- cgit 1.4.1-3-g733a5 From f18cf82ca8764d6b0b07549cdba25b91bd0243fa Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 15 Jan 2021 10:41:29 +0100 Subject: Don't trigger needless_return lint in macros --- clippy_lints/src/returns.rs | 3 +++ tests/ui/needless_return.fixed | 15 +++++++++++++++ tests/ui/needless_return.rs | 15 +++++++++++++++ 3 files changed, 33 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 0e031e6151b..63548d8fdb4 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -217,6 +217,9 @@ fn check_final_expr<'tcx>( } fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + if ret_span.from_expansion() { + return; + } match inner_span { Some(inner_span) => { if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d849e093da7..86bfc5b4bb2 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + 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 29f2bd1852a..51061370dfe 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); -- cgit 1.4.1-3-g733a5 From 83f1abff4874a66124a6aaefec248ca4051c0d23 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 4 Jan 2021 21:20:44 +0100 Subject: Fix FP with empty return for `needless_return` lint --- clippy_lints/src/returns.rs | 11 ++++++++++- tests/ui/needless_return.fixed | 13 +++++++++++++ tests/ui/needless_return.rs | 13 +++++++++++++ tests/ui/needless_return.stderr | 20 +++++++++++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 63548d8fdb4..e438f92b136 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -131,7 +131,16 @@ impl<'tcx> LateLintPass<'tcx> for Return { _: HirId, ) { match kind { - FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), + FnKind::Closure(_) => { + // when returning without value in closure, replace this `return` + // with an empty block to prevent invalid suggestion (see #6501) + let replacement = if let ExprKind::Ret(None) = &body.value.kind { + RetReplacement::Block + } else { + RetReplacement::Empty + }; + check_final_expr(cx, &body.value, Some(body.value.span), replacement) + }, FnKind::ItemFn(..) | FnKind::Method(..) => { if let ExprKind::Block(ref block, _) = body.value.kind { check_block_return(cx, block); diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 86bfc5b4bb2..f137e8ecae9 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -101,6 +101,19 @@ fn test_return_in_macro() { needed_return!(0); } +mod issue6501 { + fn foo(bar: Result<(), ()>) { + bar.unwrap_or_else(|_| {}) + } + + fn test_closure() { + let _ = || { + + }; + let _ = || {}; + } +} + 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 51061370dfe..d754e4d37a8 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -101,6 +101,19 @@ fn test_return_in_macro() { needed_return!(0); } +mod issue6501 { + fn foo(bar: Result<(), ()>) { + bar.unwrap_or_else(|_| return) + } + + fn test_closure() { + let _ = || { + return; + }; + let _ = || return; + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index f73c833a801..12d94e892ed 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -84,5 +84,23 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: aborting due to 14 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:91:32 + | +LL | bar.unwrap_or_else(|_| return) + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:96:13 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:98:20 + | +LL | let _ = || return; + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 837bc9906552af5da429bef4b37be588b8d2f49d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 31 Dec 2020 11:10:13 -0500 Subject: Initial implementation of redundant_slicing lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++++ clippy_lints/src/redundant_slicing.rs | 56 +++++++++++++++++++++++++++++++++++ tests/ui/redundant_slicing.rs | 11 +++++++ tests/ui/redundant_slicing.stderr | 16 ++++++++++ 5 files changed, 89 insertions(+) create mode 100644 clippy_lints/src/redundant_slicing.rs create mode 100644 tests/ui/redundant_slicing.rs create mode 100644 tests/ui/redundant_slicing.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e9ad55b4f..85f6929f924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2162,6 +2162,7 @@ Released 2018-09-13 [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4f9ebb4af3d..70fdfd22caa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -301,6 +301,7 @@ mod redundant_closure_call; mod redundant_else; mod redundant_field_names; mod redundant_pub_crate; +mod redundant_slicing; mod redundant_static_lifetimes; mod ref_option_ref; mod reference; @@ -849,6 +850,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &redundant_else::REDUNDANT_ELSE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, + &redundant_slicing::REDUNDANT_SLICING, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, &ref_option_ref::REF_OPTION_REF, &reference::DEREF_ADDROF, @@ -1229,6 +1231,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); + store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1591,6 +1594,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_slicing::REDUNDANT_SLICING), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), @@ -1835,6 +1839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&redundant_slicing::REDUNDANT_SLICING), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs new file mode 100644 index 00000000000..686298b6943 --- /dev/null +++ b/clippy_lints/src/redundant_slicing.rs @@ -0,0 +1,56 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for redundant slicing expressions which use the full range, and + /// do not change the type. + /// + /// **Why is this bad?** It unnecessarily adds complexity to the expression. + /// + /// **Known problems:** If the type being sliced has an implementation of `Index` + /// that actually changes anything then it can't be removed. However, this would be surprising + /// to people reading the code and should have a note with it. + /// + /// **Example:** + /// + /// ```ignore + /// fn get_slice(x: &[u32]) -> &[u32] { + /// &x[..] + /// } + /// ``` + /// Use instead: + /// ```ignore + /// fn get_slice(x: &[u32]) -> &[u32] { + /// x + /// } + /// ``` + pub REDUNDANT_SLICING, + complexity, + "redundant slicing of the whole range of a type" +} + +declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); + +impl LateLintPass<'_> for RedundantSlicing { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::AddrOf(_, _, addressee) = expr.kind; + if let ExprKind::Index(indexed, range) = addressee.kind; + if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); + if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed)); + then { + let mut app = Applicability::MachineApplicable; + let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned(); + + span_lint_and_sugg(cx, REDUNDANT_SLICING, expr.span, "redundant slicing of the whole range", + "use the original slice instead", hint, app); + } + } + } +} diff --git a/tests/ui/redundant_slicing.rs b/tests/ui/redundant_slicing.rs new file mode 100644 index 00000000000..922b8b4ce57 --- /dev/null +++ b/tests/ui/redundant_slicing.rs @@ -0,0 +1,11 @@ +#![allow(unused)] +#![warn(clippy::redundant_slicing)] + +fn main() { + let x: &[u32] = &[0]; + let err = &x[..]; + + let v = vec![0]; + let ok = &v[..]; + let err = &(&v[..])[..]; +} diff --git a/tests/ui/redundant_slicing.stderr b/tests/ui/redundant_slicing.stderr new file mode 100644 index 00000000000..9efd6484ad0 --- /dev/null +++ b/tests/ui/redundant_slicing.stderr @@ -0,0 +1,16 @@ +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:6:15 + | +LL | let err = &x[..]; + | ^^^^^^ help: use the original slice instead: `x` + | + = note: `-D clippy::redundant-slicing` implied by `-D warnings` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:10:15 + | +LL | let err = &(&v[..])[..]; + | ^^^^^^^^^^^^^ help: use the original slice instead: `(&v[..])` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 2a41d40807ccf01268c29ba64ac9ea0efe1b26f7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 31 Dec 2020 11:42:12 -0500 Subject: fix new lint error --- clippy_lints/src/map_unit_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index e50d11a4d71..01126e86199 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -131,7 +131,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Some(expr.span) }, hir::ExprKind::Block(ref block, _) => { - match (&block.stmts[..], block.expr.as_ref()) { + match (block.stmts, block.expr.as_ref()) { (&[], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` -- cgit 1.4.1-3-g733a5 From 27c0d6c14bde219bcaf72b4bcb6426f6f8650f6e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 31 Dec 2020 12:07:24 -0500 Subject: don't lint external macro expansions --- clippy_lints/src/redundant_slicing.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 686298b6943..5140b30a473 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -2,7 +2,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyS; +use rustc_middle::{lint::in_external_macro, ty::TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg}; @@ -39,6 +39,10 @@ declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); impl LateLintPass<'_> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess, expr.span) { + return; + } + if_chain! { if let ExprKind::AddrOf(_, _, addressee) = expr.kind; if let ExprKind::Index(indexed, range) = addressee.kind; -- cgit 1.4.1-3-g733a5 From bf028b3f4a440bc8a390837da3a3f940177de1e6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 31 Dec 2020 14:40:07 -0500 Subject: fix copy-paste error --- clippy_lints/src/redundant_slicing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 5140b30a473..4478f559266 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{lint::in_external_macro, ty::TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -39,7 +39,7 @@ declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); impl LateLintPass<'_> for RedundantSlicing { 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) { return; } -- cgit 1.4.1-3-g733a5 From 9146a77032e7d8c96ccae177c60ddb04b6159329 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 14 Jan 2021 21:38:57 +0000 Subject: Update clippy_lints/src/redundant_slicing.rs Co-authored-by: Philipp Krones --- clippy_lints/src/redundant_slicing.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 4478f559266..e5ced13514f 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -52,8 +52,15 @@ impl LateLintPass<'_> for RedundantSlicing { let mut app = Applicability::MachineApplicable; let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned(); - span_lint_and_sugg(cx, REDUNDANT_SLICING, expr.span, "redundant slicing of the whole range", - "use the original slice instead", hint, app); + span_lint_and_sugg( + cx, + REDUNDANT_SLICING, + expr.span, + "redundant slicing of the whole range", + "use the original slice instead", + hint, + app, + ); } } } -- cgit 1.4.1-3-g733a5 From 0d542b7310294a32023e935916a4d8c8f47ec1a8 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Sun, 17 Jan 2021 08:48:37 +0000 Subject: Run tests/ui/update-all-references.sh and refactor match into matches! --- clippy_lints/src/write.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 78d23e1e0ef..52118a56bb6 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -470,10 +470,7 @@ impl Write { ExprKind::Assign(lhs, rhs, _) => { if_chain! { if let ExprKind::Lit(ref lit) = rhs.kind; - if match lit.kind { - LitKind::Int(_, _) | LitKind::Float(_, _) => false, - _ => true, - }; + if matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)); if let ExprKind::Path(_, p) = &lhs.kind; then { let mut all_simple = true; -- cgit 1.4.1-3-g733a5 From 0c347d3d0686d8892512dcc1e13102d15afcc6dd Mon Sep 17 00:00:00 2001 From: Marc Dominik Migge Date: Sun, 17 Jan 2021 14:42:36 +0100 Subject: Fix false positive for unit_arg lint --- clippy_lints/src/types.rs | 11 ++++++++++- tests/ui/unit_arg.rs | 20 +++++++++++++++++++- tests/ui/unit_arg.stderr | 46 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 71 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3b5a83d2a0b..7d0eea37bc0 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -955,7 +955,16 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { .iter() .filter(|arg| { if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { - !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) + match &arg.kind { + ExprKind::Block(..) + | ExprKind::Call(..) + | ExprKind::If(..) + | ExprKind::MethodCall(..) => true, + ExprKind::Match(..) => { + !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) + }, + _ => false, + } } else { false } diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index b6a7bc5a1cc..79dac925f08 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -59,7 +59,18 @@ fn bad() { 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(())) + foo(foo(())); + foo(if true { + 1; + }); + foo(match Some(1) { + Some(_) => { + 1; + }, + None => { + 0; + }, + }); } fn ok() { @@ -71,6 +82,13 @@ fn ok() { b.bar({ 1 }); b.bar(()); question_mark(); + let named_unit_arg = (); + foo(named_unit_arg); + foo(if true { 1 } else { 0 }); + foo(match Some(1) { + Some(_) => 1, + None => 0, + }); } fn question_mark() -> Result<(), ()> { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 094cff8c985..8679706f8ec 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -156,17 +156,55 @@ LL | }); error: passing a unit value to a function --> $DIR/unit_arg.rs:62:5 | -LL | foo(foo(())) +LL | foo(foo(())); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | LL | foo(()); -LL | foo(()) +LL | foo(()); + | + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:63:5 + | +LL | / foo(if true { +LL | | 1; +LL | | }); + | |______^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | if true { +LL | 1; +LL | }; +LL | foo(()); + | + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:66:5 | +LL | / foo(match Some(1) { +LL | | Some(_) => { +LL | | 1; +LL | | }, +... | +LL | | }, +LL | | }); + | |______^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | match Some(1) { +LL | Some(_) => { +LL | 1; +LL | }, +LL | None => { +LL | 0; + ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:95:5 + --> $DIR/unit_arg.rs:113:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ @@ -177,5 +215,5 @@ LL | foo(1); LL | Some(()) | -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 2af642da28109c96145c7a5613ae2ec37d359448 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Sun, 17 Jan 2021 18:01:01 +0000 Subject: Replace another instance of match with matches --- clippy_lints/src/write.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 52118a56bb6..6721639f724 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -443,12 +443,7 @@ impl Write { return (Some(fmtstr), None); }; match &token_expr.kind { - ExprKind::Lit(lit) - if match lit.kind { - LitKind::Int(_, _) | LitKind::Float(_, _) => false, - _ => true, - } => - { + ExprKind::Lit(lit) if matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => { let mut all_simple = true; let mut seen = false; for arg in &args { -- cgit 1.4.1-3-g733a5 From ab155b14a27811c29e35bea76bfbf3845bc79fdf Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Sun, 17 Jan 2021 18:21:58 +0000 Subject: Negate results of matches! --- clippy_lints/src/write.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 6721639f724..60a9fb59cd2 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -443,7 +443,7 @@ impl Write { return (Some(fmtstr), None); }; match &token_expr.kind { - ExprKind::Lit(lit) if matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => { + ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => { let mut all_simple = true; let mut seen = false; for arg in &args { @@ -465,7 +465,7 @@ impl Write { ExprKind::Assign(lhs, rhs, _) => { if_chain! { if let ExprKind::Lit(ref lit) = rhs.kind; - if matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)); + if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)); if let ExprKind::Path(_, p) = &lhs.kind; then { let mut all_simple = true; -- cgit 1.4.1-3-g733a5 From fb2a06dcce0680dfe8d2bed542be3c55eb2e18c7 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Sun, 17 Jan 2021 18:55:59 +0000 Subject: Remove numeric literals from print_literal and write_literal tests --- clippy_lints/src/write.rs | 2 +- tests/ui/crashes/ice-3891.stderr | 4 ++-- tests/ui/print_literal.rs | 3 --- tests/ui/print_literal.stderr | 46 ++++++++++++---------------------------- tests/ui/write_literal.rs | 3 --- tests/ui/write_literal.stderr | 46 ++++++++++++---------------------------- 6 files changed, 31 insertions(+), 73 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 60a9fb59cd2..503cb82c3e5 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -461,7 +461,7 @@ impl Write { span_lint(cx, lint, token_expr.span, "literal with an empty format string"); } idx += 1; - } + }, ExprKind::Assign(lhs, rhs, _) => { if_chain! { if let ExprKind::Lit(ref lit) = rhs.kind; diff --git a/tests/ui/crashes/ice-3891.stderr b/tests/ui/crashes/ice-3891.stderr index 5a285b0e714..59469ec5891 100644 --- a/tests/ui/crashes/ice-3891.stderr +++ b/tests/ui/crashes/ice-3891.stderr @@ -1,10 +1,10 @@ -error: invalid suffix `x` for integer literal +error: invalid suffix `x` for number literal --> $DIR/ice-3891.rs:2:5 | LL | 1x; | ^^ invalid suffix `x` | - = help: the suffix must be one of the integral types (`u32`, `isize`, etc) + = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) error: aborting due to previous error diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 40ed18e9302..0c8aecc2d8c 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -19,12 +19,9 @@ fn main() { println!("{number:>0width$}", number = 1, width = 6); // these should throw warnings - println!("{} of {:b} people know binary, the other half doesn't", 1, 2); print!("Hello {}", "world"); println!("Hello {} {}", world, "world"); println!("Hello {}", "world"); - println!("10 / 4 is {}", 2.5); - println!("2 + 1 = {}", 3); // positional args don't change the fact // that we're using a literal -- this should diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index fc502e9f71d..692abdb3054 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,88 +1,70 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:22:71 - | -LL | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); - | ^ - | - = note: `-D clippy::print-literal` implied by `-D warnings` - -error: literal with an empty format string - --> $DIR/print_literal.rs:23:24 + --> $DIR/print_literal.rs:22:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ + | + = note: `-D clippy::print-literal` implied by `-D warnings` error: literal with an empty format string - --> $DIR/print_literal.rs:24:36 + --> $DIR/print_literal.rs:23:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:25:26 + --> $DIR/print_literal.rs:24:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:26:30 - | -LL | println!("10 / 4 is {}", 2.5); - | ^^^ - -error: literal with an empty format string - --> $DIR/print_literal.rs:27:28 - | -LL | println!("2 + 1 = {}", 3); - | ^ - -error: literal with an empty format string - --> $DIR/print_literal.rs:32:25 + --> $DIR/print_literal.rs:29:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:32:34 + --> $DIR/print_literal.rs:29:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:33:25 + --> $DIR/print_literal.rs:30:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:33:34 + --> $DIR/print_literal.rs:30:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:36:35 + --> $DIR/print_literal.rs:33:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:36:50 + --> $DIR/print_literal.rs:33:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:37:35 + --> $DIR/print_literal.rs:34:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:37:50 + --> $DIR/print_literal.rs:34:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index d8205c5eb67..f5de7ea71d3 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -24,12 +24,9 @@ fn main() { writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); // these should throw warnings - writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); write!(&mut v, "Hello {}", "world"); writeln!(&mut v, "Hello {} {}", world, "world"); writeln!(&mut v, "Hello {}", "world"); - writeln!(&mut v, "10 / 4 is {}", 2.5); - writeln!(&mut v, "2 + 1 = {}", 3); // positional args don't change the fact // that we're using a literal -- this should diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 54a787fe555..4dcaaa474a8 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -1,88 +1,70 @@ error: literal with an empty format string - --> $DIR/write_literal.rs:27:79 - | -LL | writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); - | ^ - | - = note: `-D clippy::write-literal` implied by `-D warnings` - -error: literal with an empty format string - --> $DIR/write_literal.rs:28:32 + --> $DIR/write_literal.rs:27:32 | LL | write!(&mut v, "Hello {}", "world"); | ^^^^^^^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` error: literal with an empty format string - --> $DIR/write_literal.rs:29:44 + --> $DIR/write_literal.rs:28:44 | LL | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:30:34 + --> $DIR/write_literal.rs:29:34 | LL | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:31:38 - | -LL | writeln!(&mut v, "10 / 4 is {}", 2.5); - | ^^^ - -error: literal with an empty format string - --> $DIR/write_literal.rs:32:36 - | -LL | writeln!(&mut v, "2 + 1 = {}", 3); - | ^ - -error: literal with an empty format string - --> $DIR/write_literal.rs:37:33 + --> $DIR/write_literal.rs:34:33 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:37:42 + --> $DIR/write_literal.rs:34:42 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:38:33 + --> $DIR/write_literal.rs:35:33 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:38:42 + --> $DIR/write_literal.rs:35:42 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:41:43 + --> $DIR/write_literal.rs:38:43 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:41:58 + --> $DIR/write_literal.rs:38:58 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:42:43 + --> $DIR/write_literal.rs:39:43 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:42:58 + --> $DIR/write_literal.rs:39:58 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 70704db36fd07353de58c9330397d0137afdeebe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 15 Jan 2021 18:51:00 +0100 Subject: Do not lint when range is completely included into another one --- clippy_lints/src/matches.rs | 12 +++++++++++- tests/ui/match_overlapping_arm.rs | 12 ++++++++++++ tests/ui/match_overlapping_arm.stderr | 26 +------------------------- 3 files changed, 24 insertions(+), 26 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 04b35835c6b..90c63f32c13 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1544,7 +1544,17 @@ where } }, (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (), - _ => return Some((a.range(), b.range())), + _ => { + // skip if the range `a` is completely included into the range `b` + if let Ordering::Equal | Ordering::Less = a.cmp(&b) { + let kind_a = Kind::End(a.range().node.1, a.range()); + let kind_b = Kind::End(b.range().node.1, b.range()); + if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) { + return None; + } + } + return Some((a.range(), b.range())); + }, } } diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 97789bb766f..3e40f2187bf 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -57,6 +57,18 @@ fn overlapping() { _ => (), } + match 42 { + 5..7 => println!("5 .. 7"), + 0..10 => println!("0 .. 10"), + _ => (), + } + + match 42 { + 5..10 => println!("5 .. 10"), + 0..=10 => println!("0 ... 10"), + _ => (), + } + /* // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns match 42 { diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index eb20d5405a9..74259cd88c7 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -23,30 +23,6 @@ note: overlaps with this LL | FOO..=11 => println!("0 ... 11"), | ^^^^^^^^ -error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:26:9 - | -LL | 0..=5 => println!("0 ... 5"), - | ^^^^^ - | -note: overlaps with this - --> $DIR/match_overlapping_arm.rs:25:9 - | -LL | 2 => println!("2"), - | ^ - -error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:32:9 - | -LL | 0..=2 => println!("0 ... 2"), - | ^^^^^ - | -note: overlaps with this - --> $DIR/match_overlapping_arm.rs:31:9 - | -LL | 2 => println!("2"), - | ^ - error: some ranges overlap --> $DIR/match_overlapping_arm.rs:55:9 | @@ -59,5 +35,5 @@ note: overlaps with this LL | 0..=11 => println!("0 ... 11"), | ^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From c53192c34762f8104542168dbc7c83cdd280151c Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 19 Jan 2021 12:46:24 +1300 Subject: Add a note to `as_conversions` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … to clalify its purpose. --- clippy_lints/src/as_conversions.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 0c8efd75514..c30d65bbc57 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -8,6 +8,14 @@ use crate::utils::span_lint_and_help; declare_clippy_lint! { /// **What it does:** Checks for usage of `as` conversions. /// + /// Note that this lint is specialized in linting *every single* use of `as` + /// regardless of whether good alternatives exist or not. + /// If you want more precise lints for `as`, please consider using these separate lints: + /// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`, + /// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. + /// There is a good explanation the reason why this lint should work in this way and how it is useful + /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). + /// /// **Why is this bad?** `as` conversions will perform many kinds of /// conversions, including silently lossy conversions and dangerous coercions. /// There are cases when it makes sense to use `as`, so the lint is -- cgit 1.4.1-3-g733a5 From 3269070261e8324c2b3074cd491ddf2ac6cf19d3 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 11 Jan 2021 23:56:12 +0100 Subject: Create new lint for the usage of inspect for each. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 +++ clippy_lints/src/methods/inspect_for_each.rs | 23 +++++++++++++++++++ clippy_lints/src/methods/mod.rs | 33 ++++++++++++++++++++++++++++ tests/ui/inspect_for_each.rs | 22 +++++++++++++++++++ tests/ui/inspect_for_each.stderr | 16 ++++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 clippy_lints/src/methods/inspect_for_each.rs create mode 100644 tests/ui/inspect_for_each.rs create mode 100644 tests/ui/inspect_for_each.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 64864c2e278..a91b7c64770 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1996,6 +1996,7 @@ Released 2018-09-13 [`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 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each [`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 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f12994c7a60..17662b81205 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -733,6 +733,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::FROM_ITER_INSTEAD_OF_COLLECT, &methods::GET_UNWRAP, &methods::INEFFICIENT_TO_STRING, + &methods::INSPECT_FOR_EACH, &methods::INTO_ITER_ON_REF, &methods::ITERATOR_STEP_BY_ZERO, &methods::ITER_CLONED_COLLECT, @@ -1507,6 +1508,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::FROM_ITER_INSTEAD_OF_COLLECT), + LintId::of(&methods::INSPECT_FOR_EACH), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITER_CLONED_COLLECT), @@ -1807,6 +1809,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CLONE_ON_COPY), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), + LintId::of(&methods::INSPECT_FOR_EACH), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs new file mode 100644 index 00000000000..6d41ee38a27 --- /dev/null +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -0,0 +1,23 @@ +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +use crate::utils::{match_trait_method, paths, span_lint_and_help}; + +use super::INSPECT_FOR_EACH; + +/// lint use of `inspect().for_each()` for `Iterators` +pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; + let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; + span_lint_and_help( + cx, + INSPECT_FOR_EACH, + inspect_span.with_hi(expr.span.hi()), + msg, + None, + hint, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f13f2491d6e..018696ef0cf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,5 +1,6 @@ mod bind_instead_of_map; mod inefficient_to_string; +mod inspect_for_each; mod manual_saturating_arithmetic; mod option_map_unwrap_or; mod unnecessary_filter_map; @@ -1405,6 +1406,36 @@ declare_clippy_lint! { "use `.collect()` instead of `::from_iter()`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `inspect().for_each()`. + /// + /// **Why is this bad?** It is the same as performing the computation + /// inside `inspect` at the beginning of the closure in `for_each`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// [1,2,3,4,5].iter() + /// .inspect(|&x| println!("inspect the number: {}", x)) + /// .for_each(|&x| { + /// assert!(x >= 0); + /// }); + /// ``` + /// Can be written as + /// ```rust + /// [1,2,3,4,5].iter() + /// .for_each(|&x| { + /// println!("inspect the number: {}", x); + /// assert!(x >= 0); + /// }); + /// ``` + pub INSPECT_FOR_EACH, + complexity, + "using `.inspect().for_each()`, which can be replaced with `.for_each()`" +} + pub struct Methods { msrv: Option, } @@ -1467,6 +1498,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_LAZY_EVALUATIONS, MAP_COLLECT_RESULT_UNIT, FROM_ITER_INSTEAD_OF_COLLECT, + INSPECT_FOR_EACH, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1553,6 +1585,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["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]), + ["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]), _ => {}, } diff --git a/tests/ui/inspect_for_each.rs b/tests/ui/inspect_for_each.rs new file mode 100644 index 00000000000..7fe45c83bca --- /dev/null +++ b/tests/ui/inspect_for_each.rs @@ -0,0 +1,22 @@ +#![warn(clippy::inspect_for_each)] + +fn main() { + let a: Vec = vec![1, 2, 3, 4, 5]; + + let mut b: Vec = Vec::new(); + a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| { + let y = do_some(x); + let z = do_more(y); + b.push(z); + }); + + assert_eq!(b, vec![4, 5, 6, 7, 8]); +} + +fn do_some(a: usize) -> usize { + a + 1 +} + +fn do_more(a: usize) -> usize { + a + 2 +} diff --git a/tests/ui/inspect_for_each.stderr b/tests/ui/inspect_for_each.stderr new file mode 100644 index 00000000000..9f976bb7458 --- /dev/null +++ b/tests/ui/inspect_for_each.stderr @@ -0,0 +1,16 @@ +error: called `inspect(..).for_each(..)` on an `Iterator` + --> $DIR/inspect_for_each.rs:7:19 + | +LL | a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| { + | ___________________^ +LL | | let y = do_some(x); +LL | | let z = do_more(y); +LL | | b.push(z); +LL | | }); + | |______^ + | + = note: `-D clippy::inspect-for-each` implied by `-D warnings` + = help: move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 966320642b157c539901ed0461d6aab6cb34729d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 19 Jan 2021 23:51:10 +0900 Subject: Fix a wrong suggestion of `ref_in_deref` --- clippy_lints/src/reference.rs | 10 ++++++++-- tests/ui/unnecessary_ref.fixed | 11 ++++++++++- tests/ui/unnecessary_ref.rs | 11 ++++++++++- tests/ui/unnecessary_ref.stderr | 10 +++++++++- 4 files changed, 37 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index efe3237990d..2dfb947b5eb 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,3 +1,4 @@ +use crate::utils::sugg::Sugg; use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; @@ -124,14 +125,19 @@ impl EarlyLintPass for RefInDeref { if let ExprKind::Paren(ref parened) = object.kind; if let ExprKind::AddrOf(_, _, ref inner) = parened.kind; then { - let mut applicability = Applicability::MachineApplicable; + let applicability = if inner.span.from_expansion() { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let sugg = Sugg::ast(cx, inner, "_").maybe_par(); span_lint_and_sugg( cx, REF_IN_DEREF, object.span, "creating a reference that is immediately dereferenced", "try this", - snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), + sugg.to_string(), applicability, ); } diff --git a/tests/ui/unnecessary_ref.fixed b/tests/ui/unnecessary_ref.fixed index f7b94118d4e..d927bae976f 100644 --- a/tests/ui/unnecessary_ref.fixed +++ b/tests/ui/unnecessary_ref.fixed @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused_variables)] +#![allow(unused_variables, dead_code)] struct Outer { inner: u32, @@ -12,3 +12,12 @@ fn main() { let outer = Outer { inner: 0 }; let inner = outer.inner; } + +struct Apple; +impl Apple { + fn hello(&self) {} +} +struct Package(pub *const Apple); +fn foobar(package: *const Package) { + unsafe { &*(*package).0 }.hello(); +} diff --git a/tests/ui/unnecessary_ref.rs b/tests/ui/unnecessary_ref.rs index 4e585b9b96b..86bfb76ec26 100644 --- a/tests/ui/unnecessary_ref.rs +++ b/tests/ui/unnecessary_ref.rs @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused_variables)] +#![allow(unused_variables, dead_code)] struct Outer { inner: u32, @@ -12,3 +12,12 @@ fn main() { let outer = Outer { inner: 0 }; let inner = (&outer).inner; } + +struct Apple; +impl Apple { + fn hello(&self) {} +} +struct Package(pub *const Apple); +fn foobar(package: *const Package) { + unsafe { &*(&*package).0 }.hello(); +} diff --git a/tests/ui/unnecessary_ref.stderr b/tests/ui/unnecessary_ref.stderr index d0a0f219097..436f4bcf738 100644 --- a/tests/ui/unnecessary_ref.stderr +++ b/tests/ui/unnecessary_ref.stderr @@ -10,5 +10,13 @@ note: the lint level is defined here LL | #[deny(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: creating a reference that is immediately dereferenced + --> $DIR/unnecessary_ref.rs:22:16 + | +LL | unsafe { &*(&*package).0 }.hello(); + | ^^^^^^^^^^^ help: try this: `(*package)` + | + = note: `-D clippy::ref-in-deref` implied by `-D warnings` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From d4bf59b6ef24042653951b69105a3a68542f3bbd Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 12 Jan 2021 11:35:44 +0100 Subject: size_of_in_element_count: Disable lint on division by byte-size It is fairly common to divide some length in bytes by the byte-size of a single element before creating a `from_raw_parts` slice or similar operation. This lint would erroneously disallow such expressions. Just in case, instead of simply disabling this lint in the RHS of a division, keep track of the inversion and enable it again on recursive division. --- clippy_lints/src/size_of_in_element_count.rs | 14 +++++++++----- tests/ui/size_of_in_element_count/expressions.rs | 9 +++++++++ tests/ui/size_of_in_element_count/expressions.stderr | 10 +++++++++- 3 files changed, 27 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index ea7a76146f5..87e386baadc 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -35,10 +35,11 @@ declare_clippy_lint! { declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option> { match expr.kind { ExprKind::Call(count_func, _func_args) => { if_chain! { + if !inverted; if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) @@ -50,10 +51,13 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => { + get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, inverted)) }, - ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr), + ExprKind::Binary(op, left, right) if BinOpKind::Div == op.node => { + get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, !inverted)) + }, + ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr, inverted), _ => None, } } @@ -128,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { // Find a size_of call in the count parameter expression and // check that it's the same type - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false); if TyS::same_type(pointee_ty, ty_used_for_size_of); then { span_lint_and_help( diff --git a/tests/ui/size_of_in_element_count/expressions.rs b/tests/ui/size_of_in_element_count/expressions.rs index b56910917ba..2594e8fa6ad 100644 --- a/tests/ui/size_of_in_element_count/expressions.rs +++ b/tests/ui/size_of_in_element_count/expressions.rs @@ -20,6 +20,15 @@ fn main() { // Count expression involving divisions of size_of (Should trigger the lint) unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + // Count expression involving divisions by size_of (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::()) }; + + // Count expression involving divisions by multiple size_of (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::())) }; + + // Count expression involving recursive divisions by size_of (Should trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + // No size_of calls (Should not trigger the lint) unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; diff --git a/tests/ui/size_of_in_element_count/expressions.stderr b/tests/ui/size_of_in_element_count/expressions.stderr index 47b98e9d947..0f0dff57f51 100644 --- a/tests/ui/size_of_in_element_count/expressions.stderr +++ b/tests/ui/size_of_in_element_count/expressions.stderr @@ -23,5 +23,13 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: aborting due to 3 previous errors +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/expressions.rs:30:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 6c830ff9e4621c6e159ce8edb33fb225352e7b69 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 19 Dec 2020 18:57:11 +0900 Subject: Run `cargo dev new_lint` --- CHANGELOG.md | 1 + clippy_lints/src/capitalized_acronyms.rs | 28 ++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 ++ tests/ui/capitalized_acronyms.rs | 5 +++++ 4 files changed, 36 insertions(+) create mode 100644 clippy_lints/src/capitalized_acronyms.rs create mode 100644 tests/ui/capitalized_acronyms.rs (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b3adeed7720..963c9e77a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1877,6 +1877,7 @@ Released 2018-09-13 [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`capitalized_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#capitalized_acronyms [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless diff --git a/clippy_lints/src/capitalized_acronyms.rs b/clippy_lints/src/capitalized_acronyms.rs new file mode 100644 index 00000000000..835e92dc04b --- /dev/null +++ b/clippy_lints/src/capitalized_acronyms.rs @@ -0,0 +1,28 @@ +use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_ast::ast::*; + +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 CAPITALIZED_ACRONYMS, + style, + "default lint description" +} + +declare_lint_pass!(CapitalizedAcronyms => [CAPITALIZED_ACRONYMS]); + +impl EarlyLintPass for CapitalizedAcronyms {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0b1b347ce42..a236e1b54bb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod booleans; mod bytecount; +mod capitalized_acronyms; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; mod checked_conversions; @@ -559,6 +560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, + &capitalized_acronyms::CAPITALIZED_ACRONYMS, &cargo_common_metadata::CARGO_COMMON_METADATA, &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, &checked_conversions::CHECKED_CONVERSIONS, diff --git a/tests/ui/capitalized_acronyms.rs b/tests/ui/capitalized_acronyms.rs new file mode 100644 index 00000000000..bf5ab9f8bb7 --- /dev/null +++ b/tests/ui/capitalized_acronyms.rs @@ -0,0 +1,5 @@ +#![warn(clippy::capitalized_acronyms)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From ab1da8f86569e55d4b33702c6a062551f8abdd6e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 19 Dec 2020 22:50:45 +0900 Subject: Add new lint `upper_case_acronyms` --- CHANGELOG.md | 2 +- clippy_lints/src/capitalized_acronyms.rs | 28 --------- clippy_lints/src/cognitive_complexity.rs | 8 +-- clippy_lints/src/int_plus_one.rs | 24 ++++---- clippy_lints/src/lib.rs | 10 +++- clippy_lints/src/serde_api.rs | 4 +- clippy_lints/src/upper_case_acronyms.rs | 93 ++++++++++++++++++++++++++++++ tests/ui/capitalized_acronyms.rs | 5 -- tests/ui/complex_types.rs | 2 +- tests/ui/complex_types.stderr | 2 +- tests/ui/crashes/ice-6256.rs | 1 + tests/ui/crashes/ice-6256.stderr | 6 +- tests/ui/enum_variants.rs | 2 +- tests/ui/needless_question_mark.fixed | 8 ++- tests/ui/needless_question_mark.rs | 8 ++- tests/ui/needless_question_mark.stderr | 28 ++++----- tests/ui/suspicious_operation_groupings.rs | 8 +-- tests/ui/transmute_ptr_to_ptr.rs | 6 +- tests/ui/unnested_or_patterns.fixed | 2 +- tests/ui/unnested_or_patterns.rs | 2 +- tests/ui/upper_case_acronyms.rs | 21 +++++++ tests/ui/upper_case_acronyms.stderr | 70 ++++++++++++++++++++++ tests/ui/use_self.fixed | 2 +- tests/ui/use_self.rs | 2 +- 24 files changed, 256 insertions(+), 88 deletions(-) delete mode 100644 clippy_lints/src/capitalized_acronyms.rs create mode 100644 clippy_lints/src/upper_case_acronyms.rs delete mode 100644 tests/ui/capitalized_acronyms.rs create mode 100644 tests/ui/upper_case_acronyms.rs create mode 100644 tests/ui/upper_case_acronyms.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 963c9e77a18..04f042b2deb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1877,7 +1877,6 @@ Released 2018-09-13 [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow -[`capitalized_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#capitalized_acronyms [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless @@ -2275,6 +2274,7 @@ Released 2018-09-13 [`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 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms [`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/capitalized_acronyms.rs b/clippy_lints/src/capitalized_acronyms.rs deleted file mode 100644 index 835e92dc04b..00000000000 --- a/clippy_lints/src/capitalized_acronyms.rs +++ /dev/null @@ -1,28 +0,0 @@ -use rustc_lint::{EarlyLintPass, EarlyContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_ast::ast::*; - -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 CAPITALIZED_ACRONYMS, - style, - "default lint description" -} - -declare_lint_pass!(CapitalizedAcronyms => [CAPITALIZED_ACRONYMS]); - -impl EarlyLintPass for CapitalizedAcronyms {} diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index b3ebdf4ca30..f21a734bb43 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -57,9 +57,9 @@ impl CognitiveComplexity { let expr = &body.value; - let mut helper = CCHelper { cc: 1, returns: 0 }; + let mut helper = CcHelper { cc: 1, returns: 0 }; helper.visit_expr(expr); - let CCHelper { cc, returns } = helper; + let CcHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) { returns @@ -136,12 +136,12 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { } } -struct CCHelper { +struct CcHelper { cc: u64, returns: u64, } -impl<'tcx> Visitor<'tcx> for CCHelper { +impl<'tcx> Visitor<'tcx> for CcHelper { type Map = Map<'tcx>; fn visit_expr(&mut self, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index c629ee05ab9..260b8988d37 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -46,8 +46,8 @@ declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]); #[derive(Copy, Clone)] enum Side { - LHS, - RHS, + Lhs, + Rhs, } impl IntPlusOne { @@ -66,11 +66,11 @@ impl IntPlusOne { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { - Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } @@ -82,10 +82,10 @@ impl IntPlusOne { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } @@ -97,10 +97,10 @@ impl IntPlusOne { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } @@ -110,11 +110,11 @@ impl IntPlusOne { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { - Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } @@ -138,8 +138,8 @@ impl IntPlusOne { if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { let rec = match side { - Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), - Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), + Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), + Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), }; return rec; } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a236e1b54bb..9b73c249643 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -6,6 +6,7 @@ #![feature(concat_idents)] #![feature(crate_visibility_modifier)] #![feature(drain_filter)] +#![feature(split_inclusive)] #![feature(in_band_lifetimes)] #![feature(once_cell)] #![feature(or_patterns)] @@ -169,7 +170,6 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod booleans; mod bytecount; -mod capitalized_acronyms; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; mod checked_conversions; @@ -342,6 +342,7 @@ mod unused_self; mod unused_unit; mod unwrap; mod unwrap_in_result; +mod upper_case_acronyms; mod use_self; mod useless_conversion; mod vec; @@ -560,7 +561,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, - &capitalized_acronyms::CAPITALIZED_ACRONYMS, &cargo_common_metadata::CARGO_COMMON_METADATA, &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, &checked_conversions::CHECKED_CONVERSIONS, @@ -946,6 +946,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, &unwrap_in_result::UNWRAP_IN_RESULT, + &upper_case_acronyms::UPPER_CASE_ACRONYMS, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &vec::USELESS_VEC, @@ -985,7 +986,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeAPI); + store.register_late_pass(|| box serde_api::SerdeApi); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); @@ -1176,6 +1177,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_early_pass(|| box upper_case_acronyms::UpperCaseAcronyms); store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); @@ -1661,6 +1663,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), @@ -1778,6 +1781,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&upper_case_acronyms::UPPER_CASE_ACRONYMS), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 44e739725c8..90cf1b6c861 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -18,9 +18,9 @@ declare_clippy_lint! { "various things that will negatively affect your serde experience" } -declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]); +declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]); -impl<'tcx> LateLintPass<'tcx> for SerdeAPI { +impl<'tcx> LateLintPass<'tcx> for SerdeApi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs new file mode 100644 index 00000000000..61e7031716a --- /dev/null +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -0,0 +1,93 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use itertools::Itertools; +use rustc_ast::ast::{Item, ItemKind, Variant}; +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::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** Checks for camel case name containing a capitalized acronym. + /// + /// **Why is this bad?** In CamelCase, acronyms count as one word. + /// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case) + /// for more. + /// + /// **Known problems:** When two acronyms are contiguous, the lint can't tell where + /// the first acronym ends and the second starts, so it suggests to lowercase all of + /// the letters in the second acronym. + /// + /// **Example:** + /// + /// ```rust + /// struct HTTPResponse; + /// ``` + /// Use instead: + /// ```rust + /// struct HttpResponse; + /// ``` + pub UPPER_CASE_ACRONYMS, + style, + "capitalized acronyms are against the naming convention" +} + +declare_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]); + +fn correct_ident(ident: &str) -> String { + let ident = ident.chars().rev().collect::(); + let fragments = ident + .split_inclusive(|x: char| !x.is_ascii_lowercase()) + .rev() + .map(|x| x.chars().rev().collect::()); + + let mut ident = fragments.clone().next().unwrap(); + for (ref prev, ref curr) in fragments.tuple_windows() { + if [prev, curr] + .iter() + .all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase()) + { + ident.push_str(&curr.to_ascii_lowercase()); + } else { + ident.push_str(curr); + } + } + ident +} + +fn check_ident(cx: &EarlyContext<'_>, ident: &Ident) { + let span = ident.span; + let ident = &ident.as_str(); + let corrected = correct_ident(ident); + if ident != &corrected { + span_lint_and_sugg( + cx, + UPPER_CASE_ACRONYMS, + span, + &format!("name `{}` contains a capitalized acronym", ident), + "consider making the acronym lowercase, except the initial letter", + corrected, + Applicability::MaybeIncorrect, + ) + } +} + +impl EarlyLintPass for UpperCaseAcronyms { + fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) { + if_chain! { + if !in_external_macro(cx.sess(), it.span); + if matches!( + it.kind, + ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..) + ); + then { + check_ident(cx, &it.ident); + } + } + } + + fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) { + check_ident(cx, &v.ident); + } +} diff --git a/tests/ui/capitalized_acronyms.rs b/tests/ui/capitalized_acronyms.rs deleted file mode 100644 index bf5ab9f8bb7..00000000000 --- a/tests/ui/capitalized_acronyms.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![warn(clippy::capitalized_acronyms)] - -fn main() { - // test code goes here -} diff --git a/tests/ui/complex_types.rs b/tests/ui/complex_types.rs index be61fb6b9be..383bbb49dbe 100644 --- a/tests/ui/complex_types.rs +++ b/tests/ui/complex_types.rs @@ -11,7 +11,7 @@ struct S { f: Vec>>, } -struct TS(Vec>>); +struct Ts(Vec>>); enum E { Tuple(Vec>>), diff --git a/tests/ui/complex_types.stderr b/tests/ui/complex_types.stderr index 8f5dbd27956..7fcbb4bce88 100644 --- a/tests/ui/complex_types.stderr +++ b/tests/ui/complex_types.stderr @@ -21,7 +21,7 @@ LL | f: Vec>>, error: very complex type used. Consider factoring parts into `type` definitions --> $DIR/complex_types.rs:14:11 | -LL | struct TS(Vec>>); +LL | struct Ts(Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs index 6f60d45d68a..5409f36b3f1 100644 --- a/tests/ui/crashes/ice-6256.rs +++ b/tests/ui/crashes/ice-6256.rs @@ -1,5 +1,6 @@ // 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() +#![allow(clippy::upper_case_acronyms)] trait TT {} diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr index 0e8353a418a..d1a8bdc3c8d 100644 --- a/tests/ui/crashes/ice-6256.stderr +++ b/tests/ui/crashes/ice-6256.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types - --> $DIR/ice-6256.rs:11:28 + --> $DIR/ice-6256.rs:12: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 +note: the anonymous lifetime #1 defined on the body at 12:13... + --> $DIR/ice-6256.rs:12:13 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 01774a2a984..89d99dcf0c8 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -1,6 +1,6 @@ #![feature(non_ascii_idents)] #![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] -#![allow(non_camel_case_types)] +#![allow(non_camel_case_types, clippy::upper_case_acronyms)] enum FakeCallType { CALL, diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index 70218f3f041..71fb3565224 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::needless_question_mark)] -#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![allow( + clippy::needless_return, + clippy::unnecessary_unwrap, + clippy::upper_case_acronyms, + dead_code, + unused_must_use +)] #![feature(custom_inner_attributes)] struct TO { diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 60ac2c8d72e..e31f6f48fa7 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::needless_question_mark)] -#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![allow( + clippy::needless_return, + clippy::unnecessary_unwrap, + clippy::upper_case_acronyms, + dead_code, + unused_must_use +)] #![feature(custom_inner_attributes)] struct TO { diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index b4eb21882ec..567bc518a3f 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,5 +1,5 @@ error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:17:12 + --> $DIR/needless_question_mark.rs:23:12 | LL | return Some(to.magic?); | ^^^^^^^^^^^^^^^ help: try: `to.magic` @@ -7,79 +7,79 @@ LL | return Some(to.magic?); = note: `-D clippy::needless-question-mark` implied by `-D warnings` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:25:12 + --> $DIR/needless_question_mark.rs:31:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:30:5 + --> $DIR/needless_question_mark.rs:36:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:35:21 + --> $DIR/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:44:9 + --> $DIR/needless_question_mark.rs:50:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:49:12 + --> $DIR/needless_question_mark.rs:55:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try: `tr.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:56:12 + --> $DIR/needless_question_mark.rs:62:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:60:5 + --> $DIR/needless_question_mark.rs:66:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:64:21 + --> $DIR/needless_question_mark.rs:70:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:72:9 + --> $DIR/needless_question_mark.rs:78:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:79:16 + --> $DIR/needless_question_mark.rs:85:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:132:9 + --> $DIR/needless_question_mark.rs:138:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:148:9 + --> $DIR/needless_question_mark.rs:154:9 | LL | Some(to.magic?) // should be triggered | ^^^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:156:9 + --> $DIR/needless_question_mark.rs:162:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs index dd6f4ec7bd9..2f8c7cec50f 100644 --- a/tests/ui/suspicious_operation_groupings.rs +++ b/tests/ui/suspicious_operation_groupings.rs @@ -27,7 +27,7 @@ fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { s1.a < s2.a && s1.a < s2.b } -struct SAOnly { +struct SaOnly { a: i32, } @@ -37,13 +37,13 @@ impl S { } } -fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SAOnly) -> bool { +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool { // This is superficially similar to `buggy_ab_cmp`, but we should not suggest // `s2.b` since that is invalid. s1.a < s2.a && s1.a() < s1.b } -fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool { +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool { macro_rules! s1 { () => { S { @@ -60,7 +60,7 @@ fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool s1.a < s2.a && s1!().a < s1.b } -fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SAOnly) -> bool { +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool { // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid s1.a < s2.a && s1.b < s1.b } diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 26b03bdc740..9e213aab68c 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -53,10 +53,10 @@ fn transmute_ptr_to_ptr() { // dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) const _: &() = { - struct ZST; - let zst = &ZST; + struct Zst; + let zst = &Zst; - unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } + unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) } }; fn main() {} diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index b39e891094f..13a036cd800 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -3,7 +3,7 @@ #![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] -#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 096f5a71150..4a10cc702c4 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -3,7 +3,7 @@ #![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] -#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs new file mode 100644 index 00000000000..af0b5776348 --- /dev/null +++ b/tests/ui/upper_case_acronyms.rs @@ -0,0 +1,21 @@ +#![warn(clippy::upper_case_acronyms)] + +struct HTTPResponse; // linted + +struct CString; // not linted + +enum Flags { + NS, // linted + CWR, + ECE, + URG, + ACK, + PSH, + RST, + SYN, + FIN, +} + +struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething` + +fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr new file mode 100644 index 00000000000..2065fe10bb1 --- /dev/null +++ b/tests/ui/upper_case_acronyms.stderr @@ -0,0 +1,70 @@ +error: name `HTTPResponse` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:3:8 + | +LL | struct HTTPResponse; // linted + | ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse` + | + = note: `-D clippy::upper-case-acronyms` implied by `-D warnings` + +error: name `NS` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:8:5 + | +LL | NS, // linted + | ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns` + +error: name `CWR` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:9:5 + | +LL | CWR, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr` + +error: name `ECE` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:10:5 + | +LL | ECE, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Ece` + +error: name `URG` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:11:5 + | +LL | URG, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Urg` + +error: name `ACK` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:12:5 + | +LL | ACK, + | ^^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ack` + +error: name `PSH` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:13:5 + | +LL | PSH, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Psh` + +error: name `RST` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:14:5 + | +LL | RST, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Rst` + +error: name `SYN` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:15:5 + | +LL | SYN, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Syn` + +error: name `FIN` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:16:5 + | +LL | FIN, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` + +error: name `GCCLLVMSomething` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:19:8 + | +LL | struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething` + | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` + +error: aborting due to 11 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index d6a890014e6..bb2012441d9 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -3,7 +3,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -#![allow(clippy::should_implement_trait)] +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)] fn main() {} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index b04d9ce75b2..ddfd2beba31 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -3,7 +3,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -#![allow(clippy::should_implement_trait)] +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)] fn main() {} -- cgit 1.4.1-3-g733a5 From 0ccb491caa668d55522c1bc78e368329a310db4c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 20 Jan 2021 18:14:09 +0900 Subject: Remove nightly-gate of `split_inclusive` Co-authored-by: Philipp Krones --- clippy_lints/src/lib.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9b73c249643..53764bb7390 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -6,7 +6,6 @@ #![feature(concat_idents)] #![feature(crate_visibility_modifier)] #![feature(drain_filter)] -#![feature(split_inclusive)] #![feature(in_band_lifetimes)] #![feature(once_cell)] #![feature(or_patterns)] -- cgit 1.4.1-3-g733a5 From e42208f1b7befe316e32c66b6dc6d242a56f4d84 Mon Sep 17 00:00:00 2001 From: pastchick3 <331604390@qq.com> Date: Wed, 20 Jan 2021 20:05:25 +0800 Subject: Improve the suggestion message of `stable_sort_primitive`. --- clippy_lints/src/stable_sort_primitive.rs | 31 +++++++++++++++++++------------ tests/ui/stable_sort_primitive.stderr | 27 ++++++++++++++++++++------- 2 files changed, 39 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 99e4b293ac6..276a9338819 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; +use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg}; use if_chain::if_chain; @@ -107,25 +107,32 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option 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( + span_lint_and_then( cx, STABLE_SORT_PRIMITIVE, expr.span, format!( - "used {} instead of {} to sort primitive type `{}`", + "used `{}` on primitive type `{}`", detection.method.stable_name(), - detection.method.unstable_name(), detection.slice_type, ) .as_str(), - "try", - format!( - "{}.{}({})", - detection.slice_name, - detection.method.unstable_name(), - detection.method_args - ), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + expr.span, + "try", + format!( + "{}.{}({})", + detection.slice_name, + detection.method.unstable_name(), + detection.method_args, + ), + Applicability::MachineApplicable, + ); + diag.note( + "an unstable sort would perform faster without any observable difference for this data type", + ); + }, ); } } diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index 780389f32bc..b8d22ed2504 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,46 +1,59 @@ -error: used sort instead of sort_unstable to sort primitive type `i32` +error: used `sort` on primitive type `i32` --> $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` + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `bool` +error: used `sort` on primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `char` +error: used `sort` on primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `str` +error: used `sort` on primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `tuple` +error: used `sort` on primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `array` +error: used `sort` on primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `i32` +error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); | ^^^^^^^^^^ help: try: `arr.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From f1ab3024b27cc7c02a80fd54382a10a1b4ef3bcd Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 12:48:30 -0800 Subject: New lint: exhaustive_enums --- CHANGELOG.md | 1 + clippy_lints/src/exhaustive_enums.rs | 68 ++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 +++ tests/ui/exhaustive_enums.fixed | 24 +++++++++++++ tests/ui/exhaustive_enums.rs | 23 ++++++++++++ tests/ui/exhaustive_enums.stderr | 28 +++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 clippy_lints/src/exhaustive_enums.rs create mode 100644 tests/ui/exhaustive_enums.fixed create mode 100644 tests/ui/exhaustive_enums.rs create mode 100644 tests/ui/exhaustive_enums.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f042b2deb..4cf2125ea2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1938,6 +1938,7 @@ Released 2018-09-13 [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums [`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 diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs new file mode 100644 index 00000000000..099171962d3 --- /dev/null +++ b/clippy_lints/src/exhaustive_enums.rs @@ -0,0 +1,68 @@ +use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::{Item, ItemKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// **What it does:** Warns on any `enum`s that are not tagged `#[non_exhaustive]` + /// + /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does + /// not wish to make a stability commitment around enums may wish to disable them by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// enum Foo { + /// Bar, + /// Baz + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// enum Foo { + /// Bar, + /// Baz + /// } /// ``` + pub EXHAUSTIVE_ENUMS, + restriction, + "default lint description" +} + +declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]); + +impl EarlyLintPass for ExhaustiveEnums { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if let ItemKind::Enum(..) = item.kind; + if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); + then { + if let Some(snippet) = snippet_opt(cx, item.span) { + span_lint_and_sugg( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + "try adding #[non_exhaustive]", + format!("#[non_exhaustive]\n{}", snippet), + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_help( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + None, + "try adding #[non_exhaustive]", + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 53764bb7390..465ad3846ce 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -200,6 +200,7 @@ mod escape; mod eta_reduction; mod eval_order_dependence; mod excessive_bools; +mod exhaustive_enums; mod exit; mod explicit_write; mod fallible_impl_from; @@ -611,6 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS, + &exhaustive_enums::EXHAUSTIVE_ENUMS, &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, @@ -1096,6 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); + store.register_early_pass(move || box exhaustive_enums::ExhaustiveEnums); store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); @@ -1246,6 +1249,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(&exhaustive_enums::EXHAUSTIVE_ENUMS), LintId::of(&exit::EXIT), LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), LintId::of(&implicit_return::IMPLICIT_RETURN), diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed new file mode 100644 index 00000000000..2d5f0474bc0 --- /dev/null +++ b/tests/ui/exhaustive_enums.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +#[non_exhaustive] +enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +#[non_exhaustive] +enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs new file mode 100644 index 00000000000..5c88454ae61 --- /dev/null +++ b/tests/ui/exhaustive_enums.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +#[non_exhaustive] +enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr new file mode 100644 index 00000000000..ee5a1836267 --- /dev/null +++ b/tests/ui/exhaustive_enums.stderr @@ -0,0 +1,28 @@ +error: enums should not be exhaustive + --> $DIR/exhaustive_enums.rs:10:1 + | +LL | / enum Exhaustive { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/exhaustive_enums.rs:3:9 + | +LL | #![deny(clippy::exhaustive_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | enum Exhaustive { +LL | Foo, +LL | Bar, +LL | Baz, +LL | Quux(String), + ... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From dc93188805ac20fbccd7bd616a6b114680e9303a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:31:15 -0800 Subject: Make exhaustive_enums a late pass --- clippy_lints/src/exhaustive_enums.rs | 8 ++++---- clippy_lints/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs index 099171962d3..1391ebd9e31 100644 --- a/clippy_lints/src/exhaustive_enums.rs +++ b/clippy_lints/src/exhaustive_enums.rs @@ -1,8 +1,8 @@ use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Item, ItemKind}; +use rustc_hir::{Item, ItemKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -36,8 +36,8 @@ declare_clippy_lint! { declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]); -impl EarlyLintPass for ExhaustiveEnums { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +impl LateLintPass<'_> for ExhaustiveEnums { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if_chain! { if let ItemKind::Enum(..) = item.kind; if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 465ad3846ce..becd9f333fd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1098,7 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); - store.register_early_pass(move || box exhaustive_enums::ExhaustiveEnums); + store.register_late_pass(move || box exhaustive_enums::ExhaustiveEnums); store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); -- cgit 1.4.1-3-g733a5 From f6cb96ef07ac6197dac5be16adbe2b7950c82d99 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:34:44 -0800 Subject: Make exhaustive_enums only warn on exported items --- clippy_lints/src/exhaustive_enums.rs | 3 ++- tests/ui/exhaustive_enums.fixed | 22 ++++++++++++++++++++-- tests/ui/exhaustive_enums.rs | 22 ++++++++++++++++++++-- tests/ui/exhaustive_enums.stderr | 4 ++-- 4 files changed, 44 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs index 1391ebd9e31..2e1c0728d2c 100644 --- a/clippy_lints/src/exhaustive_enums.rs +++ b/clippy_lints/src/exhaustive_enums.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; declare_clippy_lint! { - /// **What it does:** Warns on any `enum`s that are not tagged `#[non_exhaustive]` + /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does /// not wish to make a stability commitment around enums may wish to disable them by default. @@ -40,6 +40,7 @@ impl LateLintPass<'_> for ExhaustiveEnums { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if_chain! { if let ItemKind::Enum(..) = item.kind; + if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { if let Some(snippet) = snippet_opt(cx, item.span) { diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed index 2d5f0474bc0..71c4a251e3b 100644 --- a/tests/ui/exhaustive_enums.fixed +++ b/tests/ui/exhaustive_enums.fixed @@ -8,15 +8,33 @@ fn main() { } #[non_exhaustive] -enum Exhaustive { +pub enum Exhaustive { Foo, Bar, Baz, Quux(String), } +// no warning, already non_exhaustive #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { Foo, Bar, Baz, diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs index 5c88454ae61..45af6851dd1 100644 --- a/tests/ui/exhaustive_enums.rs +++ b/tests/ui/exhaustive_enums.rs @@ -7,15 +7,33 @@ fn main() { // nop } -enum Exhaustive { +pub enum Exhaustive { Foo, Bar, Baz, Quux(String), } +// no warning, already non_exhaustive #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { Foo, Bar, Baz, diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr index ee5a1836267..280c40b00aa 100644 --- a/tests/ui/exhaustive_enums.stderr +++ b/tests/ui/exhaustive_enums.stderr @@ -1,7 +1,7 @@ error: enums should not be exhaustive --> $DIR/exhaustive_enums.rs:10:1 | -LL | / enum Exhaustive { +LL | / pub enum Exhaustive { LL | | Foo, LL | | Bar, LL | | Baz, @@ -17,7 +17,7 @@ LL | #![deny(clippy::exhaustive_enums)] help: try adding #[non_exhaustive] | LL | #[non_exhaustive] -LL | enum Exhaustive { +LL | pub enum Exhaustive { LL | Foo, LL | Bar, LL | Baz, -- cgit 1.4.1-3-g733a5 From 09d4d49299c6614a0ae956980e709a234d21a9ef Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:36:18 -0800 Subject: ExhaustiveEnums -> ExhaustiveItems --- clippy_lints/src/exhaustive_enums.rs | 69 ------------------------------------ clippy_lints/src/exhaustive_items.rs | 69 ++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 8 ++--- tests/ui/exhaustive_enums.fixed | 42 ---------------------- tests/ui/exhaustive_enums.rs | 41 --------------------- tests/ui/exhaustive_enums.stderr | 28 --------------- tests/ui/exhaustive_items.fixed | 42 ++++++++++++++++++++++ tests/ui/exhaustive_items.rs | 41 +++++++++++++++++++++ tests/ui/exhaustive_items.stderr | 28 +++++++++++++++ 9 files changed, 184 insertions(+), 184 deletions(-) delete mode 100644 clippy_lints/src/exhaustive_enums.rs create mode 100644 clippy_lints/src/exhaustive_items.rs delete mode 100644 tests/ui/exhaustive_enums.fixed delete mode 100644 tests/ui/exhaustive_enums.rs delete mode 100644 tests/ui/exhaustive_enums.stderr create mode 100644 tests/ui/exhaustive_items.fixed create mode 100644 tests/ui/exhaustive_items.rs create mode 100644 tests/ui/exhaustive_items.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs deleted file mode 100644 index 2e1c0728d2c..00000000000 --- a/clippy_lints/src/exhaustive_enums.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_hir::{Item, ItemKind}; -use rustc_errors::Applicability; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` - /// - /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does - /// not wish to make a stability commitment around enums may wish to disable them by default. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// enum Foo { - /// Bar, - /// Baz - /// } - /// ``` - /// Use instead: - /// ```rust - /// #[non_exhaustive] - /// enum Foo { - /// Bar, - /// Baz - /// } /// ``` - pub EXHAUSTIVE_ENUMS, - restriction, - "default lint description" -} - -declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]); - -impl LateLintPass<'_> for ExhaustiveEnums { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Enum(..) = item.kind; - if cx.access_levels.is_exported(item.hir_id); - if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); - then { - if let Some(snippet) = snippet_opt(cx, item.span) { - span_lint_and_sugg( - cx, - EXHAUSTIVE_ENUMS, - item.span, - "enums should not be exhaustive", - "try adding #[non_exhaustive]", - format!("#[non_exhaustive]\n{}", snippet), - Applicability::MaybeIncorrect, - ); - } else { - span_lint_and_help( - cx, - EXHAUSTIVE_ENUMS, - item.span, - "enums should not be exhaustive", - None, - "try adding #[non_exhaustive]", - ); - } - } - } - } -} diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs new file mode 100644 index 00000000000..0fa6c4b589f --- /dev/null +++ b/clippy_lints/src/exhaustive_items.rs @@ -0,0 +1,69 @@ +use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_hir::{Item, ItemKind}; +use rustc_errors::Applicability; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` + /// + /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does + /// not wish to make a stability commitment around enums may wish to disable them by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// enum Foo { + /// Bar, + /// Baz + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// enum Foo { + /// Bar, + /// Baz + /// } /// ``` + pub EXHAUSTIVE_ENUMS, + restriction, + "default lint description" +} + +declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS]); + +impl LateLintPass<'_> for ExhaustiveItems { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Enum(..) = item.kind; + if cx.access_levels.is_exported(item.hir_id); + if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); + then { + if let Some(snippet) = snippet_opt(cx, item.span) { + span_lint_and_sugg( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + "try adding #[non_exhaustive]", + format!("#[non_exhaustive]\n{}", snippet), + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_help( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + None, + "try adding #[non_exhaustive]", + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index becd9f333fd..a5deebf7d5f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -200,7 +200,7 @@ mod escape; mod eta_reduction; mod eval_order_dependence; mod excessive_bools; -mod exhaustive_enums; +mod exhaustive_items; mod exit; mod explicit_write; mod fallible_impl_from; @@ -612,7 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS, - &exhaustive_enums::EXHAUSTIVE_ENUMS, + &exhaustive_items::EXHAUSTIVE_ENUMS, &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, @@ -1098,7 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); - store.register_late_pass(move || box exhaustive_enums::ExhaustiveEnums); + store.register_late_pass(move || box exhaustive_items::ExhaustiveItems); store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); @@ -1249,7 +1249,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(&exhaustive_enums::EXHAUSTIVE_ENUMS), + LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(&exit::EXIT), LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), LintId::of(&implicit_return::IMPLICIT_RETURN), diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed deleted file mode 100644 index 71c4a251e3b..00000000000 --- a/tests/ui/exhaustive_enums.fixed +++ /dev/null @@ -1,42 +0,0 @@ -// run-rustfix - -#![deny(clippy::exhaustive_enums)] -#![allow(unused)] - -fn main() { - // nop -} - -#[non_exhaustive] -pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs deleted file mode 100644 index 45af6851dd1..00000000000 --- a/tests/ui/exhaustive_enums.rs +++ /dev/null @@ -1,41 +0,0 @@ -// run-rustfix - -#![deny(clippy::exhaustive_enums)] -#![allow(unused)] - -fn main() { - // nop -} - -pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr deleted file mode 100644 index 280c40b00aa..00000000000 --- a/tests/ui/exhaustive_enums.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: enums should not be exhaustive - --> $DIR/exhaustive_enums.rs:10:1 - | -LL | / pub enum Exhaustive { -LL | | Foo, -LL | | Bar, -LL | | Baz, -LL | | Quux(String), -LL | | } - | |_^ - | -note: the lint level is defined here - --> $DIR/exhaustive_enums.rs:3:9 - | -LL | #![deny(clippy::exhaustive_enums)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: try adding #[non_exhaustive] - | -LL | #[non_exhaustive] -LL | pub enum Exhaustive { -LL | Foo, -LL | Bar, -LL | Baz, -LL | Quux(String), - ... - -error: aborting due to previous error - diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed new file mode 100644 index 00000000000..71c4a251e3b --- /dev/null +++ b/tests/ui/exhaustive_items.fixed @@ -0,0 +1,42 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +#[non_exhaustive] +pub enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, already non_exhaustive +#[non_exhaustive] +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs new file mode 100644 index 00000000000..45af6851dd1 --- /dev/null +++ b/tests/ui/exhaustive_items.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +pub enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, already non_exhaustive +#[non_exhaustive] +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr new file mode 100644 index 00000000000..d00d950efc7 --- /dev/null +++ b/tests/ui/exhaustive_items.stderr @@ -0,0 +1,28 @@ +error: enums should not be exhaustive + --> $DIR/exhaustive_items.rs:10:1 + | +LL | / pub enum Exhaustive { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/exhaustive_items.rs:3:9 + | +LL | #![deny(clippy::exhaustive_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | pub enum Exhaustive { +LL | Foo, +LL | Bar, +LL | Baz, +LL | Quux(String), + ... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 2d509f8b404b77fd7fa949a13fd38c9f5f51b9fb Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 21 Jan 2021 18:12:46 -0600 Subject: Check if let guard in collapsible_match --- clippy_lints/src/collapsible_match.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index a34ba2d00a8..c75aa2bde97 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -85,7 +85,12 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) { // the "wild-like" branches must be equal if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); // the binding must not be used in the if guard - if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard)); + if match arm.guard { + None => true, + Some(Guard::If(expr) | Guard::IfLet(_, expr)) => { + !LocalUsedVisitor::new(binding_id).check_expr(expr) + } + }; // ...or anywhere in the inner match if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm)); then { -- cgit 1.4.1-3-g733a5 From e89ad4ba71816cbe6fb1f90743d9229fc824b1c0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 14 Dec 2020 13:50:53 -0600 Subject: Fix comment --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 623554ea2d1..c8b69f4fbae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3059,7 +3059,7 @@ fn lint_filter_map_map<'tcx>( _filter_args: &'tcx [hir::Expr<'_>], _map_args: &'tcx [hir::Expr<'_>], ) { - // lint if caller of `.filter().map()` is an Iterator + // lint if caller of `.filter_map().map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let msg = "called `filter_map(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; -- cgit 1.4.1-3-g733a5 From 7a8660861ecf11474e03823387a50eeaa2c18a57 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 1 Jan 2021 13:00:44 -0600 Subject: Add expr_fallback to SpanlessEq --- clippy_lints/src/utils/hir_utils.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 10120a8805d..1c7398f17f2 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -24,6 +24,7 @@ pub struct SpanlessEq<'a, 'tcx> { cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, allow_side_effects: bool, + expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -32,6 +33,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { cx, maybe_typeck_results: cx.maybe_typeck_results(), allow_side_effects: true, + expr_fallback: None, } } @@ -43,6 +45,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } + pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { + Self { + expr_fallback: Some(Box::new(expr_fallback)), + ..self + } + } + /// Checks whether two statements are the same. pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { @@ -81,7 +90,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { + let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, @@ -158,7 +167,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), _ => false, - } + }; + is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right)) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { -- cgit 1.4.1-3-g733a5 From c92bdc4dbbd777f6933f7990f87066147a629c8d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 1 Jan 2021 13:00:09 -0600 Subject: Split filter_map into manual_filter_map --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 109 +++++++++++++++++++++++++++++++++----- tests/ui/filter_methods.stderr | 12 +---- tests/ui/manual_filter_map.fixed | 37 +++++++++++++ tests/ui/manual_filter_map.rs | 37 +++++++++++++ tests/ui/manual_filter_map.stderr | 22 ++++++++ 7 files changed, 198 insertions(+), 23 deletions(-) create mode 100644 tests/ui/manual_filter_map.fixed create mode 100644 tests/ui/manual_filter_map.rs create mode 100644 tests/ui/manual_filter_map.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f042b2deb..8feb1a148af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2035,6 +2035,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_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 53764bb7390..bde9c630c6c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -745,6 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH, &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, + &methods::MANUAL_FILTER_MAP, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, @@ -1526,6 +1527,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::ITER_NTH), LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), + LintId::of(&methods::MANUAL_FILTER_MAP), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), @@ -1823,6 +1825,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::INSPECT_FOR_EACH), + LintId::of(&methods::MANUAL_FILTER_MAP), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c8b69f4fbae..518d2e67ad1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,7 +15,8 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{TraitItem, TraitItemKind}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, PatKind, QPath, TraitItem, TraitItemKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; @@ -450,6 +451,32 @@ declare_clippy_lint! { "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply + /// as `filter_map(_)`. + /// + /// **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and + /// less performant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// (0_i32..10) + /// .filter(|n| n.checked_add(1).is_some()) + /// .map(|n| n.checked_add(1).unwrap()); + /// ``` + /// + /// Good: + /// ```rust + /// (0_i32..10).filter_map(|n| n.checked_add(1)); + /// ``` + pub MANUAL_FILTER_MAP, + complexity, + "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`" +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// @@ -1473,6 +1500,7 @@ impl_lint_pass!(Methods => [ FILTER_NEXT, SKIP_WHILE_NEXT, FILTER_MAP, + MANUAL_FILTER_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, FIND_MAP, @@ -1540,7 +1568,7 @@ impl<'tcx> LateLintPass<'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"] => lint_filter_map(cx, expr), ["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], self.msrv.as_ref()), ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2989,17 +3017,72 @@ fn lint_skip_while_next<'tcx>( } /// lint use of `filter().map()` for `Iterators` -fn lint_filter_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter().map()` is an Iterator - if match_trait_method(cx, expr, &paths::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); +fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; + if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; + if match_trait_method(cx, expr, &paths::ITERATOR); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::UnDeref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + if_chain! { + if let ExprKind::Path(QPath::Resolved(None, a_path)) = a_path.kind; + if let ExprKind::Path(QPath::Resolved(None, b_path)) = b.kind; + if a_path.res == Res::Local(filter_param_id); + if b_path.res == Res::Local(map_param_id); + if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); + then { + return true; + } + } + false + }; + if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + then { + let span = filter_span.to(map_span); + let msg = "`filter(..).map(..)` can be simplified as `filter_map(..)`"; + let to_opt = if is_result { ".ok()" } else { "" }; + let sugg = format!("filter_map(|{}| {}{})", map_param_ident, snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, MANUAL_FILTER_MAP, span, msg, "try", sugg, Applicability::MachineApplicable); + } } } diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 11922681379..c7b4f28be3a 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,12 +1,3 @@ -error: called `filter(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:6:21 - | -LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::filter-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.filter_map(..)` instead - error: called `filter(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:8:21 | @@ -17,6 +8,7 @@ LL | | .filter(|&x| x == 0) LL | | .flat_map(|x| x.checked_mul(2)) | |_______________________________________^ | + = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` error: called `filter_map(..).flat_map(..)` on an `Iterator` @@ -43,5 +35,5 @@ LL | | .map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by only calling `.filter_map(..)` instead -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed new file mode 100644 index 00000000000..fc8f58f8ea5 --- /dev/null +++ b/tests/ui/manual_filter_map.fixed @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_filter_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).filter_map(|a| to_opt(a)); + + // ref pattern, expect() + let _ = (0..).filter_map(|a| to_opt(a)); + + // is_ok(), unwrap_or() + let _ = (0..).filter_map(|a| to_res(a).ok()); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .filter(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs new file mode 100644 index 00000000000..3af4bbee3bf --- /dev/null +++ b/tests/ui/manual_filter_map.rs @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_filter_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + + // ref pattern, expect() + let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + + // is_ok(), unwrap_or() + let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .filter(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr new file mode 100644 index 00000000000..4d4e2d5c12f --- /dev/null +++ b/tests/ui/manual_filter_map.stderr @@ -0,0 +1,22 @@ +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:8:19 + | +LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` + | + = note: `-D clippy::manual-filter-map` implied by `-D warnings` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:11:19 + | +LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:14:19 + | +LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From a752d31e0a8cd8c7d8549daf421a8b791d1325a4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 14 Jan 2021 16:36:36 -0600 Subject: Replace find_map with manual_find_map --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 64 ++++++++++++++++++++++++++--------------- tests/ui/find_map.stderr | 26 ----------------- tests/ui/manual_find_map.fixed | 37 ++++++++++++++++++++++++ tests/ui/manual_find_map.rs | 37 ++++++++++++++++++++++++ tests/ui/manual_find_map.stderr | 22 ++++++++++++++ 7 files changed, 141 insertions(+), 49 deletions(-) delete mode 100644 tests/ui/find_map.stderr create mode 100644 tests/ui/manual_find_map.fixed create mode 100644 tests/ui/manual_find_map.rs create mode 100644 tests/ui/manual_find_map.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 8feb1a148af..7f2de888d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2036,6 +2036,7 @@ Released 2018-09-13 [`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_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bde9c630c6c..b22ddfacf86 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -746,6 +746,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, &methods::MANUAL_FILTER_MAP, + &methods::MANUAL_FIND_MAP, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, @@ -1528,6 +1529,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_FILTER_MAP), + LintId::of(&methods::MANUAL_FIND_MAP), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), @@ -1826,6 +1828,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::INSPECT_FOR_EACH), LintId::of(&methods::MANUAL_FILTER_MAP), + LintId::of(&methods::MANUAL_FIND_MAP), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 518d2e67ad1..9d078588746 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -477,6 +477,32 @@ declare_clippy_lint! { "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply + /// as `find_map(_)`. + /// + /// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and + /// less performant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// (0_i32..10) + /// .find(|n| n.checked_add(1).is_some()) + /// .map(|n| n.checked_add(1).unwrap()); + /// ``` + /// + /// Good: + /// ```rust + /// (0_i32..10).find_map(|n| n.checked_add(1)); + /// ``` + pub MANUAL_FIND_MAP, + complexity, + "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`" +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// @@ -1501,6 +1527,7 @@ impl_lint_pass!(Methods => [ SKIP_WHILE_NEXT, FILTER_MAP, MANUAL_FILTER_MAP, + MANUAL_FIND_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, FIND_MAP, @@ -1568,10 +1595,10 @@ impl<'tcx> LateLintPass<'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), + ["map", "filter"] => lint_filter_map(cx, expr, false), ["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], self.msrv.as_ref()), - ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), + ["map", "find"] => lint_filter_map(cx, expr, true), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), @@ -3016,12 +3043,12 @@ fn lint_skip_while_next<'tcx>( } } -/// lint use of `filter().map()` for `Iterators` -fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { +/// lint use of `filter().map()` or `find().map()` for `Iterators` +fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { if_chain! { if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if match_trait_method(cx, expr, &paths::ITERATOR); + if match_trait_method(cx, map_recv, &paths::ITERATOR); // filter(|x| ...is_some())... if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; @@ -3078,10 +3105,16 @@ fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); then { let span = filter_span.to(map_span); - let msg = "`filter(..).map(..)` can be simplified as `filter_map(..)`"; + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); let to_opt = if is_result { ".ok()" } else { "" }; - let sugg = format!("filter_map(|{}| {}{})", map_param_ident, snippet(cx, map_arg.span, ".."), to_opt); - span_lint_and_sugg(cx, MANUAL_FILTER_MAP, span, msg, "try", sugg, Applicability::MachineApplicable); + let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, + snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); } } } @@ -3120,21 +3153,6 @@ fn lint_filter_map_next<'tcx>( } } -/// lint use of `find().map()` for `Iterators` -fn lint_find_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _find_args: &'tcx [hir::Expr<'_>], - map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter().map()` is an Iterator - if match_trait_method(cx, &map_args[0], &paths::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); - } -} - /// lint use of `filter_map().map()` for `Iterators` fn lint_filter_map_map<'tcx>( cx: &LateContext<'tcx>, diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr deleted file mode 100644 index aea3cc62afc..00000000000 --- a/tests/ui/find_map.stderr +++ /dev/null @@ -1,26 +0,0 @@ -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()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::find-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.find_map(..)` instead - -error: called `find(..).map(..)` on an `Iterator` - --> $DIR/find_map.rs:23:29 - | -LL | let _: Option = desserts_of_the_week - | _____________________________^ -LL | | .iter() -LL | | .find(|dessert| match *dessert { -LL | | Dessert::Cake(_) => true, -... | -LL | | _ => unreachable!(), -LL | | }); - | |__________^ - | - = help: this is more succinctly expressed by calling `.find_map(..)` instead - -error: aborting due to 2 previous errors - diff --git a/tests/ui/manual_find_map.fixed b/tests/ui/manual_find_map.fixed new file mode 100644 index 00000000000..95e97c4fd1f --- /dev/null +++ b/tests/ui/manual_find_map.fixed @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_find_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).find_map(|a| to_opt(a)); + + // ref pattern, expect() + let _ = (0..).find_map(|a| to_opt(a)); + + // is_ok(), unwrap_or() + let _ = (0..).find_map(|a| to_res(a).ok()); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .find(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_find_map.rs b/tests/ui/manual_find_map.rs new file mode 100644 index 00000000000..cd3c82e3b25 --- /dev/null +++ b/tests/ui/manual_find_map.rs @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_find_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + + // ref pattern, expect() + let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + + // is_ok(), unwrap_or() + let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .find(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr new file mode 100644 index 00000000000..9e7f798df45 --- /dev/null +++ b/tests/ui/manual_find_map.stderr @@ -0,0 +1,22 @@ +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:8:19 + | +LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` + | + = note: `-D clippy::manual-find-map` implied by `-D warnings` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:11:19 + | +LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:14:19 + | +LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From a22915bf4893e16d64100365d902e52211374a0c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 14 Dec 2020 14:03:11 -0600 Subject: Remove unneeded allow's --- clippy_dev/src/ra_setup.rs | 2 -- clippy_lints/src/loops.rs | 1 - 2 files changed, 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index 5f5048e79e7..a8a6a2cb1bd 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -1,5 +1,3 @@ -#![allow(clippy::filter_map)] - use std::fs; use std::fs::File; use std::io::prelude::*; diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c5ab2874b0..c7e1088e353 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1069,7 +1069,6 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( ) -> 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 { -- cgit 1.4.1-3-g733a5 From 82bab19a0105ff86176b79a5eae7a9ea7d300cb9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 14 Jan 2021 16:47:22 -0600 Subject: Deprecate find_map lint --- clippy_lints/src/deprecated_lints.rs | 5 +++++ clippy_lints/src/lib.rs | 6 ++++-- clippy_lints/src/methods/mod.rs | 23 ----------------------- 3 files changed, 9 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index bec0c9f93a0..ea386237f20 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -166,3 +166,8 @@ declare_deprecated_lint! { pub PANIC_PARAMS, "this lint has been uplifted to rustc and is now called `panic_fmt`" } + +declare_deprecated_lint! { + pub FIND_MAP, + "this lint is replaced by `manual_find_map`, a more specific lint" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b22ddfacf86..7097812e088 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -505,6 +505,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::panic_params", "this lint has been uplifted to rustc and is now called `panic_fmt`", ); + store.register_removed( + "clippy::find_map", + "this lint is replaced by `manual_find_map`, a more specific lint", + ); // 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` @@ -732,7 +736,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::FILTER_MAP, &methods::FILTER_MAP_NEXT, &methods::FILTER_NEXT, - &methods::FIND_MAP, &methods::FLAT_MAP_IDENTITY, &methods::FROM_ITER_INSTEAD_OF_COLLECT, &methods::GET_UNWRAP, @@ -1333,7 +1336,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), - LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), LintId::of(&methods::MAP_UNWRAP_OR), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9d078588746..7a459a440ca 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -547,28 +547,6 @@ declare_clippy_lint! { "call to `flat_map` where `flatten` is sufficient" } -declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.find(_).map(_)`. - /// - /// **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. - /// - /// **Example:** - /// ```rust - /// (0..3).find(|x| *x == 2).map(|x| x * 2); - /// ``` - /// Can be written as - /// ```rust - /// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None }); - /// ``` - pub FIND_MAP, - pedantic, - "using a combination of `find` and `map` can usually be written as a single method call" -} - declare_clippy_lint! { /// **What it does:** Checks for an iterator or string search (such as `find()`, /// `position()`, or `rposition()`) followed by a call to `is_some()`. @@ -1530,7 +1508,6 @@ impl_lint_pass!(Methods => [ MANUAL_FIND_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, - FIND_MAP, MAP_FLATTEN, ITERATOR_STEP_BY_ZERO, ITER_NEXT_SLICE, -- cgit 1.4.1-3-g733a5 From 23662d1353a52be7c35bb3905337f2aadf7c504a Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 22 Jan 2021 22:51:06 +0900 Subject: Improve the example in `ref_in_deref` --- clippy_lints/src/reference.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 2dfb947b5eb..e1450466a7c 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -111,6 +111,12 @@ declare_clippy_lint! { /// let point = Point(30, 20); /// let x = (&point).0; /// ``` + /// Use instead: + /// ```rust + /// # struct Point(u32, u32); + /// # let point = Point(30, 20); + /// let x = point.0; + /// ``` pub REF_IN_DEREF, complexity, "Use of reference in auto dereference expression." -- cgit 1.4.1-3-g733a5 From 8cb7e85006853e99e6ba2bf378c801fb53eee8fa Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:41:57 -0800 Subject: Add exhaustive_structs lint --- CHANGELOG.md | 1 + clippy_lints/src/exhaustive_items.rs | 54 ++++++++++++++++++---- clippy_lints/src/lib.rs | 2 + tests/ui/exhaustive_items.fixed | 86 ++++++++++++++++++++++++------------ tests/ui/exhaustive_items.rs | 85 +++++++++++++++++++++++------------ tests/ui/exhaustive_items.stderr | 52 +++++++++++++++------- 6 files changed, 199 insertions(+), 81 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf2125ea2f..ccc7eb7e881 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1939,6 +1939,7 @@ Released 2018-09-13 [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs [`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 diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 0fa6c4b589f..d604ad0004f 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,7 +1,7 @@ use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_hir::{Item, ItemKind}; use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -10,7 +10,8 @@ declare_clippy_lint! { /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does - /// not wish to make a stability commitment around enums may wish to disable them by default. + /// not wish to make a stability commitment around exported enums may wish to + /// disable them by default. /// /// **Known problems:** None. /// @@ -28,25 +29,62 @@ declare_clippy_lint! { /// enum Foo { /// Bar, /// Baz - /// } /// ``` + /// } + /// ``` pub EXHAUSTIVE_ENUMS, restriction, - "default lint description" + "detects exported enums that have not been marked #[non_exhaustive]" +} + +declare_clippy_lint! { + /// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]` + /// + /// **Why is this bad?** Exhaustive structs are typically fine, but a project which does + /// not wish to make a stability commitment around exported structs may wish to + /// disable them by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct Foo { + /// bar: u8, + /// baz: String, + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// struct Foo { + /// bar: u8, + /// baz: String, + /// } + /// ``` + pub EXHAUSTIVE_STRUCTS, + restriction, + "detects exported structs that have not been marked #[non_exhaustive]" } -declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS]); +declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if_chain! { - if let ItemKind::Enum(..) = item.kind; + if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { + let lint = if let ItemKind::Enum(..) = item.kind { + EXHAUSTIVE_ENUMS + } else { + EXHAUSTIVE_STRUCTS + }; + if let Some(snippet) = snippet_opt(cx, item.span) { span_lint_and_sugg( cx, - EXHAUSTIVE_ENUMS, + lint, item.span, "enums should not be exhaustive", "try adding #[non_exhaustive]", @@ -56,7 +94,7 @@ impl LateLintPass<'_> for ExhaustiveItems { } else { span_lint_and_help( cx, - EXHAUSTIVE_ENUMS, + lint, item.span, "enums should not be exhaustive", None, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a5deebf7d5f..91c74026f64 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -613,6 +613,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS, &exhaustive_items::EXHAUSTIVE_ENUMS, + &exhaustive_items::EXHAUSTIVE_STRUCTS, &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, @@ -1250,6 +1251,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS), + LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS), LintId::of(&exit::EXIT), LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), LintId::of(&implicit_return::IMPLICIT_RETURN), diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 71c4a251e3b..bc146c6afc5 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -1,42 +1,72 @@ // run-rustfix -#![deny(clippy::exhaustive_enums)] +#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] #![allow(unused)] fn main() { // nop } -#[non_exhaustive] +pub mod enums { + #[non_exhaustive] pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} + // no warning, already non_exhaustive + #[non_exhaustive] + pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), + } + + // no warning, private + enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), + // no warning, private + #[non_exhaustive] + enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } } -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), +pub mod structs { + #[non_exhaustive] +pub struct Exhaustive { + foo: u8, + bar: String, + } + + // no warning, already non_exhaustive + #[non_exhaustive] + pub struct NonExhaustive { + foo: u8, + bar: String, + } + + // no warning, private + struct ExhaustivePrivate { + foo: u8, + bar: String, + } + + // no warning, private + #[non_exhaustive] + struct NonExhaustivePrivate { + foo: u8, + bar: String, + } } diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs index 45af6851dd1..ed86b50be30 100644 --- a/tests/ui/exhaustive_items.rs +++ b/tests/ui/exhaustive_items.rs @@ -1,41 +1,70 @@ // run-rustfix -#![deny(clippy::exhaustive_enums)] +#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] #![allow(unused)] fn main() { // nop } -pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} +pub mod enums { + pub enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} + // no warning, already non_exhaustive + #[non_exhaustive] + pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), + } + + // no warning, private + enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), + // no warning, private + #[non_exhaustive] + enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } } -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), +pub mod structs { + pub struct Exhaustive { + foo: u8, + bar: String, + } + + // no warning, already non_exhaustive + #[non_exhaustive] + pub struct NonExhaustive { + foo: u8, + bar: String, + } + + // no warning, private + struct ExhaustivePrivate { + foo: u8, + bar: String, + } + + // no warning, private + #[non_exhaustive] + struct NonExhaustivePrivate { + foo: u8, + bar: String, + } } diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index d00d950efc7..7e286e65949 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -1,28 +1,46 @@ error: enums should not be exhaustive - --> $DIR/exhaustive_items.rs:10:1 + --> $DIR/exhaustive_items.rs:11:5 | -LL | / pub enum Exhaustive { -LL | | Foo, -LL | | Bar, -LL | | Baz, -LL | | Quux(String), -LL | | } - | |_^ +LL | / pub enum Exhaustive { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_____^ | note: the lint level is defined here - --> $DIR/exhaustive_items.rs:3:9 + --> $DIR/exhaustive_items.rs:3:35 | -LL | #![deny(clippy::exhaustive_enums)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding #[non_exhaustive] | -LL | #[non_exhaustive] +LL | #[non_exhaustive] LL | pub enum Exhaustive { -LL | Foo, -LL | Bar, -LL | Baz, -LL | Quux(String), +LL | Foo, +LL | Bar, +LL | Baz, +LL | Quux(String), ... -error: aborting due to previous error +error: enums should not be exhaustive + --> $DIR/exhaustive_items.rs:46:5 + | +LL | / pub struct Exhaustive { +LL | | foo: u8, +LL | | bar: String, +LL | | } + | |_____^ + | +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | pub struct Exhaustive { +LL | foo: u8, +LL | bar: String, +LL | } + | + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 752274eabdd9be991c9a4e11850890301b697032 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 14:00:25 -0800 Subject: Fix indentation of suggestion --- clippy_lints/src/exhaustive_items.rs | 5 +++-- tests/ui/exhaustive_items.fixed | 4 ++-- tests/ui/exhaustive_items.stderr | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index d604ad0004f..bbcf9bfea27 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -82,13 +82,14 @@ impl LateLintPass<'_> for ExhaustiveItems { }; if let Some(snippet) = snippet_opt(cx, item.span) { + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_sugg( cx, lint, item.span, "enums should not be exhaustive", "try adding #[non_exhaustive]", - format!("#[non_exhaustive]\n{}", snippet), + format!("#[non_exhaustive]\n{}{}", indent, snippet), Applicability::MaybeIncorrect, ); } else { diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index bc146c6afc5..7e355d2a58b 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -9,7 +9,7 @@ fn main() { pub mod enums { #[non_exhaustive] -pub enum Exhaustive { + pub enum Exhaustive { Foo, Bar, Baz, @@ -45,7 +45,7 @@ pub enum Exhaustive { pub mod structs { #[non_exhaustive] -pub struct Exhaustive { + pub struct Exhaustive { foo: u8, bar: String, } diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index 7e286e65949..1716c8e2cb5 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -17,7 +17,7 @@ LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] help: try adding #[non_exhaustive] | LL | #[non_exhaustive] -LL | pub enum Exhaustive { +LL | pub enum Exhaustive { LL | Foo, LL | Bar, LL | Baz, @@ -36,7 +36,7 @@ LL | | } help: try adding #[non_exhaustive] | LL | #[non_exhaustive] -LL | pub struct Exhaustive { +LL | pub struct Exhaustive { LL | foo: u8, LL | bar: String, LL | } -- cgit 1.4.1-3-g733a5 From 65d003a1128e9234730a66c79065afdabb31afa0 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 22 Jan 2021 12:08:46 -0800 Subject: Clean up suggestion span; clarify help message --- clippy_lints/src/exhaustive_items.rs | 27 ++++++++++++++++----------- tests/ui/exhaustive_items.stderr | 22 ++++++++++------------ 2 files changed, 26 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index bbcf9bfea27..4749e36238c 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -75,29 +75,34 @@ impl LateLintPass<'_> for ExhaustiveItems { if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { - let lint = if let ItemKind::Enum(..) = item.kind { - EXHAUSTIVE_ENUMS + let (lint, msg) = if let ItemKind::Enum(..) = item.kind { + (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") } else { - EXHAUSTIVE_STRUCTS + (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") }; + let suggestion_span = item.span.until(item.ident.span); - if let Some(snippet) = snippet_opt(cx, item.span) { + if let Some(snippet) = snippet_opt(cx, suggestion_span) { let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); - span_lint_and_sugg( + span_lint_and_then( cx, lint, item.span, - "enums should not be exhaustive", - "try adding #[non_exhaustive]", - format!("#[non_exhaustive]\n{}{}", indent, snippet), - Applicability::MaybeIncorrect, + msg, + |diag| { + let sugg = format!("#[non_exhaustive]\n{}{}", indent, snippet); + diag.span_suggestion(suggestion_span, + "try adding #[non_exhaustive]", + sugg, + Applicability::MaybeIncorrect); + } ); } else { span_lint_and_help( cx, lint, item.span, - "enums should not be exhaustive", + msg, None, "try adding #[non_exhaustive]", ); diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index 1716c8e2cb5..a24e64b6705 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -1,4 +1,4 @@ -error: enums should not be exhaustive +error: exported enums should not be exhaustive --> $DIR/exhaustive_items.rs:11:5 | LL | / pub enum Exhaustive { @@ -10,21 +10,17 @@ LL | | } | |_____^ | note: the lint level is defined here - --> $DIR/exhaustive_items.rs:3:35 + --> $DIR/exhaustive_items.rs:3:9 | LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding #[non_exhaustive] | LL | #[non_exhaustive] LL | pub enum Exhaustive { -LL | Foo, -LL | Bar, -LL | Baz, -LL | Quux(String), - ... + | -error: enums should not be exhaustive +error: exported structs should not be exhaustive --> $DIR/exhaustive_items.rs:46:5 | LL | / pub struct Exhaustive { @@ -33,13 +29,15 @@ LL | | bar: String, LL | | } | |_____^ | +note: the lint level is defined here + --> $DIR/exhaustive_items.rs:3:35 + | +LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding #[non_exhaustive] | LL | #[non_exhaustive] LL | pub struct Exhaustive { -LL | foo: u8, -LL | bar: String, -LL | } | error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 66afdd1f4292e7fda6ea89113c0c8343e3321d99 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 21 Jan 2021 19:21:12 -0600 Subject: Enhance collapsible_match for adjusted bindings --- clippy_lints/src/collapsible_match.rs | 22 +++++++++++++++++--- tests/ui/collapsible_match2.rs | 29 ++++++++++++++++++++++++++ tests/ui/collapsible_match2.stderr | 38 ++++++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index c75aa2bde97..604ba102046 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -2,7 +2,7 @@ use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{span_lint_and_then, SpanlessEq}; use if_chain::if_chain; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -72,8 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) { if arms_inner.iter().all(|arm| arm.guard.is_none()); // match expression must be a local binding // match { .. } - if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind; - if let Res::Local(binding_id) = path.res; + if let Some(binding_id) = addr_adjusted_binding(expr_in, cx); // one of the branches must be "wild-like" if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); let (wild_inner_arm, non_wild_inner_arm) = @@ -175,3 +174,20 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { } false } + +/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`) +/// Returns `None` if a non-reference type is de-referenced. +/// For example, if `Vec` is de-referenced to a slice, `None` is returned. +fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option { + loop { + match expr.kind { + ExprKind::AddrOf(_, _, e) => expr = e, + ExprKind::Path(QPath::Resolved(None, path)) => match path.res { + Res::Local(binding_id) => break Some(binding_id), + _ => break None, + }, + ExprKind::Unary(UnOp::UnDeref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e, + _ => break None, + } + } +} diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index d571ac4ab69..8372a212477 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.rs @@ -40,6 +40,35 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> // there is still a better way to write this. mac!(res_opt => Ok(val), val => Some(n), foo(n)); } + + // deref reference value + match Some(&[1]) { + Some(s) => match *s { + [n] => foo(n), + _ => (), + }, + _ => (), + } + + // ref pattern and deref + match Some(&[1]) { + Some(ref s) => match &*s { + [n] => foo(n), + _ => (), + }, + _ => (), + } +} + +fn no_lint() { + // deref inner value (cannot pattern match with Vec) + match Some(vec![1]) { + Some(s) => match *s { + [n] => foo(n), + _ => (), + }, + _ => (), + } } fn make() -> T { diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 490d82d12cd..b2eb457d173 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -57,5 +57,41 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | Replace this binding = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:46:20 + | +LL | Some(s) => match *s { + | ____________________^ +LL | | [n] => foo(n), +LL | | _ => (), +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:46:14 + | +LL | Some(s) => match *s { + | ^ Replace this binding +LL | [n] => foo(n), + | ^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:55:24 + | +LL | Some(ref s) => match &*s { + | ________________________^ +LL | | [n] => foo(n), +LL | | _ => (), +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:55:14 + | +LL | Some(ref s) => match &*s { + | ^^^^^ Replace this binding +LL | [n] => foo(n), + | ^^^ with this pattern + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 50abde20c901cf815480f0b494234025c315cc7f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 21 Jan 2021 19:25:22 -0600 Subject: Fix dogfood --- clippy_lints/src/main_recursion.rs | 3 +-- clippy_lints/src/ptr.rs | 3 +-- clippy_lints/src/utils/higher.rs | 3 +-- clippy_lints/src/utils/mod.rs | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index eceae706e4f..1ed3f3de839 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -43,8 +43,7 @@ impl LateLintPass<'_> for MainRecursion { if_chain! { if let ExprKind::Call(func, _) = &expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let QPath::Resolved(_, path) = &path; + if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind; if let Some(def_id) = path.res.opt_def_id(); if is_entrypoint_fn(cx, def_id); then { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c6329a1381c..b5aa3411140 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -263,8 +263,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } else if match_type(cx, ty, &paths::COW) { if_chain! { if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; - if let TyKind::Path(ref path) = ty.kind; - if let QPath::Resolved(None, ref pp) = *path; + if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind; if let [ref bx] = *pp.segments; if let Some(ref params) = bx.args; if !params.parenthesized; diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 9b3585865da..bb71d95a7e2 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -158,8 +158,7 @@ pub fn for_loop<'tcx>( /// `while cond { body }` becomes `(cond, body)`. pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { if_chain! { - if let hir::ExprKind::Loop(block, _, hir::LoopSource::While) = &expr.kind; - if let hir::Block { expr: Some(expr), .. } = &**block; + if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While) = &expr.kind; if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind; if let hir::ExprKind::DropTemps(cond) = &cond.kind; if let [hir::Arm { body, .. }, ..] = &arms[..]; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8698a618f90..991fae6b1aa 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1126,8 +1126,7 @@ pub fn is_self(slf: &Param<'_>) -> bool { pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { if_chain! { - if let TyKind::Path(ref qp) = slf.kind; - if let QPath::Resolved(None, ref path) = *qp; + if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind; if let Res::SelfTy(..) = path.res; then { return true -- cgit 1.4.1-3-g733a5 From e0ae980fab38e61bf39380e69bbf0000e681a9f8 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 25 Jan 2021 14:35:57 -0800 Subject: Better suggestion span --- clippy_lints/src/exhaustive_items.rs | 43 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 27 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 4749e36238c..32b1299efce 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_then}; +use crate::utils::{indent_of, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -80,33 +80,22 @@ impl LateLintPass<'_> for ExhaustiveItems { } else { (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") }; - let suggestion_span = item.span.until(item.ident.span); + let suggestion_span = item.span.shrink_to_lo(); + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); + span_lint_and_then( + cx, + lint, + item.span, + msg, + |diag| { + let sugg = format!("#[non_exhaustive]\n{}", indent); + diag.span_suggestion(suggestion_span, + "try adding #[non_exhaustive]", + sugg, + Applicability::MaybeIncorrect); + } + ); - if let Some(snippet) = snippet_opt(cx, suggestion_span) { - let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); - span_lint_and_then( - cx, - lint, - item.span, - msg, - |diag| { - let sugg = format!("#[non_exhaustive]\n{}{}", indent, snippet); - diag.span_suggestion(suggestion_span, - "try adding #[non_exhaustive]", - sugg, - Applicability::MaybeIncorrect); - } - ); - } else { - span_lint_and_help( - cx, - lint, - item.span, - msg, - None, - "try adding #[non_exhaustive]", - ); - } } } } -- cgit 1.4.1-3-g733a5 From 7f1595e18f542f2caf209a05dd090b4a2ebd3b8a Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Fri, 29 Jan 2021 22:17:18 -0800 Subject: Fix let_and_return false positive The issue: See this Rust playground link: https://play.rust-lang.org/?edition=2018&gist=12cb5d1e7527f8c37743b87fc4a53748 Run the above with clippy to see the following warning: ``` warning: returning the result of a `let` binding from a block --> src/main.rs:24:5 | 23 | let value = Foo::new(&x).value(); | --------------------------------- unnecessary `let` binding 24 | value | ^^^^^ | = note: `#[warn(clippy::let_and_return)]` on by default = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return help: return the expression directly | 23 | 24 | Foo::new(&x).value() | ``` Implementing the suggested fix, removing the temporary let binding, yields a compiler error: ``` error[E0597]: `x` does not live long enough --> src/main.rs:23:14 | 23 | Foo::new(&x).value() | ---------^^- | | | | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... 24 | } | - | | | `x` dropped here while still borrowed | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `Foo` | = note: the temporary is part of an expression at the end of a block; consider forcing this temporary to be dropped sooner, before the block's local variables are dropped help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block | 23 | let x = Foo::new(&x).value(); x | ^^^^^^^ ^^^ ``` The fix: Of course, clippy looks like it should already handle this edge case; however, it appears `utils::fn_def_id` is not returning a `DefId` for `Foo::new`. Changing the `qpath_res` lookup to use the child Path `hir_id` instead of the parent Call `hir_id` fixes the issue. --- clippy_lints/src/utils/mod.rs | 3 ++- tests/ui/let_and_return.rs | 12 +++++++++++- tests/ui/let_and_return.stderr | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 991fae6b1aa..caad7c88bcf 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1546,10 +1546,11 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { ExprKind::Call( Expr { kind: ExprKind::Path(qpath), + hir_id: path_hir_id, .. }, .., - ) => cx.typeck_results().qpath_res(qpath, expr.hir_id).opt_def_id(), + ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(), _ => None, } } diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 73e550b3df8..e3561863c1e 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -117,7 +117,11 @@ mod no_lint_if_stmt_borrows { fn drop(&mut self) {} } - impl Foo<'_> { + impl<'a> Foo<'a> { + fn new(inner: &'a Inner) -> Self { + Self { inner } + } + fn value(&self) -> i32 { 42 } @@ -132,6 +136,12 @@ mod no_lint_if_stmt_borrows { let value = some_foo(&x).value(); value } + + fn test2() -> i32 { + let x = Inner {}; + let value = Foo::new(&x).value(); + value + } } } diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index fe878e5f206..a6941dabeb8 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -28,7 +28,7 @@ LL | 5 | error: returning the result of a `let` binding from a block - --> $DIR/let_and_return.rs:154:13 + --> $DIR/let_and_return.rs:164:13 | LL | let clone = Arc::clone(&self.foo); | ---------------------------------- unnecessary `let` binding -- cgit 1.4.1-3-g733a5 From 797cf6554dd48792ed22c4f81322979548d542d1 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 30 Jan 2021 17:43:00 +0100 Subject: Get rid of regex and lazy_static dependencies --- clippy_lints/Cargo.toml | 2 -- clippy_lints/src/case_sensitive_file_extension_comparisons.rs | 10 ++++------ 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 38098f8a14c..a9516560a61 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -34,8 +34,6 @@ rustc-semver="1.1.0" url = { version = "2.1.0", features = ["serde"] } quote = "1" syn = { version = "1", features = ["full"] } -regex = "1.4" -lazy_static = "1.4" [features] deny-warnings = [] diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index d5347ce6ed7..6969ac949d8 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -1,8 +1,6 @@ use crate::utils::paths::STRING; use crate::utils::{match_def_path, span_lint_and_help}; use if_chain::if_chain; -use lazy_static::lazy_static; -use regex::Regex; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; @@ -41,14 +39,14 @@ declare_clippy_lint! { declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]); fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - lazy_static! { - static ref RE: Regex = Regex::new(r"^\.([a-z0-9]{1,5}|[A-Z0-9]{1,5})$").unwrap(); - } if_chain! { if let ExprKind::MethodCall(PathSegment { ident, .. }, _, [obj, extension, ..], span) = expr.kind; if ident.as_str() == "ends_with"; if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; - if RE.is_match(&ext_literal.as_str()); + if (2..=6).contains(&ext_literal.as_str().len()); + if ext_literal.as_str().starts_with('.'); + if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10)) + || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10)); then { let mut ty = ctx.typeck_results().expr_ty(obj); ty = match ty.kind() { -- cgit 1.4.1-3-g733a5 From 3874631600811d964f18784859b34302d5533358 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 30 Jan 2021 17:43:20 +0100 Subject: Remove unknown_clippy_lints allow attribute --- clippy_lints/src/utils/diagnostics.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index a7a6b5855b7..6caa04f651f 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -186,8 +186,6 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` - -#[allow(clippy::unknown_clippy_lints)] #[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<'a, T: LintContext>( cx: &'a T, -- cgit 1.4.1-3-g733a5 From 939136d1bc512e4acb3a91bbec2d5f7db311d4d8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 25 Jan 2021 13:39:35 -0600 Subject: Remove Option from path_to_res return type --- clippy_lints/src/utils/internal_lints.rs | 6 +++--- clippy_lints/src/utils/mod.rs | 28 +++++++++++++++------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 822863ca3e2..cccad243e1b 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -760,7 +760,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { // 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()); + if let Some(ty_did) = path_to_res(cx, &segments[..]).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 }); @@ -833,7 +833,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, path: &[&str]) -> bool { - if path_to_res(cx, path).is_some() { + if path_to_res(cx, path) != Res::Err { return true; } @@ -906,7 +906,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) { + if let Some(def_id) = path_to_res(cx, module).opt_def_id() { for item in cx.tcx.item_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d0db3a67533..1c58512d4b5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -309,7 +309,15 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { /// Gets the definition associated to a path. #[allow(clippy::shadow_unrelated)] // false positive #6563 -pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { +pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { + macro_rules! try_res { + ($e:expr) => { + match $e { + Some(e) => e, + None => return Res::Err, + } + }; + } fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { tcx.item_children(def_id) .iter() @@ -318,12 +326,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { let (krate, first, path) = match *path { [krate, first, ref path @ ..] => (krate, first, path), - _ => return None, + _ => return Res::Err, }; let tcx = cx.tcx; let crates = tcx.crates(); - let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?; - let first = item_child_by_name(tcx, krate.as_def_id(), first)?; + let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)); + let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first)); let last = path .iter() .copied() @@ -343,21 +351,15 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { } else { None } - })?; - Some(last.res) + }); + try_res!(last).res } /// Convenience function to get the `DefId` of a trait by path. /// It could be a trait or trait alias. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { - let res = match path_to_res(cx, path) { - Some(res) => res, - None => return None, - }; - - match res { + match path_to_res(cx, path) { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), - Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path), _ => None, } } -- cgit 1.4.1-3-g733a5 From 5753614152b4c6d9c0d20bc311a335c4746c3ed0 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 27 Jan 2021 09:34:59 +0100 Subject: Draft skeleton for new lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 +++ clippy_lints/src/loops.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index dadb6832d1f..e8e738313d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1969,6 +1969,7 @@ Released 2018-09-13 [`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_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles +[`for_loops_over_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 54007c29c6c..37932087355 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,6 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, &loops::FOR_LOOPS_OVER_FALLIBLES, + &loops::FOR_LOOPS_OVER_OPTIONS, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -1488,6 +1489,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1820,6 +1822,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), + LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5211ca7da32..c1a59650cb0 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -494,6 +494,37 @@ declare_clippy_lint! { "there is no reason to have a single element loop" } +declare_clippy_lint! { + /// **What it does:** Checks for iteration of `Option`s with + /// a single `if let Some()` expression inside. + /// + /// **Why is this bad?** It is verbose and can be simplified + /// by first calling the `flatten` method on the `Iterator`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x = vec![Some(1), Some(2), Some(3)]; + /// for n in x { + /// if let Some(n) = n { + /// println!("{}", n); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// let x = vec![Some(1), Some(2), Some(3)]; + /// for n in x.iter().flatten() { + /// println!("{}", n); + /// } + /// ``` + pub FOR_LOOPS_OVER_OPTIONS, + complexity, + "for loops over `Option`s or `Result`s with a single expression can be simplified" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -501,6 +532,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, FOR_LOOPS_OVER_FALLIBLES, + FOR_LOOPS_OVER_OPTIONS, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -830,6 +862,7 @@ fn check_for_loop<'tcx>( 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); + check_for_loop_over_options_or_results(cx, pat, arg, body, expr); } // this function assumes the given expression is a `for` loop. @@ -1953,6 +1986,37 @@ fn check_for_single_element_loop<'tcx>( } } +/// Check if a for loop loops over `Option`s or `Result`s and contains only +/// a `if let Some` or `if let Ok` expression. +fn check_for_loop_over_options_or_results<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if_chain! { + if let ExprKind::Block(ref block, _) = body.kind; + if block.stmts.is_empty(); + if let Some(inner_expr) = block.expr; + if let ExprKind::Match(ref _match_expr, ref _match_arms, MatchSource::IfLetDesugar{ contains_else_clause }) = inner_expr.kind; + if !contains_else_clause; + then { + // println!("if_let_expr:\n{:?}", snippet(cx, if_let_expr.span, "..")); + // println!("pat is:\n {:?}", snippet(cx, pat.span, "..")); + // println!("arg is:\n {:?}", snippet(cx, arg.span, "..")); + // println!("body is:\n {:?}", snippet(cx, body.span, "..")); + // println!("arg kind is: {:?}", arg.kind); + // println!("expr is:\n {:?}", snippet(cx, expr.span, "..")); + // todo!(); + let arg_snippet = snippet(cx, arg.span, ".."); + let msg = "looping over `Option`s or `Result`s with an `if let` expression."; + let hint = format!("try turn {} into an `Iterator` and use `flatten`: `{}.iter().flatten()`", arg_snippet, arg_snippet); + span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS, expr.span, msg, None, &hint); + } + } +} + struct MutatePairDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, hir_id_low: Option, -- cgit 1.4.1-3-g733a5 From 3da25ed955baffe8c14cee4950762d268f1b48e7 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 27 Jan 2021 09:49:53 +0100 Subject: Rename lint --- CHANGELOG.md | 3 ++- clippy_lints/src/lib.rs | 6 +++--- clippy_lints/src/loops.rs | 6 +++--- tests/ui/for_loops_over_options.rs | 31 --------------------------- tests/ui/for_loops_over_options_or_results.rs | 31 +++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 38 deletions(-) delete mode 100644 tests/ui/for_loops_over_options.rs create mode 100644 tests/ui/for_loops_over_options_or_results.rs (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e738313d4..4bd04ffbd99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1969,7 +1969,8 @@ Released 2018-09-13 [`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_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles -[`for_loops_over_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options +[`for_loops_over_options_or_results`]: +https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options_or_results [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 37932087355..dae6c93c7cb 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: &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, &loops::FOR_LOOPS_OVER_FALLIBLES, - &loops::FOR_LOOPS_OVER_OPTIONS, + &loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -1489,7 +1489,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS), + LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1822,7 +1822,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS), + LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c1a59650cb0..e9047cce15f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -520,7 +520,7 @@ declare_clippy_lint! { /// println!("{}", n); /// } /// ``` - pub FOR_LOOPS_OVER_OPTIONS, + pub FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, complexity, "for loops over `Option`s or `Result`s with a single expression can be simplified" } @@ -532,7 +532,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, FOR_LOOPS_OVER_FALLIBLES, - FOR_LOOPS_OVER_OPTIONS, + FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -2012,7 +2012,7 @@ fn check_for_loop_over_options_or_results<'tcx>( let arg_snippet = snippet(cx, arg.span, ".."); let msg = "looping over `Option`s or `Result`s with an `if let` expression."; let hint = format!("try turn {} into an `Iterator` and use `flatten`: `{}.iter().flatten()`", arg_snippet, arg_snippet); - span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS, expr.span, msg, None, &hint); + span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, expr.span, msg, None, &hint); } } } diff --git a/tests/ui/for_loops_over_options.rs b/tests/ui/for_loops_over_options.rs deleted file mode 100644 index d8144864a21..00000000000 --- a/tests/ui/for_loops_over_options.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![warn(clippy::for_loops_over_options)] - -fn main() { - let x = vec![Some(1), Some(2), Some(3)]; - for n in x { - if let Some(n) = n { - println!("{}", n); - } - } - - let y: Vec> = vec![]; - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } - } - - // This should not trigger the lint - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } else { - println!("Oops!"); - } - } - - // This should not trigger the lint - for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { - println!("{}", n); - } -} diff --git a/tests/ui/for_loops_over_options_or_results.rs b/tests/ui/for_loops_over_options_or_results.rs new file mode 100644 index 00000000000..02e24b250f7 --- /dev/null +++ b/tests/ui/for_loops_over_options_or_results.rs @@ -0,0 +1,31 @@ +#![warn(clippy::for_loops_over_options_or_results)] + +fn main() { + let x = vec![Some(1), Some(2), Some(3)]; + for n in x { + if let Some(n) = n { + println!("{}", n); + } + } + + let y: Vec> = vec![]; + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } + } + + // This should not trigger the lint + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } else { + println!("Oops!"); + } + } + + // This should not trigger the lint + for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { + println!("{}", n); + } +} -- cgit 1.4.1-3-g733a5 From b87e189694eebb5b83d758528032cf4d4db81472 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Fri, 29 Jan 2021 01:38:34 +0100 Subject: Implement manual flatten lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/loops.rs | 85 ++++++++++++++++++--------- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/needless_question_mark.rs | 26 +------- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/utils/higher.rs | 9 +-- clippy_lints/src/utils/internal_lints.rs | 16 +++-- clippy_lints/src/utils/mod.rs | 28 ++++++++- clippy_lints/src/vec.rs | 2 +- tests/ui/for_loops_over_options_or_results.rs | 31 ---------- tests/ui/manual_flatten.rs | 54 +++++++++++++++++ tests/ui/manual_flatten.stderr | 51 ++++++++++++++++ 13 files changed, 208 insertions(+), 106 deletions(-) delete mode 100644 tests/ui/for_loops_over_options_or_results.rs create mode 100644 tests/ui/manual_flatten.rs create mode 100644 tests/ui/manual_flatten.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e40cd8174fc..aceabcbbdfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1969,7 +1969,6 @@ Released 2018-09-13 [`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_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles -[`for_loops_over_options_or_results`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options_or_results [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect @@ -2040,6 +2039,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dae6c93c7cb..cd0a95a4585 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,8 +685,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, &loops::FOR_LOOPS_OVER_FALLIBLES, - &loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, &loops::ITER_NEXT_LOOP, + &loops::MANUAL_FLATTEN, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, &loops::NEEDLESS_COLLECT, @@ -1489,8 +1489,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), LintId::of(&loops::ITER_NEXT_LOOP), + LintId::of(&loops::MANUAL_FLATTEN), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::NEEDLESS_COLLECT), @@ -1822,7 +1822,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), + LintId::of(&loops::MANUAL_FLATTEN), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index e9047cce15f..db5aec82e90 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,10 +5,10 @@ use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, - last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path, snippet, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, sugg, SpanlessEq, + indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor, is_refutable, is_some_ctor, + is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, + 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; @@ -495,8 +495,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for iteration of `Option`s with - /// a single `if let Some()` expression inside. + /// **What it does:** Check for unnecessary `if let` usage in a for loop + /// where only the `Some` or `Ok` variant of the iterator element is used. /// /// **Why is this bad?** It is verbose and can be simplified /// by first calling the `flatten` method on the `Iterator`. @@ -516,23 +516,23 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// let x = vec![Some(1), Some(2), Some(3)]; - /// for n in x.iter().flatten() { + /// for n in x.into_iter().flatten() { /// println!("{}", n); /// } /// ``` - pub FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, + pub MANUAL_FLATTEN, complexity, "for loops over `Option`s or `Result`s with a single expression can be simplified" } declare_lint_pass!(Loops => [ MANUAL_MEMCPY, + MANUAL_FLATTEN, NEEDLESS_RANGE_LOOP, EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, FOR_LOOPS_OVER_FALLIBLES, - FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -549,14 +549,14 @@ declare_lint_pass!(Loops => [ impl<'tcx> LateLintPass<'tcx> for Loops { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((pat, arg, body)) = higher::for_loop(expr) { + if let Some((pat, arg, body, span)) = higher::for_loop(expr) { // we don't want to check expanded macros // this check is not at the top of the function // since higher::for_loop expressions are marked as expansions if body.span.from_expansion() { return; } - check_for_loop(cx, pat, arg, body, expr); + check_for_loop(cx, pat, arg, body, expr, span); } // we don't want to check expanded macros @@ -851,6 +851,7 @@ fn check_for_loop<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, + span: Span, ) { let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { @@ -862,7 +863,7 @@ fn check_for_loop<'tcx>( 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); - check_for_loop_over_options_or_results(cx, pat, arg, body, expr); + check_manual_flatten(cx, pat, arg, body, span); } // this function assumes the given expression is a `for` loop. @@ -1986,33 +1987,61 @@ fn check_for_single_element_loop<'tcx>( } } -/// Check if a for loop loops over `Option`s or `Result`s and contains only -/// a `if let Some` or `if let Ok` expression. -fn check_for_loop_over_options_or_results<'tcx>( +/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the +/// iterator element is used. +fn check_manual_flatten<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, + span: Span, ) { if_chain! { + // Ensure the `if let` statement is the only expression in the for-loop if let ExprKind::Block(ref block, _) = body.kind; if block.stmts.is_empty(); if let Some(inner_expr) = block.expr; - if let ExprKind::Match(ref _match_expr, ref _match_arms, MatchSource::IfLetDesugar{ contains_else_clause }) = inner_expr.kind; - if !contains_else_clause; + if let ExprKind::Match( + ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + ) = inner_expr.kind; + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; + if let Res::Local(match_expr_path_id) = match_expr_path.res; + if pat_hir_id == match_expr_path_id; + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + let if_let_type = if is_some_ctor(cx, path.res) { + "Some" + } else { + "Ok" + }; + // Determine if `arg` is `Iterator` or implicitly calls `into_iter` + let arg_ty = cx.typeck_results().expr_ty(arg); + if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR); + if let is_iterator = implements_trait(cx, arg_ty, id, &[]); + then { - // println!("if_let_expr:\n{:?}", snippet(cx, if_let_expr.span, "..")); - // println!("pat is:\n {:?}", snippet(cx, pat.span, "..")); - // println!("arg is:\n {:?}", snippet(cx, arg.span, "..")); - // println!("body is:\n {:?}", snippet(cx, body.span, "..")); - // println!("arg kind is: {:?}", arg.kind); - // println!("expr is:\n {:?}", snippet(cx, expr.span, "..")); - // todo!(); + // Prepare the error message + let msg = format!("Unnecessary `if let` since only the `{}` variant of the iterator element is used.", if_let_type); + + // Prepare the help message let arg_snippet = snippet(cx, arg.span, ".."); - let msg = "looping over `Option`s or `Result`s with an `if let` expression."; - let hint = format!("try turn {} into an `Iterator` and use `flatten`: `{}.iter().flatten()`", arg_snippet, arg_snippet); - span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, expr.span, msg, None, &hint); + let hint = if is_iterator { + format!("try: `{}.flatten()`", arg_snippet) + } else { + format!("try: `{}.into_iter().flatten()`", arg_snippet) + }; + + span_lint_and_help( + cx, + MANUAL_FLATTEN, + span, + &msg, + Some(arg.span), + &hint, + ); } } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 2f3cdb894f0..d7239b328bb 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { return; } - if let Some((_, arg, body)) = higher::for_loop(expr) { + if let Some((_, arg, body, _)) = higher::for_loop(expr) { // A `for` loop lowers to: // ```rust // match ::std::iter::Iterator::next(&mut iter) { diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 9e9b79ee1cf..fe8d4d07abc 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,8 +1,6 @@ use rustc_errors::Applicability; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -160,7 +158,7 @@ fn is_some_or_ok_call<'a>( // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; @@ -208,25 +206,3 @@ fn is_some_or_ok_call<'a>( fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); } - -fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == ok_id; - } - } - } - false -} - -fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == some_id; - } - } - } - false -} diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 3e454eecd97..59503817c0f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -442,7 +442,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { 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, + Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true, _ => cur_expr = parent_expr, } } diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 340d340d6d3..df7f0f95782 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -9,6 +9,7 @@ use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; +use rustc_span::source_map::Span; /// Converts a hir binary operator to the corresponding `ast` type. #[must_use] @@ -133,11 +134,11 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { false } -/// Recover the essential nodes of a desugared for loop: -/// `for pat in arg { body }` becomes `(pat, arg, body)`. +/// Recover the essential nodes of a desugared for loop as well as the entire span: +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. pub fn for_loop<'tcx>( expr: &'tcx hir::Expr<'tcx>, -) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { +) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> { if_chain! { if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; @@ -148,7 +149,7 @@ pub fn for_loop<'tcx>( if let hir::StmtKind::Local(ref local) = let_stmt.kind; if let hir::StmtKind::Expr(ref expr) = body.kind; then { - return Some((&*local.pat, &iterargs[0], expr)); + return Some((&*local.pat, &iterargs[0], expr, arms[0].span)); } } None diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 822863ca3e2..b3eae933062 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -841,15 +841,13 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { // 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; - } + for item_def_id in lang_items.items().iter().flatten() { + let lang_item_path = cx.get_def_path(*item_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(*item_def_id) { + if child.ident.name == *item { + return true; } } } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d0db3a67533..3390c71dd8e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -37,7 +37,7 @@ use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; @@ -50,7 +50,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; 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_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -1700,6 +1700,30 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } +/// Check if the resolution of a given path is an `Ok` variant of `Result`. +pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +/// Check if the resolution of a given path is a `Some` variant of `Option`. +pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 149cceb39dd..c132e4de4f6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { // search for `for _ in vec![…]` if_chain! { - if let Some((_, arg, _)) = higher::for_loop(expr); + if let Some((_, arg, _, _)) = higher::for_loop(expr); if let Some(vec_args) = higher::vec_macro(cx, arg); if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg))); then { diff --git a/tests/ui/for_loops_over_options_or_results.rs b/tests/ui/for_loops_over_options_or_results.rs deleted file mode 100644 index 02e24b250f7..00000000000 --- a/tests/ui/for_loops_over_options_or_results.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![warn(clippy::for_loops_over_options_or_results)] - -fn main() { - let x = vec![Some(1), Some(2), Some(3)]; - for n in x { - if let Some(n) = n { - println!("{}", n); - } - } - - let y: Vec> = vec![]; - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } - } - - // This should not trigger the lint - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } else { - println!("Oops!"); - } - } - - // This should not trigger the lint - for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { - println!("{}", n); - } -} diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs new file mode 100644 index 00000000000..f183ceecdd8 --- /dev/null +++ b/tests/ui/manual_flatten.rs @@ -0,0 +1,54 @@ +#![warn(clippy::manual_flatten)] + +fn main() { + let x = vec![Some(1), Some(2), Some(3)]; + for n in x { + if let Some(n) = n { + println!("{}", n); + } + } + + let y: Vec> = vec![]; + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } + } + + let z = vec![Some(1), Some(2), Some(3)]; + let z = z.iter(); + for n in z { + if let Some(n) = n { + println!("{}", n); + } + } + + // Using the `None` variant should not trigger the lint + let z = vec![Some(1), Some(2), Some(3)]; + for n in z { + if n.is_none() { + println!("Nada."); + } + } + + // Using the `Err` variant should not trigger the lint + for n in y.clone() { + if let Err(e) = n { + println!("Oops: {}!", e); + } + } + + // Having an else clause should not trigger the lint + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } else { + println!("Oops!"); + } + } + + // Using manual flatten should not trigger the lint + for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { + println!("{}", n); + } +} diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr new file mode 100644 index 00000000000..cf99a2d9ab1 --- /dev/null +++ b/tests/ui/manual_flatten.stderr @@ -0,0 +1,51 @@ +error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. + --> $DIR/manual_flatten.rs:5:5 + | +LL | / for n in x { +LL | | if let Some(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::manual-flatten` implied by `-D warnings` +help: try: `x.into_iter().flatten()` + --> $DIR/manual_flatten.rs:5:14 + | +LL | for n in x { + | ^ + +error: Unnecessary `if let` since only the `Ok` variant of the iterator element is used. + --> $DIR/manual_flatten.rs:12:5 + | +LL | / for n in y.clone() { +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | +help: try: `y.clone().into_iter().flatten()` + --> $DIR/manual_flatten.rs:12:14 + | +LL | for n in y.clone() { + | ^^^^^^^^^ + +error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. + --> $DIR/manual_flatten.rs:20:5 + | +LL | / for n in z { +LL | | if let Some(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | +help: try: `z.flatten()` + --> $DIR/manual_flatten.rs:20:14 + | +LL | for n in z { + | ^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 7825bf36d8dfee0099a23eb74451783da3ce261f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 Jan 2021 21:43:35 +0100 Subject: Fix suggestions that need parens --- clippy_lints/src/methods/mod.rs | 9 +++++++-- tests/ui/from_iter_instead_of_collect.rs | 8 +++++++- tests/ui/from_iter_instead_of_collect.stderr | 14 +++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7a459a440ca..f53b2f67d1d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4094,14 +4094,19 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); then { // `expr` implements `FromIterator` trait - let iter_expr = snippet(cx, args[0].span, ".."); + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let sugg = if higher::range(&args[0]).is_some() { + format!("{}.collect::<{}>()", iter_expr, ty) + } else { + format!("{}.collect()", iter_expr) + }; span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", - format!("{}.collect()", iter_expr), + sugg, Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 045eb3133d3..6c81366c4df 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::iter::FromIterator; fn main() { @@ -10,4 +10,10 @@ fn main() { HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); Vec::from_iter(vec![42u32]); + + let a = vec![0, 1, 2]; + assert_eq!(a, Vec::from_iter(0..3)); + + let mut b = VecDeque::from_iter(0..3); + b.push_back(4); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 46bdc2f4e19..e2161dd3b57 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -12,5 +12,17 @@ error: usage of `FromIterator::from_iter` LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()` -error: aborting due to 2 previous errors +error: usage of `FromIterator::from_iter` + --> $DIR/from_iter_instead_of_collect.rs:15:19 + | +LL | assert_eq!(a, Vec::from_iter(0..3)); + | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` + +error: usage of `FromIterator::from_iter` + --> $DIR/from_iter_instead_of_collect.rs:17:17 + | +LL | let mut b = VecDeque::from_iter(0..3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 0fb09d6b218b005b90fe5b3f3d5346a2b707e19e Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 1 Feb 2021 18:38:43 -0800 Subject: exhaustive_structs: don't trigger for structs with private fields --- clippy_lints/src/exhaustive_items.rs | 10 +++++++--- tests/ui/exhaustive_items.fixed | 25 +++++++++++++++++-------- tests/ui/exhaustive_items.rs | 23 ++++++++++++++++------- tests/ui/exhaustive_items.stderr | 4 ++-- 4 files changed, 42 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 32b1299efce..e3988d0038c 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -75,10 +75,14 @@ impl LateLintPass<'_> for ExhaustiveItems { if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { - let (lint, msg) = if let ItemKind::Enum(..) = item.kind { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - } else { + let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { + if v.fields().iter().any(|f| !f.vis.node.is_pub()) { + // skip structs with private fields + return; + } (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") + } else { + (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") }; let suggestion_span = item.span.shrink_to_lo(); let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 8174a0175ab..383b3d85a66 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -56,27 +56,36 @@ pub mod enums { pub mod structs { #[non_exhaustive] pub struct Exhaustive { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, already non_exhaustive #[non_exhaustive] pub struct NonExhaustive { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, + } + + // no warning, private fields + pub struct ExhaustivePrivateFieldTuple(u8); + + // no warning, private fields + pub struct ExhaustivePrivateField { + pub foo: u8, + bar: String } // no warning, private struct ExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, private #[non_exhaustive] struct NonExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } } diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs index b476f09f8a0..6f59dbf2da5 100644 --- a/tests/ui/exhaustive_items.rs +++ b/tests/ui/exhaustive_items.rs @@ -53,27 +53,36 @@ pub mod enums { pub mod structs { pub struct Exhaustive { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, already non_exhaustive #[non_exhaustive] pub struct NonExhaustive { - foo: u8, + pub foo: u8, + pub bar: String, + } + + // no warning, private fields + pub struct ExhaustivePrivateFieldTuple(u8); + + // no warning, private fields + pub struct ExhaustivePrivateField { + pub foo: u8, bar: String, } // no warning, private struct ExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, private #[non_exhaustive] struct NonExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } } diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index 7369fe75a4f..8fbab535a9b 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -41,8 +41,8 @@ error: exported structs should not be exhaustive --> $DIR/exhaustive_items.rs:55:5 | LL | / pub struct Exhaustive { -LL | | foo: u8, -LL | | bar: String, +LL | | pub foo: u8, +LL | | pub bar: String, LL | | } | |_____^ | -- cgit 1.4.1-3-g733a5 From bde667af7e7d512978daff3bc2b540bb913bd6a1 Mon Sep 17 00:00:00 2001 From: Caden Haustein Date: Wed, 30 Dec 2020 16:37:59 -0600 Subject: Add missing_panics_doc lint --- CHANGELOG.md | 1 + clippy_dev/src/bless.rs | 3 + clippy_dev/src/lib.rs | 13 +++ clippy_dev/src/ra_setup.rs | 3 + clippy_dev/src/serve.rs | 3 + clippy_lints/src/doc.rs | 125 +++++++++++++++++++++++- clippy_lints/src/lib.rs | 2 + clippy_lints/src/utils/diagnostics.rs | 2 +- mini-macro/src/lib.rs | 3 + tests/ui/doc_panics.rs | 95 ++++++++++++++++++ tests/ui/doc_panics.stderr | 67 +++++++++++++ tests/ui/should_impl_trait/corner_cases.rs | 3 +- tests/ui/should_impl_trait/method_list_1.rs | 3 +- tests/ui/should_impl_trait/method_list_1.stderr | 28 +++--- tests/ui/should_impl_trait/method_list_2.rs | 3 +- tests/ui/should_impl_trait/method_list_2.stderr | 30 +++--- 16 files changed, 346 insertions(+), 38 deletions(-) create mode 100644 tests/ui/doc_panics.rs create mode 100644 tests/ui/doc_panics.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index dadb6832d1f..c1032204a22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2079,6 +2079,7 @@ Released 2018-09-13 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index b877806946c..2a869e9d449 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -24,6 +24,9 @@ static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::ne fs::metadata(path).ok()?.modified().ok() }); +/// # Panics +/// +/// Panics if the path to a test file is broken pub fn bless(ignore_timestamp: bool) { let test_suite_dirs = [ clippy_project_root().join("tests").join("ui"), diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 24d70d433f3..01d1fc9211a 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -236,6 +236,10 @@ pub struct FileChange { /// `path` is the relative path to the file on which you want to perform the replacement. /// /// See `replace_region_in_text` for documentation of the other options. +/// +/// # Panics +/// +/// Panics if the path could not read or then written pub fn replace_region_in_file( path: &Path, start: &str, @@ -283,6 +287,10 @@ where /// .new_lines; /// assert_eq!("replace_start\na different\ntext\nreplace_end", result); /// ``` +/// +/// # Panics +/// +/// Panics if start or end is not valid regex pub fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange where F: FnOnce() -> Vec, @@ -329,6 +337,11 @@ where } /// Returns the path to the Clippy project directory +/// +/// # Panics +/// +/// Panics if the current directory could not be retrieved, there was an error reading any of the +/// Cargo.toml files or ancestor directory is the clippy root directory #[must_use] pub fn clippy_project_root() -> PathBuf { let current_dir = std::env::current_dir().unwrap(); diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index a8a6a2cb1bd..a3c329b578b 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -8,6 +8,9 @@ use std::path::{Path, PathBuf}; // 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 +/// # Panics +/// +/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read pub fn run(rustc_path: Option<&str>) { // we can unwrap here because the arg is required by clap let rustc_path = PathBuf::from(rustc_path.unwrap()); diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index a46c0e4d3f0..faa94859601 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -4,6 +4,9 @@ use std::process::Command; use std::thread; use std::time::{Duration, SystemTime}; +/// # Panics +/// +/// Panics if the python commands could not be spawned pub fn run(port: u16, lint: Option<&str>) -> ! { let mut url = Some(match lint { None => format!("http://localhost:{}", port), diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 3a754f49917..b3e3635c1c1 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,4 +1,7 @@ -use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; +use crate::utils::{ + implements_trait, is_entrypoint_fn, is_type_diagnostic_item, match_panic_def_id, method_chain_args, return_ty, + span_lint, span_lint_and_note, +}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; @@ -8,7 +11,10 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; use rustc_hir as hir; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; @@ -122,6 +128,37 @@ declare_clippy_lint! { "`pub fn` returns `Result` without `# Errors` in doc comment" } +declare_clippy_lint! { + /// **What it does:** Checks the doc comments of publicly visible functions that + /// may panic and warns if there is no `# Panics` section. + /// + /// **Why is this bad?** Documenting the scenarios in which panicking occurs + /// can help callers who do not want to panic to avoid those situations. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// + /// Since the following function may panic it has a `# Panics` section in + /// its doc comment: + /// + /// ```rust + /// /// # Panics + /// /// + /// /// Will panic if y is 0 + /// pub fn divide_by(x: i32, y: i32) -> i32 { + /// if y == 0 { + /// panic!("Cannot divide by 0") + /// } else { + /// x / y + /// } + /// } + /// ``` + pub MISSING_PANICS_DOC, + pedantic, + "`pub fn` may panic without `# Panics` in doc comment" +} + declare_clippy_lint! { /// **What it does:** Checks for `fn main() { .. }` in doctests /// @@ -166,7 +203,9 @@ impl DocMarkdown { } } -impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, NEEDLESS_DOCTEST_MAIN]); +impl_lint_pass!(DocMarkdown => + [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN] +); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { @@ -180,7 +219,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { if !(is_entrypoint_fn(cx, cx.tcx.hir().local_def_id(item.hir_id).to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(item.hir_id); + let mut fpu = FindPanicUnwrap { + cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + panic_span: None, + }; + fpu.visit_expr(&body.value); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } }, hir::ItemKind::Impl(ref impl_) => { @@ -200,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { let headers = check_attrs(cx, &self.valid_idents, &item.attrs); if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None, None); } } } @@ -211,7 +258,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { return; } if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(item.hir_id); + let mut fpu = FindPanicUnwrap { + cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + panic_span: None, + }; + fpu.visit_expr(&body.value); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } } } @@ -223,6 +278,7 @@ fn lint_for_missing_headers<'tcx>( sig: &hir::FnSig<'_>, headers: DocHeaders, body_id: Option, + panic_span: Option, ) { if !cx.access_levels.is_exported(hir_id) { return; // Private functions do not require doc comments @@ -235,6 +291,16 @@ fn lint_for_missing_headers<'tcx>( "unsafe function's docs miss `# Safety` section", ); } + if !headers.panics && panic_span.is_some() { + span_lint_and_note( + cx, + MISSING_PANICS_DOC, + span, + "docs for function which may panic missing `# Panics` section", + panic_span, + "first possible panic found here", + ); + } if !headers.errors { if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { span_lint( @@ -321,6 +387,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: struct DocHeaders { safety: bool, errors: bool, + panics: bool, } fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &'a [Attribute]) -> DocHeaders { @@ -338,6 +405,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs return DocHeaders { safety: true, errors: true, + panics: true, }; } } @@ -353,6 +421,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs return DocHeaders { safety: false, errors: false, + panics: false, }; } @@ -394,6 +463,7 @@ fn check_doc<'a, Events: Iterator, Range, Range o, Err(e) => e - 1, @@ -607,3 +678,47 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { ); } } + +struct FindPanicUnwrap<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + panic_span: Option, + typeck_results: &'tcx ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.panic_span.is_some() { + return; + } + + // check for `begin_panic` + if_chain! { + if let ExprKind::Call(ref func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let Some(path_def_id) = path.res.opt_def_id(); + if match_panic_def_id(self.cx, path_def_id); + then { + self.panic_span = Some(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); + if is_type_diagnostic_item(self.cx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.cx, reciever_ty, sym::result_type) + { + self.panic_span = Some(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 54007c29c6c..5a40c00bd67 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -592,6 +592,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, + &doc::MISSING_PANICS_DOC, &doc::MISSING_SAFETY_DOC, &doc::NEEDLESS_DOCTEST_MAIN, &double_comparison::DOUBLE_COMPARISONS, @@ -1317,6 +1318,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), LintId::of(&doc::DOC_MARKDOWN), LintId::of(&doc::MISSING_ERRORS_DOC), + LintId::of(&doc::MISSING_PANICS_DOC), LintId::of(&empty_enum::EMPTY_ENUM), LintId::of(&enum_variants::MODULE_NAME_REPETITIONS), LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES), diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 6caa04f651f..269be217c2d 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -110,7 +110,7 @@ pub fn span_lint_and_help<'a, T: LintContext>( pub fn span_lint_and_note<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, - span: Span, + span: impl Into, msg: &str, note_span: Option, note: &str, diff --git a/mini-macro/src/lib.rs b/mini-macro/src/lib.rs index ba946563ec5..2b793589049 100644 --- a/mini-macro/src/lib.rs +++ b/mini-macro/src/lib.rs @@ -7,6 +7,9 @@ extern crate proc_macro; use proc_macro::{quote, TokenStream}; #[proc_macro_derive(ClippyMiniMacroTest)] +/// # Panics +/// +/// Panics if the macro derivation fails pub fn mini_macro(_: TokenStream) -> TokenStream { quote!( #[allow(unused)] diff --git a/tests/ui/doc_panics.rs b/tests/ui/doc_panics.rs new file mode 100644 index 00000000000..7ef932f367b --- /dev/null +++ b/tests/ui/doc_panics.rs @@ -0,0 +1,95 @@ +#![warn(clippy::missing_panics_doc)] +#![allow(clippy::option_map_unit_fn)] + +fn main() {} + +/// This needs to be documented +pub fn unwrap() { + let result = Err("Hi"); + result.unwrap() +} + +/// This needs to be documented +pub fn panic() { + panic!("This function panics") +} + +/// This needs to be documented +pub fn todo() { + todo!() +} + +/// This needs to be documented +pub fn inner_body(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `result` if an error +pub fn unwrap_documented() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is documented +/// +/// # Panics +/// +/// Panics just because +pub fn panic_documented() { + panic!("This function panics") +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `opt` is Just(10) +pub fn inner_body_documented(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn todo_documented() { + todo!() +} + +/// This is okay because it is private +fn unwrap_private() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is okay because it is private +fn panic_private() { + panic!("This function panics") +} + +/// This is okay because it is private +fn todo_private() { + todo!() +} + +/// This is okay because it is private +fn inner_body_private(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} diff --git a/tests/ui/doc_panics.stderr b/tests/ui/doc_panics.stderr new file mode 100644 index 00000000000..c0c4e9e4fa7 --- /dev/null +++ b/tests/ui/doc_panics.stderr @@ -0,0 +1,67 @@ +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:7:1 + | +LL | / pub fn unwrap() { +LL | | let result = Err("Hi"); +LL | | result.unwrap() +LL | | } + | |_^ + | + = note: `-D clippy::missing-panics-doc` implied by `-D warnings` +note: first possible panic found here + --> $DIR/doc_panics.rs:9:5 + | +LL | result.unwrap() + | ^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:13:1 + | +LL | / pub fn panic() { +LL | | panic!("This function panics") +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:14:5 + | +LL | panic!("This function panics") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:18:1 + | +LL | / pub fn todo() { +LL | | todo!() +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:19:5 + | +LL | todo!() + | ^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:23:1 + | +LL | / pub fn inner_body(opt: Option) { +LL | | opt.map(|x| { +LL | | if x == 10 { +LL | | panic!() +LL | | } +LL | | }); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:26:13 + | +LL | panic!() + | ^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/should_impl_trait/corner_cases.rs b/tests/ui/should_impl_trait/corner_cases.rs index 6c5ffe6aba8..a7f8f54f2be 100644 --- a/tests/ui/should_impl_trait/corner_cases.rs +++ b/tests/ui/should_impl_trait/corner_cases.rs @@ -8,7 +8,8 @@ clippy::unused_self, clippy::needless_lifetimes, clippy::missing_safety_doc, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::missing_panics_doc )] use std::ops::Mul; diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs index f8d248fc98d..69a3390b03b 100644 --- a/tests/ui/should_impl_trait/method_list_1.rs +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -8,7 +8,8 @@ clippy::unused_self, clippy::needless_lifetimes, clippy::missing_safety_doc, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::missing_panics_doc )] use std::ops::Mul; diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr index 2b7d4628c3f..86c63946516 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> $DIR/method_list_1.rs:25:5 + --> $DIR/method_list_1.rs:26:5 | LL | / pub fn add(self, other: T) -> T { LL | | unimplemented!() @@ -10,7 +10,7 @@ LL | | } = 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 + --> $DIR/method_list_1.rs:30:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | unimplemented!() @@ -20,7 +20,7 @@ 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 + --> $DIR/method_list_1.rs:34:5 | LL | / pub fn as_ref(&self) -> &T { LL | | unimplemented!() @@ -30,7 +30,7 @@ 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 + --> $DIR/method_list_1.rs:38:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | unimplemented!() @@ -40,7 +40,7 @@ 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 + --> $DIR/method_list_1.rs:42:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -50,7 +50,7 @@ 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 + --> $DIR/method_list_1.rs:46:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -60,7 +60,7 @@ 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 + --> $DIR/method_list_1.rs:50:5 | LL | / pub fn borrow(&self) -> &str { LL | | unimplemented!() @@ -70,7 +70,7 @@ 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 + --> $DIR/method_list_1.rs:54:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | unimplemented!() @@ -80,7 +80,7 @@ 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 + --> $DIR/method_list_1.rs:58:5 | LL | / pub fn clone(&self) -> Self { LL | | unimplemented!() @@ -90,7 +90,7 @@ 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 + --> $DIR/method_list_1.rs:62:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | unimplemented!() @@ -100,7 +100,7 @@ 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 + --> $DIR/method_list_1.rs:70:5 | LL | / pub fn deref(&self) -> &Self { LL | | unimplemented!() @@ -110,7 +110,7 @@ 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 + --> $DIR/method_list_1.rs:74:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | unimplemented!() @@ -120,7 +120,7 @@ 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 + --> $DIR/method_list_1.rs:78:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -130,7 +130,7 @@ 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 + --> $DIR/method_list_1.rs:82:5 | LL | / pub fn drop(&mut self) { LL | | unimplemented!() diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index ed5e0d384bf..2cdc1a06fe6 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -8,7 +8,8 @@ clippy::unused_self, clippy::needless_lifetimes, clippy::missing_safety_doc, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::missing_panics_doc )] use std::ops::Mul; diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr index b6fd4356956..0142e299108 100644 --- a/tests/ui/should_impl_trait/method_list_2.stderr +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -1,5 +1,5 @@ error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` - --> $DIR/method_list_2.rs:26:5 + --> $DIR/method_list_2.rs:27:5 | LL | / pub fn eq(&self, other: &Self) -> bool { LL | | unimplemented!() @@ -10,7 +10,7 @@ LL | | } = 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 + --> $DIR/method_list_2.rs:31:5 | LL | / pub fn from_iter(iter: T) -> Self { LL | | unimplemented!() @@ -20,7 +20,7 @@ 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 + --> $DIR/method_list_2.rs:35:5 | LL | / pub fn from_str(s: &str) -> Result { LL | | unimplemented!() @@ -30,7 +30,7 @@ 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 + --> $DIR/method_list_2.rs:39:5 | LL | / pub fn hash(&self, state: &mut T) { LL | | unimplemented!() @@ -40,7 +40,7 @@ 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 + --> $DIR/method_list_2.rs:43:5 | LL | / pub fn index(&self, index: usize) -> &Self { LL | | unimplemented!() @@ -50,7 +50,7 @@ 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 + --> $DIR/method_list_2.rs:47:5 | LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { LL | | unimplemented!() @@ -60,7 +60,7 @@ 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 + --> $DIR/method_list_2.rs:51:5 | LL | / pub fn into_iter(self) -> Self { LL | | unimplemented!() @@ -70,7 +70,7 @@ 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 + --> $DIR/method_list_2.rs:55:5 | LL | / pub fn mul(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -80,7 +80,7 @@ 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 + --> $DIR/method_list_2.rs:59:5 | LL | / pub fn neg(self) -> Self { LL | | unimplemented!() @@ -90,7 +90,7 @@ 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 + --> $DIR/method_list_2.rs:63:5 | LL | / pub fn next(&mut self) -> Option { LL | | unimplemented!() @@ -100,7 +100,7 @@ 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 + --> $DIR/method_list_2.rs:67:5 | LL | / pub fn not(self) -> Self { LL | | unimplemented!() @@ -110,7 +110,7 @@ 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 + --> $DIR/method_list_2.rs:71:5 | LL | / pub fn rem(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -120,7 +120,7 @@ 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 + --> $DIR/method_list_2.rs:75:5 | LL | / pub fn shl(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -130,7 +130,7 @@ 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 + --> $DIR/method_list_2.rs:79:5 | LL | / pub fn shr(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -140,7 +140,7 @@ 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 + --> $DIR/method_list_2.rs:83:5 | LL | / pub fn sub(self, rhs: Self) -> Self { LL | | unimplemented!() -- cgit 1.4.1-3-g733a5 From e07cd5b6fe47b1e26f19a1bede7c2e4967cb46d7 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Tue, 2 Feb 2021 19:04:20 +0100 Subject: Enhance manual flatten --- clippy_lints/src/loops.rs | 112 ++++++++++++++++++++++++++--------------- tests/ui/manual_flatten.rs | 10 ++++ tests/ui/manual_flatten.stderr | 47 ++++++++--------- 3 files changed, 101 insertions(+), 68 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index db5aec82e90..23dce283f28 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1996,52 +1996,82 @@ fn check_manual_flatten<'tcx>( body: &'tcx Expr<'_>, span: Span, ) { - if_chain! { - // Ensure the `if let` statement is the only expression in the for-loop - if let ExprKind::Block(ref block, _) = body.kind; - if block.stmts.is_empty(); - if let Some(inner_expr) = block.expr; - if let ExprKind::Match( - ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } - ) = inner_expr.kind; - // Ensure match_expr in `if let` statement is the same as the pat from the for-loop - if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; - if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; - if let Res::Local(match_expr_path_id) = match_expr_path.res; - if pat_hir_id == match_expr_path_id; - // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); - let if_let_type = if is_some_ctor(cx, path.res) { - "Some" + if let ExprKind::Block(ref block, _) = body.kind { + // Ensure the `if let` statement is the only expression or statement in the for-loop + let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { + let match_stmt = &block.stmts[0]; + if let StmtKind::Semi(inner_expr) = match_stmt.kind { + Some(inner_expr) + } else { + None + } + } else if block.stmts.is_empty() { + block.expr } else { - "Ok" + None }; - // Determine if `arg` is `Iterator` or implicitly calls `into_iter` - let arg_ty = cx.typeck_results().expr_ty(arg); - if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR); - if let is_iterator = implements_trait(cx, arg_ty, id, &[]); - then { - // Prepare the error message - let msg = format!("Unnecessary `if let` since only the `{}` variant of the iterator element is used.", if_let_type); + if_chain! { + if let Some(inner_expr) = inner_expr; + if let ExprKind::Match( + ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + ) = inner_expr.kind; + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; + if let Res::Local(match_expr_path_id) = match_expr_path.res; + if pat_hir_id == match_expr_path_id; + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; + let some_ctor = is_some_ctor(cx, path.res); + let ok_ctor = is_ok_ctor(cx, path.res); + if some_ctor || ok_ctor; + let if_let_type = if some_ctor { "Some" } else { "Ok" }; - // Prepare the help message - let arg_snippet = snippet(cx, arg.span, ".."); - let hint = if is_iterator { - format!("try: `{}.flatten()`", arg_snippet) - } else { - format!("try: `{}.into_iter().flatten()`", arg_snippet) - }; + then { + // Prepare the error message + let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); - span_lint_and_help( - cx, - MANUAL_FLATTEN, - span, - &msg, - Some(arg.span), - &hint, - ); + // Prepare the help message + let mut applicability = Applicability::MaybeIncorrect; + let arg_snippet = snippet_with_applicability( + cx, + arg.span, + "..", + &mut applicability, + ); + // Determine if `arg` is by reference, an `Iterator`, or implicitly adjusted with `into_iter` + let hint = match arg.kind { + ExprKind::AddrOf(_, _, arg_expr) => { + format!("{}.iter().flatten()", snippet(cx, arg_expr.span, "..")) + }, + ExprKind::MethodCall(_, _, _, _) | ExprKind::Path(QPath::Resolved(None, _)) => { + // Determine if `arg` is `Iterator` or implicitly calls `into_iter` + let arg_ty = cx.typeck_results().expr_ty(arg); + if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR) { + let is_iterator = implements_trait(cx, arg_ty, id, &[]); + if is_iterator { + format!("{}.flatten()", arg_snippet) + } else { + format!("{}.into_iter().flatten()", arg_snippet) + } + } else { + return + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + MANUAL_FLATTEN, + span, + &msg, + "try", + hint, + applicability, + ) + } } } } diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index f183ceecdd8..b97cceb66f8 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,6 +1,7 @@ #![warn(clippy::manual_flatten)] fn main() { + // Test for loop over implicitly adjusted `Iterator` with `if let` expression let x = vec![Some(1), Some(2), Some(3)]; for n in x { if let Some(n) = n { @@ -8,13 +9,22 @@ fn main() { } } + // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement let y: Vec> = vec![]; for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + }; + } + + // Test for loop over by reference + for n in &y { if let Ok(n) = n { println!("{}", n); } } + // Test for loop over `Iterator` with `if let` expression let z = vec![Some(1), Some(2), Some(3)]; let z = z.iter(); for n in z { diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr index cf99a2d9ab1..754921eb739 100644 --- a/tests/ui/manual_flatten.stderr +++ b/tests/ui/manual_flatten.stderr @@ -1,51 +1,44 @@ -error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. - --> $DIR/manual_flatten.rs:5:5 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:6:5 | LL | / for n in x { LL | | if let Some(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ + | |_____^ help: try: `x.into_iter().flatten()` | = note: `-D clippy::manual-flatten` implied by `-D warnings` -help: try: `x.into_iter().flatten()` - --> $DIR/manual_flatten.rs:5:14 - | -LL | for n in x { - | ^ -error: Unnecessary `if let` since only the `Ok` variant of the iterator element is used. - --> $DIR/manual_flatten.rs:12:5 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:14:5 | LL | / for n in y.clone() { LL | | if let Ok(n) = n { LL | | println!("{}", n); -LL | | } +LL | | }; LL | | } - | |_____^ - | -help: try: `y.clone().into_iter().flatten()` - --> $DIR/manual_flatten.rs:12:14 + | |_____^ help: try: `y.clone().into_iter().flatten()` + +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:21:5 | -LL | for n in y.clone() { - | ^^^^^^^^^ +LL | / for n in &y { +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ help: try: `y.iter().flatten()` -error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. - --> $DIR/manual_flatten.rs:20:5 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:30:5 | LL | / for n in z { LL | | if let Some(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ - | -help: try: `z.flatten()` - --> $DIR/manual_flatten.rs:20:14 - | -LL | for n in z { - | ^ + | |_____^ help: try: `z.flatten()` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From bfbc0835870f8bf6cde79a01dfe7de351dde14aa Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Tue, 2 Feb 2021 18:39:23 -0500 Subject: Fix for issue 6640 --- clippy_lints/src/unnecessary_wraps.rs | 92 ++++++++++++++++++---------- tests/ui/unnecessary_wraps.rs | 28 +++++++++ tests/ui/unnecessary_wraps.stderr | 109 ++-------------------------------- 3 files changed, 93 insertions(+), 136 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 8ac5dd696b7..eec76ce8b03 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,6 @@ use crate::utils::{ - contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, + contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_sugg, + span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -64,6 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { span: Span, hir_id: HirId, ) { + // Abort if public function/method or closure. match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { @@ -74,6 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { _ => (), } + // Abort if the method is implementing a trait or of it a trait method. if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { if matches!( item.kind, @@ -83,22 +85,43 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } } - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) { + // Check if return type is Option or Result. If neither, abort. + let return_ty = return_ty(cx, hir_id); + let (return_type_label, path) = if is_type_diagnostic_item(cx, return_ty, sym::option_type) { ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { + } else if is_type_diagnostic_item(cx, return_ty, sym::result_type) { ("Result", &paths::RESULT_OK) } else { return; }; + // Take the first inner type of the Option or Result. If can't, abort. + let inner_ty = if_chain! { + // Skip Option or Result and take the first outermost inner type. + if let Some(inner_ty) = return_ty.walk().nth(1); + if let GenericArgKind::Type(inner_ty) = inner_ty.unpack(); + then { + inner_ty + } else { + return; + } + }; + + // Check if all return expression respect the following condition and collect them. let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { + // Abort if in macro. if !in_macro(ret_expr.span); + // Check if a function call. if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; + // Check if OPTION_SOME or RESULT_OK, depending on return type. if match_qpath(qpath, path); + // Make sure the function call has only one argument. if args.len() == 1; + // Make sure the function argument does not contain a return expression. if !contains_return(&args[0]); then { suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); @@ -110,39 +133,42 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { }); if can_sugg && !suggs.is_empty() { - span_lint_and_then( - cx, - UNNECESSARY_WRAPS, - span, - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type - ) - .as_str(), - |diag| { - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.for_each(|inner_ty| { + // Issue 6640: If the inner type is Unit, emit lint similar to clippy::unused_unit. + if inner_ty.is_unit() { + span_lint_and_sugg( + cx, + UNNECESSARY_WRAPS, + fn_decl.output.span(), + "unneeded wrapped unit return type", + format!("remove the `-> {}<()>`", return_type_label).as_str(), + String::new(), + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_then( + cx, + UNNECESSARY_WRAPS, + span, + format!( + "this function's return value is unnecessarily wrapped by `{}`", + return_type_label + ) + .as_str(), + |diag| { diag.span_suggestion( fn_decl.output.span(), - format!("remove `{}` from the return type...", return_type).as_str(), - inner_ty, + format!("remove `{}` from the return type...", return_type_label).as_str(), + inner_ty.to_string(), Applicability::MaybeIncorrect, ); - }); - diag.multipart_suggestion( - "...and change the returning expressions", - suggs, - Applicability::MaybeIncorrect, - ); - }, - ); + diag.multipart_suggestion( + "...and change the returning expressions", + suggs, + Applicability::MaybeIncorrect, + ); + }, + ); + } } } } diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index a4570098d71..2d6aedc97ef 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -116,8 +116,36 @@ fn issue_6384(s: &str) -> Option<&str> { }) } +// should be linted +fn issue_6640_1(a: bool, b: bool) -> Option<()> { + if a && b { + return Some(()); + } + if a { + Some(()); + Some(()) + } else { + return Some(()); + } +} + +// should be linted +fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + if a && b { + return Ok(()); + } + if a { + Ok(()); + Ok(()) + } else { + return Ok(()); + } +} + fn main() { // method calls are not linted func1(true, true); func2(true, true); + issue_6640_1(true, true); + issue_6640_2(true, true); } diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 410f054b8ef..3ca5a8d1702 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -1,106 +1,9 @@ -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:8:1 - | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(42); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | - = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` -help: remove `Option` from the return type... - | -LL | fn func1(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 42; -LL | } -LL | if a { -LL | Some(-1); -LL | 2 -LL | } else { - ... - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:21:1 - | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(10); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func2(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 10; -LL | } -LL | if a { -LL | 20 -LL | } else { -LL | 30 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:51:1 - | -LL | / fn func5() -> Option { -LL | | Some(1) -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func5() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wraps.rs:61:1 - | -LL | / fn func7() -> Result { -LL | | Ok(1) -LL | | } - | |_^ - | -help: remove `Result` from the return type... - | -LL | fn func7() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:93:5 - | -LL | / fn func12() -> Option { -LL | | Some(1) -LL | | } - | |_____^ - | -help: remove `Option` from the return type... - | -LL | fn func12() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 +error[E0282]: type annotations needed + --> $DIR/unnecessary_wraps.rs:138:9 | +LL | Ok(()); + | ^^ cannot infer type for type parameter `E` declared on the enum `Result` -error: aborting due to 5 previous errors +error: aborting due to previous error +For more information about this error, try `rustc --explain E0282`. -- cgit 1.4.1-3-g733a5 From e0e51e418906916123b3bd15c175d80d3dc74bf2 Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Tue, 2 Feb 2021 19:14:13 -0500 Subject: Fixed test --- clippy_lints/src/unnecessary_wraps.rs | 2 +- tests/ui/unnecessary_wraps.rs | 1 - tests/ui/unnecessary_wraps.stderr | 121 ++++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index eec76ce8b03..7da42ba3934 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { UNNECESSARY_WRAPS, fn_decl.output.span(), "unneeded wrapped unit return type", - format!("remove the `-> {}<()>`", return_type_label).as_str(), + format!("remove the `-> {}<[...]>`", return_type_label).as_str(), String::new(), Applicability::MaybeIncorrect, ); diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index 2d6aedc97ef..5aaa99bbe5a 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -135,7 +135,6 @@ fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { return Ok(()); } if a { - Ok(()); Ok(()) } else { return Ok(()); diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 3ca5a8d1702..f28981f9e34 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -1,9 +1,118 @@ -error[E0282]: type annotations needed - --> $DIR/unnecessary_wraps.rs:138:9 +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:8:1 | -LL | Ok(()); - | ^^ cannot infer type for type parameter `E` declared on the enum `Result` +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` +help: remove `Option` from the return type... + | +LL | fn func1(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:21:1 + | +LL | / fn func2(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(10); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func2(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 10; +LL | } +LL | if a { +LL | 20 +LL | } else { +LL | 30 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:51:1 + | +LL | / fn func5() -> Option { +LL | | Some(1) +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func5() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Result` + --> $DIR/unnecessary_wraps.rs:61:1 + | +LL | / fn func7() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: remove `Result` from the return type... + | +LL | fn func7() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:93:5 + | +LL | / fn func12() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func12() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: unneeded wrapped unit return type + --> $DIR/unnecessary_wraps.rs:120:38 + | +LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { + | ^^^^^^^^^^ help: remove the `-> Option<[...]>` + +error: unneeded wrapped unit return type + --> $DIR/unnecessary_wraps.rs:133:38 + | +LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + | ^^^^^^^^^^^^^^^ help: remove the `-> Result<[...]>` -error: aborting due to previous error +error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0282`. -- cgit 1.4.1-3-g733a5 From 6fb10755c046ddc376efa27c396bc0e527059785 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 2 Feb 2021 17:35:39 -0800 Subject: Rustup --- clippy_lints/src/write.rs | 6 +++--- rust-toolchain | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 6358104eeda..04a0f6765c5 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -3,7 +3,7 @@ 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, LitKind, MacCall, StrLit, StrStyle}; +use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; @@ -231,10 +231,10 @@ impl_lint_pass!(Write => [ impl EarlyLintPass for Write { fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Impl { + if let ItemKind::Impl (box ImplKind { of_trait: Some(trait_ref), .. - } = &item.kind + }) = &item.kind { let trait_name = trait_ref .path diff --git a/rust-toolchain b/rust-toolchain index f55d55d7065..b617203bef6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-01-30" +channel = "nightly-2021-02-03" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] -- cgit 1.4.1-3-g733a5 From a31e5f5f3b300d200f7917e28f467437435b28c1 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 2 Feb 2021 19:57:08 -0800 Subject: Run rustfmt --- clippy_lints/src/doc.rs | 4 +-- clippy_lints/src/excessive_bools.rs | 12 ++++----- clippy_lints/src/non_expressive_names.rs | 3 +-- clippy_lints/src/utils/ast_utils.rs | 45 +++++++++----------------------- clippy_lints/src/write.rs | 2 +- 5 files changed, 22 insertions(+), 44 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 75e71eb1e4c..1348f7530f4 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -563,9 +563,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) => return false, // We found a main function ... - ItemKind::Fn(box FnKind(_, sig, _, Some(block))) - if item.ident.name == sym::main => - { + ItemKind::Fn(box FnKind(_, sig, _, Some(block))) if item.ident.name == sym::main => { let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index fecde8e2743..6a85b57af07 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,7 +1,5 @@ use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help}; -use rustc_ast::ast::{ - AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind, -}; +use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -160,15 +158,17 @@ impl EarlyLintPass for ExcessiveBools { "consider using a state machine or refactoring bools into two-variant enums", ); } - } - ItemKind::Impl(box ImplKind { of_trait: None, items, .. }) + }, + ItemKind::Impl(box ImplKind { + of_trait: None, items, .. + }) | ItemKind::Trait(box TraitKind(.., items)) => { for item in items { if let AssocItemKind::Fn(box FnKind(_, fn_sig, _, _)) = &item.kind { self.check_fn_sig(cx, fn_sig, item.span); } } - } + }, ItemKind::Fn(box FnKind(_, fn_sig, _, _)) => self.check_fn_sig(cx, fn_sig, item.span), _ => (), } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index d5222a030d7..7c74b316018 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,7 +1,6 @@ use crate::utils::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ - Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, - PatKind, + Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind, }; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 69492e84e4a..44eb3968ae7 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -229,25 +229,16 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { match (l, r) { (ExternCrate(l), ExternCrate(r)) => l == r, (Use(l), Use(r)) => eq_use_tree(l, r), - (Static(lt, lm, le), Static(rt, rm, re)) => { - lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re) - } - (Const(ld, lt, le), Const(rd, rt, re)) => { - eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re) - } + (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), + (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re), (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => { - eq_defaultness(*ld, *rd) - && eq_fn_sig(lf, rf) - && eq_generics(lg, rg) - && both(lb, rb, |l, r| eq_block(l, r)) - } - (Mod(l), Mod(r)) => { - l.inline == r.inline && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_item_kind)) - } + eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) + }, + (Mod(l), Mod(r)) => l.inline == r.inline && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_item_kind)), (ForeignMod(l), ForeignMod(r)) => { both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r)) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind)) - } + }, (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => { eq_defaultness(*ld, *rd) && eq_generics(lg, rg) @@ -259,7 +250,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }, (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => { eq_variant_data(lv, rv) && eq_generics(lg, rg) - } + }, (Trait(box TraitKind(la, lu, lg, lb, li)), Trait(box TraitKind(ra, ru, rg, rb, ri))) => { la == ra && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No) @@ -308,15 +299,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { use ForeignItemKind::*; match (l, r) { - (Static(lt, lm, le), Static(rt, rm, re)) => { - lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re) - } + (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => { - eq_defaultness(*ld, *rd) - && eq_fn_sig(lf, rf) - && eq_generics(lg, rg) - && both(lb, rb, |l, r| eq_block(l, r)) - } + eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) + }, (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => { eq_defaultness(*ld, *rd) && eq_generics(lg, rg) @@ -331,15 +317,10 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { use AssocItemKind::*; match (l, r) { - (Const(ld, lt, le), Const(rd, rt, re)) => { - eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re) - } + (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re), (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => { - eq_defaultness(*ld, *rd) - && eq_fn_sig(lf, rf) - && eq_generics(lg, rg) - && both(lb, rb, |l, r| eq_block(l, r)) - } + eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) + }, (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => { eq_defaultness(*ld, *rd) && eq_generics(lg, rg) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 04a0f6765c5..e40fdca6a99 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -231,7 +231,7 @@ impl_lint_pass!(Write => [ impl EarlyLintPass for Write { fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Impl (box ImplKind { + if let ItemKind::Impl(box ImplKind { of_trait: Some(trait_ref), .. }) = &item.kind -- cgit 1.4.1-3-g733a5 From 0f5e71f8f2d42e3eac3e855a83d53899d4da6eb3 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 3 Feb 2021 09:39:35 +0100 Subject: Add additional check on if arg type has iter method --- clippy_lints/src/loops.rs | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 23dce283f28..5bfdc98bc6a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2041,25 +2041,24 @@ fn check_manual_flatten<'tcx>( &mut applicability, ); // Determine if `arg` is by reference, an `Iterator`, or implicitly adjusted with `into_iter` - let hint = match arg.kind { - ExprKind::AddrOf(_, _, arg_expr) => { + let arg_ty = cx.typeck_results().expr_ty(arg); + let hint = if arg_ty.is_ref() { + if has_iter_method(cx, arg_ty).is_none() { + return; + } else if let ExprKind::AddrOf(_, _, arg_expr) = arg.kind { format!("{}.iter().flatten()", snippet(cx, arg_expr.span, "..")) - }, - ExprKind::MethodCall(_, _, _, _) | ExprKind::Path(QPath::Resolved(None, _)) => { - // Determine if `arg` is `Iterator` or implicitly calls `into_iter` - let arg_ty = cx.typeck_results().expr_ty(arg); - if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR) { - let is_iterator = implements_trait(cx, arg_ty, id, &[]); - if is_iterator { - format!("{}.flatten()", arg_snippet) - } else { - format!("{}.into_iter().flatten()", arg_snippet) - } - } else { - return - } - }, - _ => return, + } else { + return; + } + } else if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR) { + let is_iterator = implements_trait(cx, arg_ty, id, &[]); + if is_iterator { + format!("{}.flatten()", arg_snippet) + } else { + format!("{}.into_iter().flatten()", arg_snippet) + } + } else { + return }; span_lint_and_sugg( -- cgit 1.4.1-3-g733a5 From 78ef0f2f6c17f5933ab4dbab544b76f7da742467 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 3 Feb 2021 19:45:58 +0100 Subject: Add additional test cases and improve span lint --- clippy_lints/src/loops.rs | 47 ++++++++------------- tests/ui/manual_flatten.rs | 18 +++++++-- tests/ui/manual_flatten.stderr | 92 +++++++++++++++++++++++++++++++++++------- 3 files changed, 108 insertions(+), 49 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5bfdc98bc6a..817230a29c6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2034,42 +2034,27 @@ fn check_manual_flatten<'tcx>( // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; - let arg_snippet = snippet_with_applicability( - cx, - arg.span, - "..", - &mut applicability, - ); - // Determine if `arg` is by reference, an `Iterator`, or implicitly adjusted with `into_iter` - let arg_ty = cx.typeck_results().expr_ty(arg); - let hint = if arg_ty.is_ref() { - if has_iter_method(cx, arg_ty).is_none() { - return; - } else if let ExprKind::AddrOf(_, _, arg_expr) = arg.kind { - format!("{}.iter().flatten()", snippet(cx, arg_expr.span, "..")) - } else { - return; - } - } else if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR) { - let is_iterator = implements_trait(cx, arg_ty, id, &[]); - if is_iterator { - format!("{}.flatten()", arg_snippet) - } else { - format!("{}.into_iter().flatten()", arg_snippet) - } - } else { - return - }; + let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); - span_lint_and_sugg( + span_lint_and_then( cx, MANUAL_FLATTEN, span, &msg, - "try", - hint, - applicability, - ) + |diag| { + let sugg = format!("{}.flatten()", arg_snippet); + diag.span_suggestion( + arg.span, + "try", + sugg, + Applicability::MaybeIncorrect, + ); + diag.span_help( + inner_expr.span, + "also remove the `if let` statement in the for loop", + ); + } + ); } } } diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index b97cceb66f8..ea3440f6da2 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -4,8 +4,8 @@ fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression let x = vec![Some(1), Some(2), Some(3)]; for n in x { - if let Some(n) = n { - println!("{}", n); + if let Some(y) = n { + println!("{}", y); } } @@ -24,12 +24,22 @@ fn main() { } } + // Test for loop over an implicit reference + // Note: If `clippy::manual_flatten` is made autofixable, this case will + // lead to a follow-up lint `clippy::into_iter_on_ref` + let z = &y; + for n in z { + if let Ok(n) = n { + println!("{}", n); + } + } + // Test for loop over `Iterator` with `if let` expression let z = vec![Some(1), Some(2), Some(3)]; let z = z.iter(); for n in z { - if let Some(n) = n { - println!("{}", n); + if let Some(m) = n { + println!("{}", m); } } diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr index 754921eb739..49b8ed0564a 100644 --- a/tests/ui/manual_flatten.stderr +++ b/tests/ui/manual_flatten.stderr @@ -1,44 +1,108 @@ error: unnecessary `if let` since only the `Some` variant of the iterator element is used --> $DIR/manual_flatten.rs:6:5 | -LL | / for n in x { -LL | | if let Some(n) = n { -LL | | println!("{}", n); +LL | for n in x { + | ^ - help: try: `x.into_iter().flatten()` + | _____| + | | +LL | | if let Some(y) = n { +LL | | println!("{}", y); LL | | } LL | | } - | |_____^ help: try: `x.into_iter().flatten()` + | |_____^ | = note: `-D clippy::manual-flatten` implied by `-D warnings` +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:7:9 + | +LL | / if let Some(y) = n { +LL | | println!("{}", y); +LL | | } + | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used --> $DIR/manual_flatten.rs:14:5 | -LL | / for n in y.clone() { +LL | for n in y.clone() { + | ^ --------- help: try: `y.clone().into_iter().flatten()` + | _____| + | | LL | | if let Ok(n) = n { LL | | println!("{}", n); LL | | }; LL | | } - | |_____^ help: try: `y.clone().into_iter().flatten()` + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:15:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | }; + | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used --> $DIR/manual_flatten.rs:21:5 | -LL | / for n in &y { +LL | for n in &y { + | ^ -- help: try: `y.iter().flatten()` + | _____| + | | LL | | if let Ok(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ help: try: `y.iter().flatten()` + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:22:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } + | |_________^ -error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:30:5 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:31:5 | -LL | / for n in z { -LL | | if let Some(n) = n { +LL | for n in z { + | ^ - help: try: `z.into_iter().flatten()` + | _____| + | | +LL | | if let Ok(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ help: try: `z.flatten()` + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:32:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:40:5 + | +LL | for n in z { + | ^ - help: try: `z.flatten()` + | _____| + | | +LL | | if let Some(m) = n { +LL | | println!("{}", m); +LL | | } +LL | | } + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:41:9 + | +LL | / if let Some(m) = n { +LL | | println!("{}", m); +LL | | } + | |_________^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From e32e4dedf1781a4696c34f31d69e68c7c0eaf6a9 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 2 Feb 2021 12:26:20 +0900 Subject: New lint: default_numeric_fallback --- CHANGELOG.md | 1 + clippy_lints/src/default_numeric_fallback.rs | 388 +++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + tests/ui/default_numeric_fallback.rs | 99 +++++++ tests/ui/default_numeric_fallback.stderr | 187 +++++++++++++ 5 files changed, 679 insertions(+) create mode 100644 clippy_lints/src/default_numeric_fallback.rs create mode 100644 tests/ui/default_numeric_fallback.rs create mode 100644 tests/ui/default_numeric_fallback.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index c1032204a22..5eed9664088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1909,6 +1909,7 @@ Released 2018-09-13 [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs new file mode 100644 index 00000000000..f049e64d0fb --- /dev/null +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -0,0 +1,388 @@ +use rustc_ast::ast::{Label, LitFloatType, LitIntType, LitKind}; +use rustc_hir::{ + self as hir, + intravisit::{walk_expr, walk_stmt, walk_ty, FnKind, NestedVisitorMap, Visitor}, + Body, Expr, ExprKind, FnDecl, FnRetTy, Guard, HirId, Lit, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::{ + hir::map::Map, + ty::{self, subst::GenericArgKind, FloatTy, IntTy, Ty, TyCtxt}, +}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use crate::utils::span_lint_and_help; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type + /// inference. + /// + /// Default numeric fallback means that if numeric types have not yet been bound to concrete + /// types at the end of type inference, then integer type is bound to `i32`, and similarly + /// floating type is bound to `f64`. + /// + /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback. + /// + /// **Why is this bad?** For those who are very careful about types, default numeric fallback + /// can be a pitfall that cause unexpected runtime behavior. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let i = 10; + /// let f = 1.23; + /// ``` + /// + /// Use instead: + /// ```rust + /// let i = 10i32; + /// let f = 1.23f64; + /// ``` + pub DEFAULT_NUMERIC_FALLBACK, + restriction, + "usage of unconstrained numeric literals which may cause default numeric fallback." +} + +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); + +fn enclosing_body_owner_opt(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { + let hir_map = tcx.hir(); + for (parent, _) in hir_map.parent_iter(hir_id) { + if let Some(body) = hir_map.maybe_body_owned_by(parent) { + return Some(hir_map.body_owner(body)); + } + } + None +} + +impl LateLintPass<'_> for DefaultNumericFallback { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + hir_id: HirId, + ) { + let ret_ty_bound = match fn_decl.output { + FnRetTy::DefaultReturn(_) => None, + FnRetTy::Return(ty) => Some(ty), + } + .and_then(|ty| { + let mut infer_ty_finder = InferTyFinder::new(); + infer_ty_finder.visit_ty(ty); + if infer_ty_finder.found { + None + } else if enclosing_body_owner_opt(cx.tcx, hir_id).is_some() { + cx.typeck_results().node_type_opt(ty.hir_id) + } else { + Some(hir_ty_to_ty(cx.tcx, ty)) + } + }); + + let mut visitor = NumericFallbackVisitor::new(ret_ty_bound, cx); + visitor.visit_body(body); + } +} + +struct NumericFallbackVisitor<'a, 'tcx> { + /// Stack manages type bound of exprs. The top element holds current expr type. + ty_bounds: Vec>>, + + /// Ret type bound. + ret_ty_bound: Option>, + + /// Break type bounds. + break_ty_bounds: Vec<(Option) {} + fn a(_: Vec) -> Self { + unimplemented!() + } + } + + impl Trait> for Vec + where + T: Trait, + { + fn a(v: Vec) -> Self { + >::a(v).into_iter().map(Trait::a).collect() + } } } @@ -197,8 +213,8 @@ mod rustfix { fn fun_1() {} fn fun_2() { - Self::fun_1(); - Self::A; + nested::A::fun_1(); + nested::A::A; Self {}; } @@ -219,7 +235,8 @@ mod issue3567 { impl Test for TestStruct { fn test() -> TestStruct { - Self::from_something() + // FIXME: applicable here + TestStruct::from_something() } } } @@ -233,12 +250,14 @@ mod paths_created_by_lowering { const A: usize = 0; const B: usize = 1; - async fn g() -> Self { + // FIXME: applicable here + async fn g() -> S { Self {} } fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { - &p[Self::A..Self::B] + // FIXME: applicable here twice + &p[S::A..S::B] } } @@ -252,3 +271,194 @@ mod paths_created_by_lowering { } } } + +// reused from #1997 +mod generics { + struct Foo { + value: T, + } + + impl Foo { + // `Self` is applicable here + fn foo(value: T) -> Self { + Self { value } + } + + // `Cannot` use `Self` as a return type as the generic types are different + fn bar(value: i32) -> Foo { + Foo { value } + } + } +} + +mod issue4140 { + pub struct Error { + _from: From, + _too: To, + } + + pub trait From { + type From; + type To; + + fn from(value: T) -> Self; + } + + pub trait TryFrom + where + Self: Sized, + { + type From; + type To; + + fn try_from(value: T) -> Result>; + } + + impl TryFrom for T + where + T: From, + { + type From = Self; + type To = Self; + + fn try_from(value: F) -> Result> { + Ok(From::from(value)) + } + } + + impl From for i64 { + type From = bool; + type To = Self; + + fn from(value: bool) -> Self { + if value { + 100 + } else { + 0 + } + } + } +} + +mod issue2843 { + trait Foo { + type Bar; + } + + impl Foo for usize { + type Bar = u8; + } + + impl Foo for Option { + type Bar = Option; + } +} + +mod issue3859 { + pub struct Foo; + pub struct Bar([usize; 3]); + + impl Foo { + pub const BAR: usize = 3; + + pub fn foo() { + const _X: usize = Foo::BAR; + // const _Y: usize = Self::BAR; + } + } +} + +mod issue4305 { + trait Foo: 'static {} + + struct Bar; + + impl Foo for Bar {} + + impl From for Box { + fn from(t: T) -> Self { + // FIXME: applicable here + Box::new(t) + } + } +} + +mod lint_at_item_level { + struct Foo {} + + #[allow(clippy::use_self)] + impl Foo { + fn new() -> Foo { + Foo {} + } + } + + #[allow(clippy::use_self)] + impl Default for Foo { + fn default() -> Foo { + Foo::new() + } + } +} + +mod lint_at_impl_item_level { + struct Foo {} + + impl Foo { + #[allow(clippy::use_self)] + fn new() -> Foo { + Foo {} + } + } + + impl Default for Foo { + #[allow(clippy::use_self)] + fn default() -> Foo { + Foo::new() + } + } +} + +mod issue4734 { + #[repr(C, packed)] + pub struct X { + pub x: u32, + } + + impl From for u32 { + fn from(c: X) -> Self { + unsafe { core::mem::transmute(c) } + } + } +} + +mod nested_paths { + use std::convert::Into; + mod submod { + pub struct B {} + pub struct C {} + + impl Into for B { + fn into(self) -> C { + C {} + } + } + } + + struct A { + t: T, + } + + impl A { + fn new>(v: V) -> Self { + Self { t: Into::into(v) } + } + } + + impl A { + fn test() -> Self { + // FIXME: applicable here + A::new::(submod::B {}) + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index ddfd2beba31..347f5e96555 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -21,6 +21,7 @@ mod use_self { impl Default for Foo { fn default() -> Foo { + // FIXME: applicable here Foo::new() } } @@ -87,7 +88,11 @@ mod existential { struct Foo; impl Foo { - fn bad(foos: &[Self]) -> impl Iterator { + // FIXME: + // TyKind::Def (used for `impl Trait` types) does not include type parameters yet. + // See documentation in rustc_hir::hir::TyKind. + // The hir tree walk stops at `impl Iterator` level and does not inspect &Foo. + fn bad(foos: &[Foo]) -> impl Iterator { foos.iter() } @@ -177,11 +182,22 @@ mod issue3410 { struct B; trait Trait { - fn a(v: T); + fn a(v: T) -> Self; } impl Trait> for Vec { - fn a(_: Vec) {} + fn a(_: Vec) -> Self { + unimplemented!() + } + } + + impl Trait> for Vec + where + T: Trait, + { + fn a(v: Vec) -> Self { + >::a(v).into_iter().map(Trait::a).collect() + } } } @@ -219,6 +235,7 @@ mod issue3567 { impl Test for TestStruct { fn test() -> TestStruct { + // FIXME: applicable here TestStruct::from_something() } } @@ -233,11 +250,13 @@ mod paths_created_by_lowering { const A: usize = 0; const B: usize = 1; + // FIXME: applicable here async fn g() -> S { S {} } fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + // FIXME: applicable here twice &p[S::A..S::B] } } @@ -252,3 +271,194 @@ mod paths_created_by_lowering { } } } + +// reused from #1997 +mod generics { + struct Foo { + value: T, + } + + impl Foo { + // `Self` is applicable here + fn foo(value: T) -> Foo { + Foo { value } + } + + // `Cannot` use `Self` as a return type as the generic types are different + fn bar(value: i32) -> Foo { + Foo { value } + } + } +} + +mod issue4140 { + pub struct Error { + _from: From, + _too: To, + } + + pub trait From { + type From; + type To; + + fn from(value: T) -> Self; + } + + pub trait TryFrom + where + Self: Sized, + { + type From; + type To; + + fn try_from(value: T) -> Result>; + } + + impl TryFrom for T + where + T: From, + { + type From = T::From; + type To = T::To; + + fn try_from(value: F) -> Result> { + Ok(From::from(value)) + } + } + + impl From for i64 { + type From = bool; + type To = Self; + + fn from(value: bool) -> Self { + if value { + 100 + } else { + 0 + } + } + } +} + +mod issue2843 { + trait Foo { + type Bar; + } + + impl Foo for usize { + type Bar = u8; + } + + impl Foo for Option { + type Bar = Option; + } +} + +mod issue3859 { + pub struct Foo; + pub struct Bar([usize; 3]); + + impl Foo { + pub const BAR: usize = 3; + + pub fn foo() { + const _X: usize = Foo::BAR; + // const _Y: usize = Self::BAR; + } + } +} + +mod issue4305 { + trait Foo: 'static {} + + struct Bar; + + impl Foo for Bar {} + + impl From for Box { + fn from(t: T) -> Self { + // FIXME: applicable here + Box::new(t) + } + } +} + +mod lint_at_item_level { + struct Foo {} + + #[allow(clippy::use_self)] + impl Foo { + fn new() -> Foo { + Foo {} + } + } + + #[allow(clippy::use_self)] + impl Default for Foo { + fn default() -> Foo { + Foo::new() + } + } +} + +mod lint_at_impl_item_level { + struct Foo {} + + impl Foo { + #[allow(clippy::use_self)] + fn new() -> Foo { + Foo {} + } + } + + impl Default for Foo { + #[allow(clippy::use_self)] + fn default() -> Foo { + Foo::new() + } + } +} + +mod issue4734 { + #[repr(C, packed)] + pub struct X { + pub x: u32, + } + + impl From for u32 { + fn from(c: X) -> Self { + unsafe { core::mem::transmute(c) } + } + } +} + +mod nested_paths { + use std::convert::Into; + mod submod { + pub struct B {} + pub struct C {} + + impl Into for B { + fn into(self) -> C { + C {} + } + } + } + + struct A { + t: T, + } + + impl A { + fn new>(v: V) -> Self { + Self { t: Into::into(v) } + } + } + + impl A { + fn test() -> Self { + // FIXME: applicable here + A::new::(submod::B {}) + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 80e1bfc75e8..a88dd04f13d 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -18,12 +18,6 @@ error: unnecessary structure name repetition LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> $DIR/use_self.rs:18:13 - | -LL | Foo::new() - | ^^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> $DIR/use_self.rs:23:25 | @@ -31,25 +25,19 @@ LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:24:13 - | -LL | Foo::new() - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:90:56 + --> $DIR/use_self.rs:94:24 | -LL | fn bad(foos: &[Self]) -> impl Iterator { - | ^^^ help: use the applicable keyword: `Self` +LL | fn bad(foos: &[Foo]) -> impl Iterator { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:105:13 + --> $DIR/use_self.rs:109:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:25 + --> $DIR/use_self.rs:117:25 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -60,7 +48,7 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:114:17 + --> $DIR/use_self.rs:118:17 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` @@ -71,94 +59,82 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:21 + --> $DIR/use_self.rs:141:29 | -LL | fn baz() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | fn bar() -> Bar { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:150:13 + --> $DIR/use_self.rs:142:21 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | Bar { foo: Foo {} } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:29 + --> $DIR/use_self.rs:153:21 | -LL | fn bar() -> Bar { - | ^^^ help: use the applicable keyword: `Self` +LL | fn baz() -> Foo { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:138:21 + --> $DIR/use_self.rs:154:13 | -LL | Bar { foo: Foo {} } - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:171:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:172:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:169:21 + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:200:13 - | -LL | nested::A::fun_1(); - | ^^^^^^^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:201:13 - | -LL | nested::A::A; - | ^^^^^^^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:203:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:254:13 | -LL | TestStruct::from_something() - | ^^^^^^^^^^ help: use the applicable keyword: `Self` +LL | S {} + | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:236:25 + --> $DIR/use_self.rs:282:29 | -LL | async fn g() -> S { - | ^ help: use the applicable keyword: `Self` +LL | fn foo(value: T) -> Foo { + | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:237:13 + --> $DIR/use_self.rs:283:13 | -LL | S {} - | ^ help: use the applicable keyword: `Self` +LL | Foo { value } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:241:16 + --> $DIR/use_self.rs:320:21 | -LL | &p[S::A..S::B] - | ^ help: use the applicable keyword: `Self` +LL | type From = T::From; + | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:241:22 + --> $DIR/use_self.rs:321:19 | -LL | &p[S::A..S::B] - | ^ help: use the applicable keyword: `Self` +LL | type To = T::To; + | ^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 25 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index 1582ae114bf..d425f953a9c 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -33,7 +33,7 @@ impl SelfTrait for Bad { fn nested(_p1: Box, _p2: (&u8, &Self)) {} fn vals(_: Self) -> Self { - Self::default() + Bad::default() } } @@ -47,7 +47,7 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { - Self + Bad } } diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index 4f2506cc119..fa528cc5b7d 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -60,12 +60,6 @@ error: unnecessary structure name repetition LL | fn vals(_: Bad) -> Bad { | ^^^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:36:9 - | -LL | Bad::default() - | ^^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> $DIR/use_self_trait.rs:41:19 | @@ -84,11 +78,5 @@ error: unnecessary structure name repetition LL | fn mul(self, rhs: Bad) -> Bad { | ^^^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:50:9 - | -LL | Bad - | ^^^ help: use the applicable keyword: `Self` - -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From fc334fb8f4cc7e6513578d88f52b2899f624a1de Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 10 Oct 2020 00:26:12 +0200 Subject: use_self - fix issue with `hir_ty_to_ty` --- clippy_lints/src/use_self.rs | 97 ++++++++++++++++++++++++------------------ tests/ui/use_self.fixed | 6 ++- tests/ui/use_self.rs | 4 ++ tests/ui/use_self.stderr | 84 +++++++++++++++++------------------- tests/ui/use_self_trait.fixed | 12 +++--- tests/ui/use_self_trait.stderr | 76 +-------------------------------- 6 files changed, 112 insertions(+), 167 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 1b5070d1cff..023fce947b3 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -57,7 +57,7 @@ declare_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; -fn span_lint<'tcx>(cx: &LateContext<'tcx>, span: Span) { +fn span_lint(cx: &LateContext<'_>, span: Span) { span_lint_and_sugg( cx, USE_SELF, @@ -99,12 +99,12 @@ fn span_lint_on_qpath_resolved<'tcx>(cx: &LateContext<'tcx>, qpath: &'tcx QPath< } } -struct ImplVisitor<'a, 'tcx> { +struct BodyVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, self_ty: Ty<'tcx>, } -impl<'a, 'tcx> ImplVisitor<'a, 'tcx> { +impl<'a, 'tcx> BodyVisitor<'a, 'tcx> { fn check_trait_method_impl_decl( &mut self, impl_item: &ImplItem<'tcx>, @@ -151,46 +151,13 @@ impl<'a, 'tcx> ImplVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for ImplVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind { - match path.res { - def::Res::SelfTy(..) => {}, - _ => { - match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { - Some(Node::Expr(Expr { - kind: ExprKind::Path(QPath::TypeRelative(_, _segment)), - .. - })) => { - // The following block correctly identifies applicable lint locations - // but `hir_ty_to_ty` calls cause odd ICEs. - // - // if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { - // // FIXME: this span manipulation should not be necessary - // // @flip1995 found an ast lowering issue in - // // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162 - // span_lint_until_last_segment(self.cx, hir_ty.span, segment); - // } - }, - _ => { - if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { - span_lint(self.cx, hir_ty.span) - } - }, - } - }, - } - } - - walk_ty(self, hir_ty); - } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { fn expr_ty_matches<'tcx>(expr: &'tcx Expr<'tcx>, self_ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { let def_id = expr.hir_id.owner; @@ -247,6 +214,52 @@ impl<'a, 'tcx> Visitor<'tcx> for ImplVisitor<'a, 'tcx> { } } +struct FnSigVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + self_ty: Ty<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for FnSigVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind { + match path.res { + def::Res::SelfTy(..) => {}, + _ => { + match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { + Some(Node::Expr(Expr { + kind: ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + })) => { + // The following block correctly identifies applicable lint locations + // but `hir_ty_to_ty` calls cause odd ICEs. + // + if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { + // fixme: this span manipulation should not be necessary + // @flip1995 found an ast lowering issue in + // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 + span_lint_until_last_segment(self.cx, hir_ty.span, segment); + } + }, + _ => { + if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { + span_lint(self.cx, hir_ty.span) + } + }, + } + }, + } + } + + walk_ty(self, hir_ty); + } +} + impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { if in_external_macro(cx.sess(), impl_item.span) { @@ -270,7 +283,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // TODO: don't short-circuit upon lifetime parameters if should_check { let self_ty = hir_ty_to_ty(cx.tcx, hir_self_ty); - let visitor = &mut ImplVisitor { cx, self_ty }; + let body_visitor = &mut BodyVisitor { cx, self_ty }; + let fn_sig_visitor = &mut FnSigVisitor { cx, self_ty }; let tcx = cx.tcx; let impl_def_id = tcx.hir().local_def_id(imp.hir_id); @@ -279,11 +293,12 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let Some(impl_trait_ref) = impl_trait_ref; if let ImplItemKind::Fn(FnSig { decl: impl_decl, .. }, impl_body_id) = &impl_item.kind; then { - visitor.check_trait_method_impl_decl(impl_item, impl_decl, impl_trait_ref); + body_visitor.check_trait_method_impl_decl(impl_item, impl_decl, impl_trait_ref); let body = tcx.hir().body(*impl_body_id); - visitor.visit_body(body); + body_visitor.visit_body(body); } else { - walk_impl_item(visitor, impl_item) + walk_impl_item(body_visitor, impl_item); + walk_impl_item(fn_sig_visitor, impl_item); } } } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 916484eef93..d59750cbfd8 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -15,12 +15,14 @@ mod use_self { Self {} } fn test() -> Self { + // FIXME: applicable here Foo::new() } } impl Default for Foo { - fn default() -> Self { + // FIXME: applicable here + fn default() -> Foo { // FIXME: applicable here Foo::new() } @@ -213,7 +215,9 @@ mod rustfix { fn fun_1() {} fn fun_2() { + // FIXME: applicable here nested::A::fun_1(); + // FIXME: applicable here nested::A::A; Self {}; diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 347f5e96555..85606049774 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -15,11 +15,13 @@ mod use_self { Foo {} } fn test() -> Foo { + // FIXME: applicable here Foo::new() } } impl Default for Foo { + // FIXME: applicable here fn default() -> Foo { // FIXME: applicable here Foo::new() @@ -213,7 +215,9 @@ mod rustfix { fn fun_1() {} fn fun_2() { + // FIXME: applicable here nested::A::fun_1(); + // FIXME: applicable here nested::A::A; nested::A {}; diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index a88dd04f13d..4d213316cf5 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,16 +1,16 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:14:21 + --> $DIR/use_self.rs:15:13 | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` | = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:15:13 + --> $DIR/use_self.rs:14:21 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition --> $DIR/use_self.rs:17:22 @@ -19,28 +19,22 @@ LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:25 - | -LL | fn default() -> Foo { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:94:24 + --> $DIR/use_self.rs:96:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:109:13 + --> $DIR/use_self.rs:111:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:117:25 + --> $DIR/use_self.rs:120:17 | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` ... LL | use_self_expand!(); // Should lint in local macros | ------------------- in this macro invocation @@ -48,10 +42,10 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:118:17 + --> $DIR/use_self.rs:119:25 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` ... LL | use_self_expand!(); // Should lint in local macros | ------------------- in this macro invocation @@ -59,82 +53,82 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:141:29 - | -LL | fn bar() -> Bar { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:142:21 + --> $DIR/use_self.rs:144:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:153:21 + --> $DIR/use_self.rs:143:29 | -LL | fn baz() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | fn bar() -> Bar { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:154:13 + --> $DIR/use_self.rs:156:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:171:21 + --> $DIR/use_self.rs:155:21 + | +LL | fn baz() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:172:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:173:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:218:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:254:13 + --> $DIR/use_self.rs:258:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:282:29 + --> $DIR/use_self.rs:287:13 | -LL | fn foo(value: T) -> Foo { - | ^^^^^^ help: use the applicable keyword: `Self` +LL | Foo { value } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:283:13 + --> $DIR/use_self.rs:286:29 | -LL | Foo { value } - | ^^^ help: use the applicable keyword: `Self` +LL | fn foo(value: T) -> Foo { + | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:320:21 + --> $DIR/use_self.rs:324:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:321:19 + --> $DIR/use_self.rs:325:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index d425f953a9c..680a0623839 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -18,21 +18,21 @@ trait SelfTrait { struct Bad; impl SelfTrait for Bad { - fn refs(p1: &Self) -> &Self { + fn refs(p1: &Bad) -> &Bad { p1 } - fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { p1 } - fn mut_refs(p1: &mut Self) -> &mut Self { + fn mut_refs(p1: &mut Bad) -> &mut Bad { p1 } - fn nested(_p1: Box, _p2: (&u8, &Self)) {} + fn nested(_p1: Box, _p2: (&u8, &Bad)) {} - fn vals(_: Self) -> Self { + fn vals(_: Bad) -> Bad { Bad::default() } } @@ -40,7 +40,7 @@ impl SelfTrait for Bad { impl Mul for Bad { type Output = Self; - fn mul(self, rhs: Self) -> Self { + fn mul(self, rhs: Bad) -> Bad { rhs } } diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index fa528cc5b7d..5409ccedf85 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -1,82 +1,10 @@ -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:21:18 - | -LL | fn refs(p1: &Bad) -> &Bad { - | ^^^ help: use the applicable keyword: `Self` - | - = note: `-D clippy::use-self` implied by `-D warnings` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:21:27 - | -LL | fn refs(p1: &Bad) -> &Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:25:33 - | -LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:25:49 - | -LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:29:26 - | -LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:29:39 - | -LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:33:24 - | -LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:33:42 - | -LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:35:16 - | -LL | fn vals(_: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:35:24 - | -LL | fn vals(_: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> $DIR/use_self_trait.rs:41:19 | LL | type Output = Bad; | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:43:23 | -LL | fn mul(self, rhs: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:43:31 - | -LL | fn mul(self, rhs: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` + = note: `-D clippy::use-self` implied by `-D warnings` -error: aborting due to 13 previous errors +error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From ae2dd671f5003e6722ca4d18ef447141db237213 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 16 Oct 2020 13:25:38 +0200 Subject: Rewrite use_self lint one more time This rewrite gets rid of complicated visitors, by using the lint infrastructure as much as possible --- clippy_lints/src/use_self.rs | 604 ++++++++++++++++++++++++------------------- 1 file changed, 344 insertions(+), 260 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 023fce947b3..fbd996ad0e9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,21 +1,22 @@ -use crate::utils; -use crate::utils::snippet_opt; -use crate::utils::span_lint_and_sugg; +use crate::utils::{meets_msrv, qpath_res, snippet_opt, span_lint_and_sugg}; use if_chain::if_chain; + use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::intravisit::{walk_expr, walk_impl_item, walk_ty, NestedVisitorMap, Visitor}; use rustc_hir::{ - def, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, ImplItem, ImplItemKind, ItemKind, Node, Path, PathSegment, + def, + def_id::LocalDefId, + intravisit::{walk_ty, NestedVisitorMap, Visitor}, + Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; -use rustc_middle::ty::Ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_middle::ty::{AssocKind, Ty}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Span}; use rustc_typeck::hir_ty_to_ty; @@ -28,9 +29,11 @@ declare_clippy_lint! { /// feels inconsistent. /// /// **Known problems:** - /// Unaddressed false negatives related to unresolved internal compiler errors. + /// - Unaddressed false negative in fn bodies of trait implementations + /// - False positive with assotiated types in traits (#4140) /// /// **Example:** + /// /// ```rust /// struct Foo {} /// impl Foo { @@ -53,113 +56,216 @@ declare_clippy_lint! { "unnecessary structure name repetition whereas `Self` is applicable" } -declare_lint_pass!(UseSelf => [USE_SELF]); +#[derive(Default)] +pub struct UseSelf { + msrv: Option, + stack: Vec, + types_to_skip: Vec, + types_to_lint: Vec, +} -const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; +const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); -fn span_lint(cx: &LateContext<'_>, span: Span) { - span_lint_and_sugg( - cx, - USE_SELF, - span, - "unnecessary structure name repetition", - "use the applicable keyword", - "Self".to_owned(), - Applicability::MachineApplicable, - ); +impl UseSelf { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + ..Self::default() + } + } } -#[allow(clippy::cast_possible_truncation)] -fn span_lint_until_last_segment<'tcx>(cx: &LateContext<'tcx>, span: Span, segment: &'tcx PathSegment<'tcx>) { - let sp = span.with_hi(segment.ident.span.lo()); - // remove the trailing :: - let span_without_last_segment = match snippet_opt(cx, sp) { - Some(snippet) => match snippet.rfind("::") { - Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)), - None => sp, - }, - None => sp, - }; - span_lint(cx, span_without_last_segment); +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum StackItem { + Check { + hir_id: HirId, + impl_trait_ref_def_id: Option, + }, + NoCheck, } -fn span_lint_on_path_until_last_segment<'tcx>(cx: &LateContext<'tcx>, path: &'tcx Path<'tcx>) { - if path.segments.len() > 1 { - span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap()); +impl_lint_pass!(UseSelf => [USE_SELF]); + +const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; + +impl<'tcx> LateLintPass<'tcx> for UseSelf { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // We push the self types of `impl`s on a stack here. Only the top type on the stack is + // relevant for linting, since this is the self type of the `impl` we're currently in. To + // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that + // we're in an `impl` or nested item, that we don't want to lint + // + // NB: If you push something on the stack in this method, remember to also pop it in the + // `check_item_post` method. + match &item.kind { + ItemKind::Impl(Impl { + self_ty: hir_self_ty, + of_trait, + .. + }) => { + let should_check = if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind { + let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; + parameters.as_ref().map_or(true, |params| { + !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) + }) + } else { + false + }; + let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id)); + if should_check { + self.stack.push(StackItem::Check { + hir_id: hir_self_ty.hir_id, + impl_trait_ref_def_id, + }); + } else { + self.stack.push(StackItem::NoCheck); + } + }, + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::Fn(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) => { + self.stack.push(StackItem::NoCheck); + }, + _ => (), + } } -} -fn span_lint_on_qpath_resolved<'tcx>(cx: &LateContext<'tcx>, qpath: &'tcx QPath<'tcx>, until_last_segment: bool) { - if let QPath::Resolved(_, path) = qpath { - if until_last_segment { - span_lint_on_path_until_last_segment(cx, path); - } else { - span_lint(cx, path.span); + fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { + use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union}; + match item.kind { + Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => { + self.stack.pop(); + }, + _ => (), } } -} -struct BodyVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - self_ty: Ty<'tcx>, -} + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { + // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait + // declaration. The collection of those types is all this method implementation does. + if_chain! { + if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind; + if let Some(StackItem::Check { impl_trait_ref_def_id: Some(def_id), .. }) = self.stack.last().copied(); + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id); + then { + // `self_ty` is the semantic self type of `impl for `. This cannot be + // `Self`. + let self_ty = impl_trait_ref.self_ty(); + + // `trait_method_sig` is the signature of the function, how it is declared in the + // trait, not in the impl of the trait. + let trait_method = cx + .tcx + .associated_items(impl_trait_ref.def_id) + .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id) + .expect("impl method matches a trait method"); + let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id); + let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); + + // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the + // implementation of the trait. + let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output { + Some(&**ty) + } else { + None + }; + let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty); + + // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. + // + // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the + // trait declaration. This is used to check if `Self` was used in the trait + // declaration. + // + // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed + // to `Self`), we want to skip linting that type and all subtypes of it. This + // avoids suggestions to e.g. replace `Vec` with `Vec`, in an `impl Trait + // for u8`, when the trait always uses `Vec`. + // + // See also https://github.com/rust-lang/rust-clippy/issues/2894. + for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { + if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { + let mut visitor = SkipTyCollector::default(); + visitor.visit_ty(&impl_hir_ty); + self.types_to_skip.extend(visitor.types_to_skip); + } + } + } + } + } -impl<'a, 'tcx> BodyVisitor<'a, 'tcx> { - fn check_trait_method_impl_decl( - &mut self, - impl_item: &ImplItem<'tcx>, - impl_decl: &'tcx FnDecl<'tcx>, - impl_trait_ref: ty::TraitRef<'tcx>, - ) { - let tcx = self.cx.tcx; - let trait_method = tcx - .associated_items(impl_trait_ref.def_id) - .find_by_name_and_kind(tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id) - .expect("impl method matches a trait method"); - - let trait_method_sig = tcx.fn_sig(trait_method.def_id); - let trait_method_sig = tcx.erase_late_bound_regions(&trait_method_sig); - - let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output { - Some(&**ty) - } else { - None - }; + fn check_impl_item_post(&mut self, _: &LateContext<'_>, _: &hir::ImplItem<'_>) { + self.types_to_skip.clear(); + } - // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. - // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait. - // We use `impl_hir_ty` to see if the type was written as `Self`, - // `hir_ty_to_ty(...)` to check semantic types of paths, and - // `trait_ty` to determine which parts of the signature in the trait, mention - // the type being implemented verbatim (as opposed to `Self`). - for (impl_hir_ty, trait_ty) in impl_decl - .inputs - .iter() - .chain(output_hir_ty) - .zip(trait_method_sig.inputs_and_output) - { - // Check if the input/output type in the trait method specifies the implemented - // type verbatim, and only suggest `Self` if that isn't the case. - // This avoids suggestions to e.g. replace `Vec` with `Vec`, - // in an `impl Trait for u8`, when the trait always uses `Vec`. - // See also https://github.com/rust-lang/rust-clippy/issues/2894. - let self_ty = impl_trait_ref.self_ty(); - if !trait_ty.walk().any(|inner| inner == self_ty.into()) { - self.visit_ty(&impl_hir_ty); - } + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) { + // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies + // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`. + // However the `node_type()` method can *only* be called in bodies. + // + // This method implementation determines which types should get linted in a `Body` and + // which shouldn't, with a visitor. We could directly lint in the visitor, but then we + // could only allow this lint on item scope. And we would have to check if those types are + // already dealt with in `check_ty` anyway. + if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { + let self_ty = ty_from_hir_id(cx, *hir_id); + + let mut visitor = LintTyCollector { + cx, + self_ty, + types_to_lint: vec![], + types_to_skip: vec![], + }; + visitor.visit_expr(&body.value); + self.types_to_lint.extend(visitor.types_to_lint); + self.types_to_skip.extend(visitor.types_to_skip); } } -} -impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) { + self.types_to_lint.clear(); + } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + if in_external_macro(cx.sess(), hir_ty.span) + | in_impl(cx, hir_ty) + | self.types_to_skip.contains(&hir_ty.hir_id) + | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) + { + return; + } + + let lint_dependend_on_expr_kind = || { + // FIXME: this span manipulation should not be necessary + // @flip1995 found an ast lowering issue in + // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 + match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { + Some(Node::Expr(Expr { + kind: ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + })) => span_lint_until_last_segment(cx, hir_ty.span, segment), + _ => span_lint(cx, hir_ty.span), + } + }; + + if self.types_to_lint.contains(&hir_ty.hir_id) { + lint_dependend_on_expr_kind(); + } else if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { + let self_ty = ty_from_hir_id(cx, *hir_id); + + if should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) { + lint_dependend_on_expr_kind(); + } + } } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - fn expr_ty_matches<'tcx>(expr: &'tcx Expr<'tcx>, self_ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool { let def_id = expr.hir_id.owner; if cx.tcx.has_typeck_results(def_id) { cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty) @@ -167,205 +273,183 @@ impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { false } } - match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => { - if expr_ty_matches(expr, self.self_ty, self.cx) { - match path.res { - def::Res::SelfTy(..) => (), - def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(self.cx, path), - _ => { - span_lint(self.cx, path.span); - }, + + if in_external_macro(cx.sess(), expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + return; + } + + if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { + let self_ty = ty_from_hir_id(cx, *hir_id); + + match &expr.kind { + ExprKind::Struct(QPath::Resolved(_, path), ..) => { + if expr_ty_matches(cx, expr, self_ty) { + match path.res { + def::Res::SelfTy(..) => (), + def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path), + _ => { + span_lint(cx, path.span); + }, + } } - } - }, - // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) - ExprKind::Call(fun, _) => { - if let Expr { - kind: ExprKind::Path(ref qpath), - .. - } = fun - { - if expr_ty_matches(expr, self.self_ty, self.cx) { - let res = utils::qpath_res(self.cx, qpath, fun.hir_id); - - if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res { - match ctor_of { - def::CtorOf::Variant => { - span_lint_on_qpath_resolved(self.cx, qpath, true); - }, - def::CtorOf::Struct => { - span_lint_on_qpath_resolved(self.cx, qpath, false); - }, + }, + // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) + ExprKind::Call(fun, _) => { + if let Expr { + kind: ExprKind::Path(ref qpath), + .. + } = fun + { + if expr_ty_matches(cx, expr, self_ty) { + let res = qpath_res(cx, qpath, fun.hir_id); + + if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res { + match ctor_of { + def::CtorOf::Variant => { + span_lint_on_qpath_resolved(cx, qpath, true); + }, + def::CtorOf::Struct => { + span_lint_on_qpath_resolved(cx, qpath, false); + }, + } } } } - } - }, - // unit enum variants (`Enum::A`) - ExprKind::Path(ref qpath) => { - if expr_ty_matches(expr, self.self_ty, self.cx) { - span_lint_on_qpath_resolved(self.cx, qpath, true); - } - }, - _ => (), + }, + // unit enum variants (`Enum::A`) + ExprKind::Path(qpath) => { + if expr_ty_matches(cx, expr, self_ty) { + span_lint_on_qpath_resolved(cx, &qpath, true); + } + }, + _ => (), + } } - walk_expr(self, expr); } + + extract_msrv_attr!(LateContext); } -struct FnSigVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - self_ty: Ty<'tcx>, +#[derive(Default)] +struct SkipTyCollector { + types_to_skip: Vec, } -impl<'a, 'tcx> Visitor<'tcx> for FnSigVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for SkipTyCollector { type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } + fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) { + self.types_to_skip.push(hir_ty.hir_id); - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind { - match path.res { - def::Res::SelfTy(..) => {}, - _ => { - match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { - Some(Node::Expr(Expr { - kind: ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - })) => { - // The following block correctly identifies applicable lint locations - // but `hir_ty_to_ty` calls cause odd ICEs. - // - if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { - // fixme: this span manipulation should not be necessary - // @flip1995 found an ast lowering issue in - // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 - span_lint_until_last_segment(self.cx, hir_ty.span, segment); - } - }, - _ => { - if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { - span_lint(self.cx, hir_ty.span) - } - }, - } - }, - } - } + walk_ty(self, hir_ty) + } - walk_ty(self, hir_ty); + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None } } -impl<'tcx> LateLintPass<'tcx> for UseSelf { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if in_external_macro(cx.sess(), impl_item.span) { - return; - } +struct LintTyCollector<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + self_ty: Ty<'tcx>, + types_to_lint: Vec, + types_to_skip: Vec, +} - let parent_id = cx.tcx.hir().get_parent_item(impl_item.hir_id); - let imp = cx.tcx.hir().expect_item(parent_id); +impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> { + type Map = Map<'tcx>; + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) { if_chain! { - if let ItemKind::Impl { self_ty: hir_self_ty, .. } = imp.kind; - if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind; + if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id); + if should_lint_ty(hir_ty, ty, self.self_ty); 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| matches!(arg, GenericArg::Lifetime(_))) - ); - - // TODO: don't short-circuit upon lifetime parameters - if should_check { - let self_ty = hir_ty_to_ty(cx.tcx, hir_self_ty); - let body_visitor = &mut BodyVisitor { cx, self_ty }; - let fn_sig_visitor = &mut FnSigVisitor { cx, self_ty }; - - let tcx = cx.tcx; - let impl_def_id = tcx.hir().local_def_id(imp.hir_id); - let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); - if_chain! { - if let Some(impl_trait_ref) = impl_trait_ref; - if let ImplItemKind::Fn(FnSig { decl: impl_decl, .. }, impl_body_id) = &impl_item.kind; - then { - body_visitor.check_trait_method_impl_decl(impl_item, impl_decl, impl_trait_ref); - let body = tcx.hir().body(*impl_body_id); - body_visitor.visit_body(body); - } else { - walk_impl_item(body_visitor, impl_item); - walk_impl_item(fn_sig_visitor, impl_item); - } - } - } + self.types_to_lint.push(hir_ty.hir_id); + } else { + self.types_to_skip.push(hir_ty.hir_id); } } + + walk_ty(self, hir_ty) + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None } - extract_msrv_attr!(LateContext); } -struct UseSelfVisitor<'a, 'tcx> { - item_path: &'a Path<'a>, - cx: &'a LateContext<'tcx>, +fn span_lint(cx: &LateContext<'_>, span: Span) { + span_lint_and_sugg( + cx, + USE_SELF, + span, + "unnecessary structure name repetition", + "use the applicable keyword", + "Self".to_owned(), + Applicability::MachineApplicable, + ); } -impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> { - type Map = Map<'tcx>; +#[allow(clippy::cast_possible_truncation)] +fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) { + let sp = span.with_hi(segment.ident.span.lo()); + // remove the trailing :: + let span_without_last_segment = match snippet_opt(cx, sp) { + Some(snippet) => match snippet.rfind("::") { + Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)), + None => sp, + }, + None => sp, + }; + span_lint(cx, span_without_last_segment); +} - fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { - if !path.segments.iter().any(|p| p.ident.span.is_dummy()) { - if path.segments.len() >= 2 { - let last_but_one = &path.segments[path.segments.len() - 2]; - if last_but_one.ident.name != kw::SelfUpper { - let enum_def_id = match path.res { - Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id), - Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => { - let variant_def_id = self.cx.tcx.parent(ctor_def_id); - variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id)) - }, - _ => None, - }; - - if self.item_path.res.opt_def_id() == enum_def_id { - span_use_self_lint(self.cx, path, Some(last_but_one)); - } - } - } +fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) { + if path.segments.len() > 1 { + span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap()); + } +} - if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper { - if self.item_path.res == path.res { - span_use_self_lint(self.cx, path, None); - } else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res { - if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) { - span_use_self_lint(self.cx, path, None); - } - } - } +fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) { + if let QPath::Resolved(_, path) = qpath { + if until_last_segment { + span_lint_on_path_until_last_segment(cx, path); + } else { + span_lint(cx, path.span); } + } +} - walk_path(self, path); +fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> { + if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) { + hir_ty_to_ty(cx.tcx, hir_ty) + } else { + unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`") } +} - fn visit_item(&mut self, item: &'tcx Item<'_>) { - match item.kind { - ItemKind::Use(..) - | ItemKind::Static(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::Union(..) - | ItemKind::Impl { .. } - | ItemKind::Fn(..) => { - // Don't check statements that shadow `Self` or where `Self` can't be used - }, - _ => walk_item(self, item), +fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool { + let map = cx.tcx.hir(); + let parent = map.get_parent_node(hir_ty.hir_id); + if_chain! { + if let Some(Node::Item(item)) = map.find(parent); + if let ItemKind::Impl { .. } = item.kind; + then { + true + } else { + false } } +} - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.cx.tcx.hir()) +fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { + if_chain! { + if ty == self_ty; + if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; + then { + !matches!(path.res, def::Res::SelfTy(..)) + } else { + false + } } } -- cgit 1.4.1-3-g733a5 From da65d8166fe50a817a49d542460986234e94dc6d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Jan 2021 19:09:24 +0100 Subject: Don't trigger use_self in macros --- clippy_lints/src/use_self.rs | 7 ++- tests/ui/auxiliary/proc_macro_derive.rs | 12 +++++ tests/ui/use_self.fixed | 13 ++++-- tests/ui/use_self.rs | 9 +++- tests/ui/use_self.stderr | 82 ++++++++++++--------------------- 5 files changed, 63 insertions(+), 60 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index fbd996ad0e9..3dfec190541 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,4 +1,4 @@ -use crate::utils::{meets_msrv, qpath_res, snippet_opt, span_lint_and_sugg}; +use crate::utils::{in_macro, meets_msrv, qpath_res, snippet_opt, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -13,7 +13,6 @@ 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::{AssocKind, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -232,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if in_external_macro(cx.sess(), hir_ty.span) + if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | self.types_to_skip.contains(&hir_ty.hir_id) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) @@ -274,7 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - if in_external_macro(cx.sess(), expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { return; } diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 24891682d36..aebeaf34679 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -41,3 +41,15 @@ pub fn derive_foo(_input: TokenStream) -> TokenStream { } } } + +#[proc_macro_derive(StructAUseSelf)] +pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream { + quote! { + struct A; + impl A { + fn new() -> A { + A + } + } + } +} diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 1632e6aca44..95e7bc75431 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,10 +1,14 @@ // run-rustfix // edition:2018 +// aux-build:proc_macro_derive.rs #![warn(clippy::use_self)] #![allow(dead_code)] #![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)] +#[macro_use] +extern crate proc_macro_derive; + fn main() {} mod use_self { @@ -109,8 +113,8 @@ mod tuple_structs { mod macros { macro_rules! use_self_expand { () => { - fn new() -> Self { - Self {} + fn new() -> Foo { + Foo {} } }; } @@ -118,8 +122,11 @@ mod macros { struct Foo {} impl Foo { - use_self_expand!(); // Should lint in local macros + use_self_expand!(); // Should not lint in local macros } + + #[derive(StructAUseSelf)] // Should not lint in derives + struct A; } mod nesting { diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index bbe92c9e338..75424f34159 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,10 +1,14 @@ // run-rustfix // edition:2018 +// aux-build:proc_macro_derive.rs #![warn(clippy::use_self)] #![allow(dead_code)] #![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)] +#[macro_use] +extern crate proc_macro_derive; + fn main() {} mod use_self { @@ -118,8 +122,11 @@ mod macros { struct Foo {} impl Foo { - use_self_expand!(); // Should lint in local macros + use_self_expand!(); // Should not lint in local macros } + + #[derive(StructAUseSelf)] // Should not lint in derives + struct A; } mod nesting { diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index d86453eb2f0..37dfef7cfe0 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:14:21 + --> $DIR/use_self.rs:18:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,194 +7,172 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:15:13 + --> $DIR/use_self.rs:19:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:17:22 + --> $DIR/use_self.rs:21:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:18:13 + --> $DIR/use_self.rs:22:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:25 + --> $DIR/use_self.rs:27:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:24:13 + --> $DIR/use_self.rs:28:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:24 + --> $DIR/use_self.rs:93:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:55 + --> $DIR/use_self.rs:93:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:104:13 + --> $DIR/use_self.rs:108:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:25 - | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` -... -LL | use_self_expand!(); // Should lint in local macros - | ------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:17 - | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` -... -LL | use_self_expand!(); // Should lint in local macros - | ------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:136:29 + --> $DIR/use_self.rs:143:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:21 + --> $DIR/use_self.rs:144:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:155:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:13 + --> $DIR/use_self.rs:156:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:166:21 + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:210:13 + --> $DIR/use_self.rs:217:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:211:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:213:13 + --> $DIR/use_self.rs:220:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:232:13 + --> $DIR/use_self.rs:239:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:246:25 + --> $DIR/use_self.rs:253:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:247:13 + --> $DIR/use_self.rs:254:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:251:16 + --> $DIR/use_self.rs:258:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:251:22 + --> $DIR/use_self.rs:258:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:274:29 + --> $DIR/use_self.rs:281:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:275:13 + --> $DIR/use_self.rs:282:13 | LL | Foo { value } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:312:21 + --> $DIR/use_self.rs:319:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:313:19 + --> $DIR/use_self.rs:320:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:450:13 + --> $DIR/use_self.rs:457:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: aborting due to 31 previous errors +error: aborting due to 29 previous errors -- cgit 1.4.1-3-g733a5 From 7e1c1c154166d9eaa4184eab166b540e17ff26f3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 9 Feb 2021 19:38:40 -0600 Subject: Fix qpath_res call --- clippy_lints/src/use_self.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 3dfec190541..cdaa08be86b 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,4 +1,4 @@ -use crate::utils::{in_macro, meets_msrv, qpath_res, snippet_opt, span_lint_and_sugg}; +use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } = fun { if expr_ty_matches(cx, expr, self_ty) { - let res = qpath_res(cx, qpath, fun.hir_id); + let res = cx.qpath_res(qpath, fun.hir_id); if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res { match ctor_of { -- cgit 1.4.1-3-g733a5 From 7f61ddd5b85f5e2340ef238476139df8a837afb1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 9 Feb 2021 19:39:38 -0600 Subject: Move "types to lint" to the item stack --- clippy_lints/src/use_self.rs | 73 +++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 32 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index cdaa08be86b..8c83ad5650d 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -59,8 +59,6 @@ declare_clippy_lint! { pub struct UseSelf { msrv: Option, stack: Vec, - types_to_skip: Vec, - types_to_lint: Vec, } const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); @@ -75,11 +73,13 @@ impl UseSelf { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug)] enum StackItem { Check { hir_id: HirId, impl_trait_ref_def_id: Option, + types_to_skip: Vec, + types_to_lint: Vec, }, NoCheck, } @@ -116,6 +116,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { self.stack.push(StackItem::Check { hir_id: hir_self_ty.hir_id, impl_trait_ref_def_id, + types_to_lint: Vec::new(), + types_to_skip: Vec::new(), }); } else { self.stack.push(StackItem::NoCheck); @@ -149,7 +151,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // declaration. The collection of those types is all this method implementation does. if_chain! { if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind; - if let Some(StackItem::Check { impl_trait_ref_def_id: Some(def_id), .. }) = self.stack.last().copied(); + if let Some(&mut StackItem::Check { + impl_trait_ref_def_id: Some(def_id), + ref mut types_to_skip, + .. + }) = self.stack.last_mut(); if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id); then { // `self_ty` is the semantic self type of `impl for `. This cannot be @@ -191,17 +197,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); visitor.visit_ty(&impl_hir_ty); - self.types_to_skip.extend(visitor.types_to_skip); + types_to_skip.extend(visitor.types_to_skip); } } } } } - fn check_impl_item_post(&mut self, _: &LateContext<'_>, _: &hir::ImplItem<'_>) { - self.types_to_skip.clear(); - } - fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) { // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`. @@ -211,7 +213,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // which shouldn't, with a visitor. We could directly lint in the visitor, but then we // could only allow this lint on item scope. And we would have to check if those types are // already dealt with in `check_ty` anyway. - if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { + if let Some(StackItem::Check { + hir_id, + types_to_lint, + types_to_skip, + .. + }) = self.stack.last_mut() + { let self_ty = ty_from_hir_id(cx, *hir_id); let mut visitor = LintTyCollector { @@ -221,25 +229,36 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { types_to_skip: vec![], }; visitor.visit_expr(&body.value); - self.types_to_lint.extend(visitor.types_to_lint); - self.types_to_skip.extend(visitor.types_to_skip); + types_to_lint.extend(visitor.types_to_lint); + types_to_skip.extend(visitor.types_to_skip); } } - fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) { - self.types_to_lint.clear(); - } - fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if in_macro(hir_ty.span) - | in_impl(cx, hir_ty) - | self.types_to_skip.contains(&hir_ty.hir_id) - | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) - { + if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { return; } - let lint_dependend_on_expr_kind = || { + let lint_dependend_on_expr_kind = if let Some(StackItem::Check { + hir_id, + types_to_lint, + types_to_skip, + .. + }) = self.stack.last() + { + if types_to_skip.contains(&hir_ty.hir_id) { + false + } else if types_to_lint.contains(&hir_ty.hir_id) { + true + } else { + let self_ty = ty_from_hir_id(cx, *hir_id); + should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) + } + } else { + false + }; + + if lint_dependend_on_expr_kind { // FIXME: this span manipulation should not be necessary // @flip1995 found an ast lowering issue in // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 @@ -250,16 +269,6 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { })) => span_lint_until_last_segment(cx, hir_ty.span, segment), _ => span_lint(cx, hir_ty.span), } - }; - - if self.types_to_lint.contains(&hir_ty.hir_id) { - lint_dependend_on_expr_kind(); - } else if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { - let self_ty = ty_from_hir_id(cx, *hir_id); - - if should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) { - lint_dependend_on_expr_kind(); - } } } -- cgit 1.4.1-3-g733a5 From 52f98d832dc5ca982b9edad7807b2a9a9ddd13e0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 9 Feb 2021 19:42:32 -0600 Subject: Use TyS::same_type --- clippy_lints/src/use_self.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 8c83ad5650d..a0dd53af661 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -13,7 +13,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{AssocKind, Ty}; +use rustc_middle::ty::{AssocKind, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Span}; @@ -452,7 +452,7 @@ fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool { fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if_chain! { - if ty == self_ty; + if TyS::same_type(ty, self_ty); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; then { !matches!(path.res, def::Res::SelfTy(..)) -- cgit 1.4.1-3-g733a5 From 94b8f23baf045b16e2a8b2e2cdd7436724bbf8d5 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Thu, 11 Feb 2021 00:45:28 +0900 Subject: Fix typo --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4cb3a858511..d3db9c666df 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1503,7 +1503,7 @@ declare_clippy_lint! { /// /// ```rust /// // Bad - /// let _ = "Hello".bytes().nth(3);; + /// let _ = "Hello".bytes().nth(3); /// /// // Good /// let _ = "Hello".as_bytes().get(3); -- cgit 1.4.1-3-g733a5 From b80ac2af9c0939428054e7d018f19fc16b920b05 Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Wed, 10 Feb 2021 21:47:04 +0200 Subject: Added boilerplate --- CHANGELOG.md | 1 + clippy_lints/src/from_str_radix_10.rs | 34 ++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++++ tests/ui/from_str_radix_10.rs | 5 +++++ 4 files changed, 44 insertions(+) create mode 100644 clippy_lints/src/from_str_radix_10.rs create mode 100644 tests/ui/from_str_radix_10.rs (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dcf9c3529b..56b74a7d0ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2092,6 +2092,7 @@ Released 2018-09-13 [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs new file mode 100644 index 00000000000..ec2a60ec47c --- /dev/null +++ b/clippy_lints/src/from_str_radix_10.rs @@ -0,0 +1,34 @@ +use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_ast::ast::*; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)` + /// + /// **Why is this bad?** + /// This specific common use case can be rewritten as `s.parse::()` + /// (and in most cases, the turbofish can be removed), which reduces code length + /// and complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let input: &str = get_input(); + /// let num = u16::from_str_radix(input, 10)?; + /// ``` + /// Use instead: + /// ```rust + /// let input: &str = get_input(); + /// let num: u16 = input.parse()?; + /// ``` + pub FROM_STR_RADIX_10, + style, + "default lint description" +} + +declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); + +impl EarlyLintPass for FromStrRadix10 {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d96911fac1a..5b84422458f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -210,6 +210,7 @@ mod floating_point_arithmetic; mod format; mod formatting; mod from_over_into; +mod from_str_radix_10; mod functions; mod future_not_send; mod get_last_with_len; @@ -637,6 +638,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &formatting::SUSPICIOUS_ELSE_FORMATTING, &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, &from_over_into::FROM_OVER_INTO, + &from_str_radix_10::FROM_STR_RADIX_10, &functions::DOUBLE_MUST_USE, &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, @@ -1468,6 +1470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&from_over_into::FROM_OVER_INTO), + LintId::of(&from_str_radix_10::FROM_STR_RADIX_10), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), @@ -1724,6 +1727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&from_over_into::FROM_OVER_INTO), + LintId::of(&from_str_radix_10::FROM_STR_RADIX_10), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::RESULT_UNIT_ERR), diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs new file mode 100644 index 00000000000..70eaa8d666c --- /dev/null +++ b/tests/ui/from_str_radix_10.rs @@ -0,0 +1,5 @@ +#![warn(clippy::from_str_radix_10)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From 64729390a1b2b6b05f8a4407658163ddff4d017e Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 02:30:37 +0200 Subject: Implemented majority of from_str_radix_10 --- clippy_lints/src/from_str_radix_10.rs | 59 ++++++++++++++++++++++++++++++++--- clippy_lints/src/lib.rs | 1 + tests/ui/from_str_radix_10.rs | 32 +++++++++++++++++-- 3 files changed, 86 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index ec2a60ec47c..612ea9ae62c 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,10 @@ -use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_lint::{LateLintPass, LateContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_ast::ast::*; +use rustc_hir::*; +use rustc_errors::Applicability; +use if_chain::if_chain; + +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** @@ -26,9 +30,56 @@ declare_clippy_lint! { /// ``` pub FROM_STR_RADIX_10, style, - "default lint description" + "from_str_radix with radix 10" } declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); -impl EarlyLintPass for FromStrRadix10 {} +impl LateLintPass<'tcx> for FromStrRadix10 { + fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { + if_chain! { + if let ExprKind::Call(maybe_path, arguments) = &exp.kind; + if let ExprKind::Path(qpath) = &maybe_path.kind; + if let QPath::TypeRelative(ty, pathseg) = &qpath; + + // check if the first part of the path is some integer primitive + if let TyKind::Path(ty_qpath) = &ty.kind; + let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); + if let def::Res::PrimTy(prim_ty) = ty_res; + if is_primitive_integer_ty(prim_ty); + + // check if the second part of the path indeed calls the associated + // function `from_str_radix` + if pathseg.ident.name.as_str() == "from_str_radix"; + + // check if the second argument resolves to a constant `10` + if arguments.len() == 2; + if is_constant_10(&arguments[1]); + + then { + span_lint_and_sugg( + cx, + FROM_STR_RADIX_10, + exp.span, + "This call to `from_str_radix` can be shortened to a call to str::parse", + "try", + format!("TODO"), + Applicability::MachineApplicable + ); + } + } + } +} + +fn is_primitive_integer_ty(ty: PrimTy) -> bool { + match ty { + PrimTy::Int(_) => true, + PrimTy::Uint(_) => true, + _ => false + } +} + +fn is_constant_10<'tcx>(expr: &Expr<'tcx>) -> bool { + // TODO + true +} \ No newline at end of file diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5b84422458f..dae686b1229 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1258,6 +1258,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_late_pass(|| box redundant_slicing::RedundantSlicing); + store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 70eaa8d666c..086616d09ff 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -1,5 +1,33 @@ #![warn(clippy::from_str_radix_10)] -fn main() { - // test code goes here +mod some_mod { + // fake function that shouldn't trigger the lint + pub fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> { + unimplemented!() + } } + +// fake function that shouldn't trigger the lint +fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> { + unimplemented!() +} + +fn main() -> Result<(), Box> { + // all of these should trigger the lint + u32::from_str_radix("30", 10)?; + i64::from_str_radix("24", 10)?; + isize::from_str_radix("100", 10)?; + u8::from_str_radix("7", 10)?; + + // none of these should trigger the lint + u16::from_str_radix("20", 3)?; + i32::from_str_radix("45", 12)?; + usize::from_str_radix("10", 16)?; + i128::from_str_radix("10", 13)?; + some_mod::from_str_radix("50", 10)?; + some_mod::from_str_radix("50", 6)?; + from_str_radix("50", 10)?; + from_str_radix("50", 6)?; + + Ok(()) +} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From a389c02461107a71da042c1a9dbf4c0e597df3b4 Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 12:30:23 +0200 Subject: from_str_radix_10 should be done --- clippy_lints/src/from_str_radix_10.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 612ea9ae62c..57661e0f9bc 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -52,18 +52,20 @@ impl LateLintPass<'tcx> for FromStrRadix10 { // function `from_str_radix` if pathseg.ident.name.as_str() == "from_str_radix"; - // check if the second argument resolves to a constant `10` + // check if the second argument is a primitive `10` if arguments.len() == 2; - if is_constant_10(&arguments[1]); + if let ExprKind::Lit(lit) = &arguments[1].kind; + if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; then { + let orig_string = crate::utils::snippet(cx, arguments[0].span, "string"); span_lint_and_sugg( cx, FROM_STR_RADIX_10, exp.span, "This call to `from_str_radix` can be shortened to a call to str::parse", "try", - format!("TODO"), + format!("({}).parse()", orig_string), Applicability::MachineApplicable ); } @@ -77,9 +79,4 @@ fn is_primitive_integer_ty(ty: PrimTy) -> bool { PrimTy::Uint(_) => true, _ => false } -} - -fn is_constant_10<'tcx>(expr: &Expr<'tcx>) -> bool { - // TODO - true } \ No newline at end of file -- cgit 1.4.1-3-g733a5 From 0b31b470ad743fc758a1c47a17d7241948cd4a3b Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 12:42:20 +0200 Subject: Changed applicability to MaybeIncorrect because of surrounding braces --- clippy_lints/src/from_str_radix_10.rs | 2 +- tests/ui/from_str_radix_10.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 57661e0f9bc..9371104bf9b 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -66,7 +66,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { "This call to `from_str_radix` can be shortened to a call to str::parse", "try", format!("({}).parse()", orig_string), - Applicability::MachineApplicable + Applicability::MaybeIncorrect ); } } diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 086616d09ff..795e795fcb3 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -19,6 +19,9 @@ fn main() -> Result<(), Box> { isize::from_str_radix("100", 10)?; u8::from_str_radix("7", 10)?; + let string = "300"; + i32::from_str_radix(string, 10)?; + // none of these should trigger the lint u16::from_str_radix("20", 3)?; i32::from_str_radix("45", 12)?; -- cgit 1.4.1-3-g733a5 From d1a627ab3bd801879a565cc9e68a322d8d28bcbf Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 12:46:11 +0200 Subject: Ran bless and rustfmt --- clippy_lints/src/from_str_radix_10.rs | 14 +++++++------- tests/ui/from_str_radix_10.rs | 2 +- tests/ui/from_str_radix_10.stderr | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 tests/ui/from_str_radix_10.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 9371104bf9b..9a8d4542616 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,8 +1,8 @@ -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; -use rustc_errors::Applicability; 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}; use crate::utils::span_lint_and_sugg; @@ -16,7 +16,7 @@ declare_clippy_lint! { /// and complexity. /// /// **Known problems:** None. - /// + /// /// **Example:** /// /// ```rust @@ -77,6 +77,6 @@ fn is_primitive_integer_ty(ty: PrimTy) -> bool { match ty { PrimTy::Int(_) => true, PrimTy::Uint(_) => true, - _ => false + _ => false, } -} \ No newline at end of file +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 795e795fcb3..2d8106da7ba 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -33,4 +33,4 @@ fn main() -> Result<(), Box> { from_str_radix("50", 6)?; Ok(()) -} \ No newline at end of file +} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr new file mode 100644 index 00000000000..376d0dd56ea --- /dev/null +++ b/tests/ui/from_str_radix_10.stderr @@ -0,0 +1,34 @@ +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:17:5 + | +LL | u32::from_str_radix("30", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("30").parse()` + | + = note: `-D clippy::from-str-radix-10` implied by `-D warnings` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:18:5 + | +LL | i64::from_str_radix("24", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("24").parse()` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:19:5 + | +LL | isize::from_str_radix("100", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("100").parse()` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:20:5 + | +LL | u8::from_str_radix("7", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("7").parse()` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:23:5 + | +LL | i32::from_str_radix(string, 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(string).parse()` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 9194c11d69aa98c9d85fd0979bf9b93d3d7de809 Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 13:15:06 +0200 Subject: Fixed doctests that shouldn't have been compiled --- clippy_lints/src/from_str_radix_10.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 9a8d4542616..53cb1e3ecd9 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -19,12 +19,12 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust + /// ```ignore /// let input: &str = get_input(); /// let num = u16::from_str_radix(input, 10)?; /// ``` /// Use instead: - /// ```rust + /// ```ignore /// let input: &str = get_input(); /// let num: u16 = input.parse()?; /// ``` -- cgit 1.4.1-3-g733a5 From 642efabfbbba6577a3698a305bac8ce0c693e67f Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Fri, 12 Feb 2021 11:53:52 +0200 Subject: Fixed typos and updated to matches! where applicable --- clippy_lints/src/from_str_radix_10.rs | 12 ++---------- tests/ui/from_str_radix_10.stderr | 10 +++++----- 2 files changed, 7 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 53cb1e3ecd9..de9add4b6b6 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -46,7 +46,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { if let TyKind::Path(ty_qpath) = &ty.kind; let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); if let def::Res::PrimTy(prim_ty) = ty_res; - if is_primitive_integer_ty(prim_ty); + if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)); // check if the second part of the path indeed calls the associated // function `from_str_radix` @@ -63,7 +63,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { cx, FROM_STR_RADIX_10, exp.span, - "This call to `from_str_radix` can be shortened to a call to str::parse", + "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", format!("({}).parse()", orig_string), Applicability::MaybeIncorrect @@ -72,11 +72,3 @@ impl LateLintPass<'tcx> for FromStrRadix10 { } } } - -fn is_primitive_integer_ty(ty: PrimTy) -> bool { - match ty { - PrimTy::Int(_) => true, - PrimTy::Uint(_) => true, - _ => false, - } -} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index 376d0dd56ea..5557cd3b9ef 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -1,4 +1,4 @@ -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:17:5 | LL | u32::from_str_radix("30", 10)?; @@ -6,25 +6,25 @@ LL | u32::from_str_radix("30", 10)?; | = note: `-D clippy::from-str-radix-10` implied by `-D warnings` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:18:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("24").parse()` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:19:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("100").parse()` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:20:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("7").parse()` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:23:5 | LL | i32::from_str_radix(string, 10)?; -- cgit 1.4.1-3-g733a5 From d36fe8556962467545f5e92bf896b6672f4e88ae Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Sat, 13 Feb 2021 00:33:08 +0200 Subject: Made parens addition smarter and added tests with bless --- clippy_lints/src/from_str_radix_10.rs | 11 +++++++++-- tests/ui/from_str_radix_10.rs | 13 +++++++++++++ tests/ui/from_str_radix_10.stderr | 34 +++++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 13 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index de9add4b6b6..993b85ed998 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -5,6 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::span_lint_and_sugg; +use crate::utils::sugg::Sugg; declare_clippy_lint! { /// **What it does:** @@ -58,14 +59,20 @@ impl LateLintPass<'tcx> for FromStrRadix10 { if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; then { - let orig_string = crate::utils::snippet(cx, arguments[0].span, "string"); + let sugg = Sugg::hir_with_applicability( + cx, + &arguments[0], + "", + &mut Applicability::MachineApplicable + ).maybe_par(); + span_lint_and_sugg( cx, FROM_STR_RADIX_10, exp.span, "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", - format!("({}).parse()", orig_string), + format!("{}.parse::<{}>()", sugg, prim_ty.name_str()), Applicability::MaybeIncorrect ); } diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 2d8106da7ba..0a973128664 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -12,12 +12,25 @@ fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> { unimplemented!() } +// to test parenthesis addition +struct Test; + +impl std::ops::Add for Test { + type Output = &'static str; + + fn add(self, _: Self) -> Self::Output { + "304" + } +} + fn main() -> Result<(), Box> { // all of these should trigger the lint u32::from_str_radix("30", 10)?; i64::from_str_radix("24", 10)?; isize::from_str_radix("100", 10)?; u8::from_str_radix("7", 10)?; + u16::from_str_radix(&("10".to_owned() + "5"), 10)?; + i128::from_str_radix(Test + Test, 10)?; let string = "300"; i32::from_str_radix(string, 10)?; diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index 5557cd3b9ef..d6669001915 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -1,34 +1,46 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:17:5 + --> $DIR/from_str_radix_10.rs:28:5 | LL | u32::from_str_radix("30", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("30").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` | = note: `-D clippy::from-str-radix-10` implied by `-D warnings` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:18:5 + --> $DIR/from_str_radix_10.rs:29:5 | LL | i64::from_str_radix("24", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("24").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:19:5 + --> $DIR/from_str_radix_10.rs:30:5 | LL | isize::from_str_radix("100", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("100").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:20:5 + --> $DIR/from_str_radix_10.rs:31:5 | LL | u8::from_str_radix("7", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("7").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:23:5 + --> $DIR/from_str_radix_10.rs:32:5 + | +LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&("10".to_owned() + "5")).parse::()` + +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> $DIR/from_str_radix_10.rs:33:5 + | +LL | i128::from_str_radix(Test + Test, 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` + +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> $DIR/from_str_radix_10.rs:36:5 | LL | i32::from_str_radix(string, 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(string).parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 12025506d61bc05a10455ae96a623515663c09fe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 14 Feb 2021 16:21:12 +0100 Subject: Do not lint when the closure is called using an iterator --- clippy_lints/src/blocks_in_if_conditions.rs | 18 +++++++++++++++++- tests/ui/blocks_in_if_conditions_closure.rs | 11 ++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 4efca10bcdf..b53f80fd8bc 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,4 +1,8 @@ -use crate::utils::{differing_macro_contexts, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{ + differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths, + snippet_block_with_applicability, span_lint, span_lint_and_sugg, +}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BlockCheckMode, Expr, ExprKind}; @@ -52,6 +56,18 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { + // do not lint if the closure is called using an iterator (see #1141) + if_chain! { + if let Some(parent) = get_parent_expr(self.cx, expr); + if let ExprKind::MethodCall(_, _, args, _) = parent.kind; + let caller = self.cx.typeck_results().expr_ty(&args[0]); + if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR); + if implements_trait(self.cx, caller, iter_id, &[]); + then { + return; + } + } + let body = self.cx.tcx.hir().body(eid); let ex = &body.value; if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { diff --git a/tests/ui/blocks_in_if_conditions_closure.rs b/tests/ui/blocks_in_if_conditions_closure.rs index acbabfa20d7..2856943b9be 100644 --- a/tests/ui/blocks_in_if_conditions_closure.rs +++ b/tests/ui/blocks_in_if_conditions_closure.rs @@ -44,4 +44,13 @@ fn macro_in_closure() { } } -fn main() {} +#[rustfmt::skip] +fn main() { + let mut range = 0..10; + range.all(|i| {i < 10} ); + + let v = vec![1, 2, 3]; + if v.into_iter().any(|x| {x == 4}) { + println!("contains 4!"); + } +} -- cgit 1.4.1-3-g733a5 From 93796b2346a1177809628ea141f0dd958f4c4d29 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Feb 2021 14:22:31 +0900 Subject: Add some restrictions to default_numeric_fallback to avoid FNs --- clippy_lints/src/default_numeric_fallback.rs | 171 +++++++++++++++++++++------ clippy_lints/src/lib.rs | 2 +- tests/ui/default_numeric_fallback.rs | 40 +++++++ tests/ui/default_numeric_fallback.stderr | 50 +++++++- 4 files changed, 218 insertions(+), 45 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index b3b5a4a8251..3102d032057 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,9 +1,14 @@ -use rustc_ast::{LitFloatType, LitIntType, LitKind}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind}; +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; +use rustc_hir::{ + intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, + Body, Expr, ExprKind, Lit, Stmt, StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, FloatTy, IntTy}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_middle::{ + hir::map::Map, + ty::{self, FloatTy, IntTy, Ty}, +}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use if_chain::if_chain; @@ -33,53 +38,141 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// let i = 10i32; - /// let f: f64 = 1.23; + /// let f = 1.23f64; /// ``` pub DEFAULT_NUMERIC_FALLBACK, restriction, "usage of unconstrained numeric literals which may cause default numeric fallback." } -#[derive(Default)] -pub struct DefaultNumericFallback { - /// Hold `init` in `Local` if `Local` has a type annotation. - bounded_inits: FxHashSet, +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); + +impl LateLintPass<'_> for DefaultNumericFallback { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + let mut visitor = NumericFallbackVisitor::new(cx); + visitor.visit_body(body); + } } -impl_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); +struct NumericFallbackVisitor<'a, 'tcx> { + /// Stack manages type bound of exprs. The top element holds current expr type. + ty_bounds: Vec>, -impl LateLintPass<'_> for DefaultNumericFallback { - fn check_stmt(&mut self, _: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if local.ty.is_some(); - if let Some(init) = local.init; - then { - self.bounded_inits.insert(init.hir_id); - } + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + ty_bounds: vec![TyBound::Nothing], + cx, } } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let expr_ty = cx.typeck_results().expr_ty(expr); - let hir_id = expr.hir_id; + /// Check whether a passed literal has potential to cause fallback or not. + fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { + let ty_bound = self.ty_bounds.last().unwrap(); if_chain! { - if let ExprKind::Lit(ref lit) = expr.kind; - if matches!(lit.node, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if matches!(expr_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); - if !self.bounded_inits.contains(&hir_id); - if !cx.tcx.hir().parent_iter(hir_id).any(|(ref hir_id, _)| self.bounded_inits.contains(hir_id)); - then { - span_lint_and_help( - cx, - DEFAULT_NUMERIC_FALLBACK, - lit.span, - "default numeric fallback might occur", - None, - "consider adding suffix to avoid default numeric fallback", - ) - } + if matches!(lit.node, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); + if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); + if !ty_bound.is_integral(); + then { + span_lint_and_help( + self.cx, + DEFAULT_NUMERIC_FALLBACK, + lit.span, + "default numeric fallback might occur", + None, + "consider adding suffix to avoid default numeric fallback", + ); + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + #[allow(clippy::too_many_lines)] + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + ExprKind::Call(func, args) => { + if_chain! { + if let ExprKind::Path(ref func_path) = func.kind; + if let Some(def_id) = self.cx.qpath_res(func_path, func.hir_id).opt_def_id(); + then { + let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); + for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) { + // Push found arg type, then visit arg. + self.ty_bounds.push(TyBound::Ty(bound)); + self.visit_expr(expr); + self.ty_bounds.pop(); + } + return; + } + } + }, + + ExprKind::MethodCall(_, _, args, _) => { + if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { + let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); + for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) { + self.ty_bounds.push(TyBound::Ty(bound)); + self.visit_expr(expr); + self.ty_bounds.pop(); + } + return; + } + }, + + ExprKind::Lit(lit) => { + let ty = self.cx.typeck_results().expr_ty(expr); + self.check_lit(lit, ty); + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + match stmt.kind { + StmtKind::Local(local) => { + if local.ty.is_some() { + self.ty_bounds.push(TyBound::Any) + } else { + self.ty_bounds.push(TyBound::Nothing) + } + }, + + _ => self.ty_bounds.push(TyBound::Nothing), + } + + walk_stmt(self, stmt); + self.ty_bounds.pop(); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +#[derive(Debug, Clone, Copy)] +enum TyBound<'ctx> { + Any, + Ty(Ty<'ctx>), + Nothing, +} + +impl<'ctx> TyBound<'ctx> { + fn is_integral(self) -> bool { + match self { + TyBound::Any => true, + TyBound::Ty(t) => t.is_integral(), + TyBound::Nothing => false, } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6590613b93a..642c1e68dac 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1028,7 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback::default()); + store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 5ca5bc1e907..420c1f0c931 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -1,12 +1,46 @@ #![warn(clippy::default_numeric_fallback)] #![allow(unused)] +#![allow(clippy::never_loop)] +#![allow(clippy::no_effect)] +#![allow(clippy::unnecessary_operation)] + +fn concrete_arg(x: i32) {} + +fn generic_arg(t: T) {} + +struct ConcreteStruct { + x: i32, +} + +struct StructForMethodCallTest { + x: i32, +} + +impl StructForMethodCallTest { + fn concrete_arg(&self, x: i32) {} + + fn generic_arg(&self, t: T) {} +} fn main() { + let s = StructForMethodCallTest { x: 10_i32 }; + // Bad. let x = 1; let x = 0.1; + let x = if true { 1 } else { 2 }; + let x: _ = { + let y = 1; + 1 + }; + + generic_arg(10); + s.generic_arg(10); + let x: _ = generic_arg(10); + let x: _ = s.generic_arg(10); + // Good. let x = 1_i32; let x: i32 = 1; @@ -14,5 +48,11 @@ fn main() { let x = 0.1_f64; let x: f64 = 0.1; let x: _ = 0.1; + let x: _ = if true { 1 } else { 2 }; + + concrete_arg(10); + s.concrete_arg(10); + let x = concrete_arg(10); + let x = s.concrete_arg(10); } diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index c36409a052c..cb7c174ad8d 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:6:13 + --> $DIR/default_numeric_fallback.rs:29:13 | LL | let x = 1; | ^ @@ -8,7 +8,7 @@ LL | let x = 1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:7:13 + --> $DIR/default_numeric_fallback.rs:30:13 | LL | let x = 0.1; | ^^^ @@ -16,7 +16,7 @@ LL | let x = 0.1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:8:23 + --> $DIR/default_numeric_fallback.rs:32:23 | LL | let x = if true { 1 } else { 2 }; | ^ @@ -24,12 +24,52 @@ LL | let x = if true { 1 } else { 2 }; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:8:34 + --> $DIR/default_numeric_fallback.rs:32:34 | LL | let x = if true { 1 } else { 2 }; | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 4 previous errors +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:35:17 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:39:17 + | +LL | generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:40:19 + | +LL | s.generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:41:28 + | +LL | let x: _ = generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:42:30 + | +LL | let x: _ = s.generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 0198ac7bdd807abeb8f6b423c22b1e594fb0b4bf Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Feb 2021 22:40:28 +0900 Subject: Add fn_sig_opt to get fn signature from HirId --- clippy_lints/src/default_numeric_fallback.rs | 37 ++++++++++++++++------------ 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3102d032057..b6730afa4af 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,12 +1,12 @@ use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, - Body, Expr, ExprKind, Lit, Stmt, StmtKind, + Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::{ hir::map::Map, - ty::{self, FloatTy, IntTy, Ty}, + ty::{self, FloatTy, IntTy, PolyFnSig, Ty}, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,15 +64,15 @@ struct NumericFallbackVisitor<'a, 'tcx> { impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { - ty_bounds: vec![TyBound::Nothing], + ty_bounds: Vec::new(), cx, } } /// Check whether a passed literal has potential to cause fallback or not. fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { - let ty_bound = self.ty_bounds.last().unwrap(); if_chain! { + if let Some(ty_bound) = self.ty_bounds.last(); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); @@ -98,19 +98,14 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Call(func, args) => { - if_chain! { - if let ExprKind::Path(ref func_path) = func.kind; - if let Some(def_id) = self.cx.qpath_res(func_path, func.hir_id).opt_def_id(); - then { - let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); - for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) { - // Push found arg type, then visit arg. - self.ty_bounds.push(TyBound::Ty(bound)); - self.visit_expr(expr); - self.ty_bounds.pop(); - } - return; + if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { + for (expr, bound) in args.iter().zip(fn_sig.skip_binder().inputs().iter()) { + // Push found arg type, then visit arg. + self.ty_bounds.push(TyBound::Ty(bound)); + self.visit_expr(expr); + self.ty_bounds.pop(); } + return; } }, @@ -160,6 +155,16 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { } } +fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option> { + let node_ty = cx.typeck_results().node_type_opt(hir_id)?; + // We can't use `TyS::fn_sig` because it automatically performs substs, this may result in FNs. + match node_ty.kind() { + ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)), + ty::FnPtr(fn_sig) => Some(*fn_sig), + _ => None, + } +} + #[derive(Debug, Clone, Copy)] enum TyBound<'ctx> { Any, -- cgit 1.4.1-3-g733a5 From fb91c76586a3a01fef6a8c49edb9e4598fbd138f Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Feb 2021 23:33:27 +0900 Subject: Add more tests for default_numeric_fallback --- clippy_lints/src/default_numeric_fallback.rs | 2 +- tests/ui/default_numeric_fallback.rs | 112 +++++++++++++++++-------- tests/ui/default_numeric_fallback.stderr | 118 +++++++++++++++++++++------ 3 files changed, 169 insertions(+), 63 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index b6730afa4af..d755112a178 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// **Why is this bad?** For those who are very careful about types, default numeric fallback /// can be a pitfall that cause unexpected runtime behavior. /// - /// **Known problems:** None. + /// **Known problems:** This lint can only be allowed at the function level or above. /// /// **Example:** /// ```rust diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 420c1f0c931..c4881094469 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -4,55 +4,97 @@ #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] -fn concrete_arg(x: i32) {} +mod basic_expr { + fn test() { + // Should lint unsuffixed literals typed `i32`. + let x = 22; + let x = [1, 2, 3]; + let x = if true { (1, 2) } else { (3, 4) }; + let x = match 1 { + 1 => 1, + _ => 2, + }; -fn generic_arg(t: T) {} + // Should lint unsuffixed literals typed `f64`. + let x = 0.12; -struct ConcreteStruct { - x: i32, + // Should NOT lint suffixed literals. + let x = 22_i32; + let x = 0.12_f64; + + // Should NOT lint literals in init expr if `Local` has a type annotation. + let x: f64 = 0.1; + let x: [i32; 3] = [1, 2, 3]; + let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; + let x: _ = 1; + } } -struct StructForMethodCallTest { - x: i32, +mod nested_local { + fn test() { + let x: _ = { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; + } } -impl StructForMethodCallTest { - fn concrete_arg(&self, x: i32) {} +mod function_def { + fn ret_i32() -> i32 { + // Even though the output type is specified, + // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. + 23 + } - fn generic_arg(&self, t: T) {} + fn test() { + // Should lint this because return type is inferred to `i32` and NOT bound to a concrete + // type. + let f = || -> _ { 1 }; + + // Even though the output type is specified, + // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. + let f = || -> i32 { 1 }; + } } -fn main() { - let s = StructForMethodCallTest { x: 10_i32 }; +mod function_calls { + fn concrete_arg(x: i32) {} + + fn generic_arg(t: T) {} - // Bad. - let x = 1; - let x = 0.1; + fn test() { + // Should NOT lint this because the argument type is bound to a concrete type. + concrete_arg(1); - let x = if true { 1 } else { 2 }; + // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type. + generic_arg(1); - let x: _ = { - let y = 1; - 1 - }; + // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type. + let x: _ = generic_arg(1); + } +} + +mod method_calls { + struct StructForMethodCallTest {} - generic_arg(10); - s.generic_arg(10); - let x: _ = generic_arg(10); - let x: _ = s.generic_arg(10); + impl StructForMethodCallTest { + fn concrete_arg(&self, x: i32) {} - // Good. - let x = 1_i32; - let x: i32 = 1; - let x: _ = 1; - let x = 0.1_f64; - let x: f64 = 0.1; - let x: _ = 0.1; + fn generic_arg(&self, t: T) {} + } - let x: _ = if true { 1 } else { 2 }; + fn test() { + let s = StructForMethodCallTest {}; - concrete_arg(10); - s.concrete_arg(10); - let x = concrete_arg(10); - let x = s.concrete_arg(10); + // Should NOT lint this because the argument type is bound to a concrete type. + s.concrete_arg(1); + + // Should lint this because the argument type is bound to a concrete type. + s.generic_arg(1); + } } + +fn main() {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index cb7c174ad8d..c71d05d7993 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,75 +1,139 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:29:13 + --> $DIR/default_numeric_fallback.rs:10:17 | -LL | let x = 1; - | ^ +LL | let x = 22; + | ^^ | = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:30:13 + --> $DIR/default_numeric_fallback.rs:11:18 + | +LL | let x = [1, 2, 3]; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:11:21 + | +LL | let x = [1, 2, 3]; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:11:24 | -LL | let x = 0.1; - | ^^^ +LL | let x = [1, 2, 3]; + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:32:23 + --> $DIR/default_numeric_fallback.rs:12:28 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:12:31 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:12:44 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ | -LL | let x = if true { 1 } else { 2 }; + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:12:47 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:13:23 + | +LL | let x = match 1 { | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:32:34 + --> $DIR/default_numeric_fallback.rs:14:13 | -LL | let x = if true { 1 } else { 2 }; - | ^ +LL | 1 => 1, + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:35:17 + --> $DIR/default_numeric_fallback.rs:14:18 | -LL | let y = 1; - | ^ +LL | 1 => 1, + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:39:17 + --> $DIR/default_numeric_fallback.rs:15:18 | -LL | generic_arg(10); - | ^^ +LL | _ => 2, + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:40:19 + --> $DIR/default_numeric_fallback.rs:19:17 | -LL | s.generic_arg(10); - | ^^ +LL | let x = 0.12; + | ^^^^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:41:28 + --> $DIR/default_numeric_fallback.rs:37:21 | -LL | let x: _ = generic_arg(10); - | ^^ +LL | let y = 1; + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:42:30 + --> $DIR/default_numeric_fallback.rs:73:21 | -LL | let x: _ = s.generic_arg(10); - | ^^ +LL | generic_arg(1); + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:76:32 + | +LL | let x: _ = generic_arg(1); + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:96:23 + | +LL | s.generic_arg(1); + | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 9 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 1f8ee3cc19f9259eea5e633affef968280d2ed1a Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 16 Feb 2021 11:20:51 +0900 Subject: Handle struct ctor case --- clippy_lints/src/default_numeric_fallback.rs | 53 +++++++++++++++++++++-- tests/ui/default_numeric_fallback.rs | 37 +++++++++++++++- tests/ui/default_numeric_fallback.stderr | 64 ++++++++++++++++++++++++++-- 3 files changed, 145 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index d755112a178..33a577f336d 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -64,7 +64,7 @@ struct NumericFallbackVisitor<'a, 'tcx> { impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { - ty_bounds: Vec::new(), + ty_bounds: vec![TyBound::Nothing], cx, } } @@ -121,6 +121,42 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { } }, + ExprKind::Struct(qpath, fields, base) => { + if_chain! { + if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(); + let ty = self.cx.tcx.type_of(def_id); + if let Some(adt_def) = ty.ty_adt_def(); + if adt_def.is_struct(); + if let Some(variant) = adt_def.variants.iter().next(); + then { + let fields_def = &variant.fields; + + // Push field type then visit each field expr. + for field in fields.iter() { + let bound = + fields_def + .iter() + .find_map(|f_def| { + if f_def.ident == field.ident + { Some(self.cx.tcx.type_of(f_def.did)) } + else { None } + }); + self.ty_bounds.push(bound.into()); + self.visit_expr(field.expr); + self.ty_bounds.pop(); + } + + // Visit base with no bound. + if let Some(base) = base { + self.ty_bounds.push(TyBound::Nothing); + self.visit_expr(base); + self.ty_bounds.pop(); + } + return; + } + } + }, + ExprKind::Lit(lit) => { let ty = self.cx.typeck_results().expr_ty(expr); self.check_lit(lit, ty); @@ -166,13 +202,13 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { +enum TyBound<'tcx> { Any, - Ty(Ty<'ctx>), + Ty(Ty<'tcx>), Nothing, } -impl<'ctx> TyBound<'ctx> { +impl<'tcx> TyBound<'tcx> { fn is_integral(self) -> bool { match self { TyBound::Any => true, @@ -181,3 +217,12 @@ impl<'ctx> TyBound<'ctx> { } } } + +impl<'tcx> From>> for TyBound<'tcx> { + fn from(v: Option>) -> Self { + match v { + Some(t) => TyBound::Ty(t), + None => TyBound::Nothing, + } + } +} diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index c4881094469..0b3758952ac 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -39,6 +39,20 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 1 }; + + let x: _ = if true { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + } else { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 2 + }; } } @@ -46,7 +60,7 @@ mod function_def { fn ret_i32() -> i32 { // Even though the output type is specified, // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. - 23 + 1 } fn test() { @@ -77,6 +91,27 @@ mod function_calls { } } +mod struct_ctor { + struct ConcreteStruct { + x: i32, + } + + struct GenericStruct { + x: T, + } + + fn test() { + // Should NOT lint this because the field type is bound to a concrete type. + ConcreteStruct { x: 1 }; + + // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type. + GenericStruct { x: 1 }; + + // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type. + let _ = GenericStruct { x: 1 }; + } +} + mod method_calls { struct StructForMethodCallTest {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index c71d05d7993..50fcdfb510e 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -112,7 +112,47 @@ LL | let y = 1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:73:21 + --> $DIR/default_numeric_fallback.rs:45:21 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:51:21 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:63:9 + | +LL | 1 + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:69:27 + | +LL | let f = || -> _ { 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:73:29 + | +LL | let f = || -> i32 { 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:87:21 | LL | generic_arg(1); | ^ @@ -120,7 +160,7 @@ LL | generic_arg(1); = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:76:32 + --> $DIR/default_numeric_fallback.rs:90:32 | LL | let x: _ = generic_arg(1); | ^ @@ -128,12 +168,28 @@ LL | let x: _ = generic_arg(1); = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:96:23 + --> $DIR/default_numeric_fallback.rs:108:28 + | +LL | GenericStruct { x: 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:111:36 + | +LL | let _ = GenericStruct { x: 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:131:23 | LL | s.generic_arg(1); | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 17 previous errors +error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 9b0c1ebc183bbf0fab5fbbe5ac8b2f94e5e56b87 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 16 Feb 2021 18:07:09 +0900 Subject: Change to span_lint_and_sugg from span_lint_and_help --- clippy_lints/src/default_numeric_fallback.rs | 19 ++++-- tests/ui/default_numeric_fallback.stderr | 95 +++++++--------------------- 2 files changed, 38 insertions(+), 76 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 33a577f336d..6ace9aa6bdf 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,4 +1,5 @@ use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; +use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, @@ -12,7 +13,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use if_chain::if_chain; -use crate::utils::span_lint_and_help; +use crate::utils::{snippet, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type @@ -75,16 +76,24 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { if let Some(ty_bound) = self.ty_bounds.last(); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); if !ty_bound.is_integral(); then { - span_lint_and_help( + let suffix = match lit_ty.kind() { + ty::Int(IntTy::I32) => "i32", + ty::Float(FloatTy::F64) => "f64", + // Default numeric fallback never results in other types. + _ => return, + }; + + let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix); + span_lint_and_sugg( self.cx, DEFAULT_NUMERIC_FALLBACK, lit.span, "default numeric fallback might occur", - None, - "consider adding suffix to avoid default numeric fallback", + "consider adding suffix", + sugg, + Applicability::MaybeIncorrect, ); } } diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index 50fcdfb510e..b31aa4ebcf8 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -2,194 +2,147 @@ error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:10:17 | LL | let x = 22; - | ^^ + | ^^ help: consider adding suffix: `22_i32` | = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` - = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:11:18 | LL | let x = [1, 2, 3]; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:11:21 | LL | let x = [1, 2, 3]; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:11:24 | LL | let x = [1, 2, 3]; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:13:23 | LL | let x = match 1 { - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:14:13 | LL | 1 => 1, - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:14:18 | LL | 1 => 1, - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:15:18 | LL | _ => 2, - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:19:17 | LL | let x = 0.12; - | ^^^^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:37:21 | LL | let y = 1; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:45:21 | LL | let y = 1; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:51:21 | LL | let y = 1; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:63:9 | LL | 1 - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:69:27 | LL | let f = || -> _ { 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:73:29 | LL | let f = || -> i32 { 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:87:21 | LL | generic_arg(1); - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:90:32 | LL | let x: _ = generic_arg(1); - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:108:28 | LL | GenericStruct { x: 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:111:36 | LL | let _ = GenericStruct { x: 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:131:23 | LL | s.generic_arg(1); - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 4ac14f9e63ba71e7f15204f9cff208f022563996 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 17 Feb 2021 09:34:35 -0600 Subject: Teach SpanlessEq binding IDs --- clippy_lints/src/utils/hir_utils.rs | 134 ++++++++++++++++++++++++------------ tests/ui/collapsible_match.rs | 8 +++ tests/ui/if_same_then_else2.rs | 6 +- tests/ui/if_same_then_else2.stderr | 4 +- 4 files changed, 102 insertions(+), 50 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index c5870dc5124..0a9719901f0 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -1,10 +1,12 @@ use crate::consts::{constant_context, constant_simple}; use crate::utils::differing_macro_contexts; use rustc_ast::ast::InlineAsmTemplatePiece; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::def::Res; use rustc_hir::{ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy, - GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, + GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lint::LateContext; @@ -52,8 +54,47 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - /// Checks whether two statements are the same. - pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + /// Use this method to wrap comparisons that may involve inter-expression context. + /// See `self.locals`. + fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { + HirEqInterExpr { + inner: self, + locals: FxHashMap::default(), + } + } + + pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { + self.inter_expr().eq_block(left, right) + } + + pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + self.inter_expr().eq_expr(left, right) + } + + pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { + self.inter_expr().eq_path_segment(left, right) + } + + pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { + self.inter_expr().eq_path_segments(left, right) + } + + pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { + self.inter_expr().eq_ty_kind(left, right) + } +} + +struct HirEqInterExpr<'a, 'b, 'tcx> { + inner: &'a mut SpanlessEq<'b, 'tcx>, + + // When binding are declared, the binding ID in the left expression is mapped to the one on the + // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, + // these blocks are considered equal since `x` is mapped to `y`. + locals: FxHashMap, +} + +impl HirEqInterExpr<'_, '_, '_> { + fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { self.eq_pat(&l.pat, &r.pat) @@ -68,21 +109,21 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } /// Checks whether two blocks are the same. - pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { + fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) } #[allow(clippy::similar_names)] - pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { + fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } - if let Some(typeck_results) = self.maybe_typeck_results { + if let Some(typeck_results) = self.inner.maybe_typeck_results { if let (Some(l), Some(r)) = ( - constant_simple(self.cx, typeck_results, left), - constant_simple(self.cx, typeck_results, right), + constant_simple(self.inner.cx, typeck_results, left), + constant_simple(self.inner.cx, typeck_results, right), ) { if l == r { return true; @@ -98,10 +139,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { - self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.inner.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)) => { - self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.inner.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)) => { @@ -116,7 +157,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.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + self.inner.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)) => { @@ -139,19 +180,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { ls == rs && self.eq_expr(le, re) && over(la, ra, |l, r| { - self.eq_expr(&l.body, &r.body) + self.eq_pat(&l.pat, &r.pat) && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r)) - && self.eq_pat(&l.pat, &r.pat) + && self.eq_expr(&l.body, &r.body) }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { - self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + self.inner.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)); - let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body)); - let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); + let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(ll_id.body)); + let ll = celcx.expr(&self.inner.cx.tcx.hir().body(ll_id.body).value); + let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(rl_id.body)); + let rl = celcx.expr(&self.inner.cx.tcx.hir().body(rl_id.body).value); self.eq_expr(le, re) && ll == rl }, @@ -168,7 +209,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), _ => false, }; - is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right)) + is_eq || self.inner.expr_fallback.as_ref().map_or(false, |f| f(left, right)) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { @@ -199,13 +240,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { left.name == right.name } - pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { + fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); li.name == ri.name && self.eq_pat(lp, rp) } /// Checks whether two patterns are the same. - pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { + fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => { @@ -214,8 +255,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, - (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { - lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r)) + (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { + let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r)); + if eq { + self.locals.insert(li, ri); + } + eq }, (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), @@ -251,8 +296,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { - left.is_global() == right.is_global() - && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)) + match (left.res, right.res) { + (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r), + (Res::Local(_), _) | (_, Res::Local(_)) => false, + _ => over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)), + } } fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { @@ -279,28 +327,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { self.eq_ty_kind(&left.kind, &right.kind) } #[allow(clippy::similar_names)] - pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { + fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { match (left, right) { (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { - let old_maybe_typeck_results = self.maybe_typeck_results; - - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); - self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(ll_id.body)); - let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); - - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body)); - self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(rl_id.body)); - let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); - - let eq_ty = self.eq_ty(lt, rt); - self.maybe_typeck_results = old_maybe_typeck_results; - eq_ty && ll == rl + let cx = self.inner.cx; + let eval_const = + |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); + self.eq_ty(lt, rt) && eval_const(ll_id.body) == eval_const(rl_id.body) }, (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) @@ -667,10 +706,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } - pub fn hash_path(&mut self, p: &Path<'_>) { - p.is_global().hash(&mut self.s); - for p in p.segments { - self.hash_name(p.ident.name); + pub fn hash_path(&mut self, path: &Path<'_>) { + match path.res { + // constant hash since equality is dependant on inter-expression context + Res::Local(_) => 1_usize.hash(&mut self.s), + _ => { + for seg in path.segments { + self.hash_name(seg.ident.name); + } + }, } } diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 3294da7e814..55467cf4229 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -232,6 +232,14 @@ fn negative_cases(res_opt: Result, String>, res_res: Result match e { + Some(e) => e, + e => e, + }, + // else branch looks the same but the binding is different + e => e, + }; } fn make() -> T { diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index e83ce47e563..a2ff1b741ca 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -12,7 +12,7 @@ fn if_same_then_else2() -> Result<&'static str, ()> { if true { for _ in &[42] { let foo: &Option<_> = &Some::(42); - if true { + if foo.is_some() { break; } else { continue; @@ -21,8 +21,8 @@ fn if_same_then_else2() -> Result<&'static str, ()> { } else { //~ ERROR same body as `if` block for _ in &[42] { - let foo: &Option<_> = &Some::(42); - if true { + let bar: &Option<_> = &Some::(42); + if bar.is_some() { break; } else { continue; diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index f98e30fa376..454322d8aac 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -5,7 +5,7 @@ LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block LL | | for _ in &[42] { -LL | | let foo: &Option<_> = &Some::(42); +LL | | let bar: &Option<_> = &Some::(42); ... | LL | | } LL | | } @@ -19,7 +19,7 @@ LL | if true { | _____________^ LL | | for _ in &[42] { LL | | let foo: &Option<_> = &Some::(42); -LL | | if true { +LL | | if foo.is_some() { ... | LL | | } LL | | } else { -- cgit 1.4.1-3-g733a5 From 742922a41a07653ec85b81a3be2c5788707e10d9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 17 Feb 2021 09:35:55 -0600 Subject: Make expr_fallback FnMut --- clippy_lints/src/utils/hir_utils.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 0a9719901f0..c042990b759 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -26,7 +26,7 @@ pub struct SpanlessEq<'a, 'tcx> { cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, allow_side_effects: bool, - expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, + expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -47,7 +47,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { + pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { Self { expr_fallback: Some(Box::new(expr_fallback)), ..self @@ -209,7 +209,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), _ => false, }; - is_eq || self.inner.expr_fallback.as_ref().map_or(false, |f| f(left, right)) + is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { -- cgit 1.4.1-3-g733a5 From 9ad6e263c9eec118a37cdbd5e182afaaad42840a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 17 Feb 2021 10:02:22 -0600 Subject: Fix match_same_arms with SpanlessEq changes --- clippy_lints/src/matches.rs | 113 ++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 63 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e33001b16bc..efc8b139425 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -3,19 +3,19 @@ use crate::utils::sugg::Sugg; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local_id, - peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt, - snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, - strip_pat_refs, + is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local, + path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, + snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, strip_pat_refs, }; 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_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat, + Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, PatKind, QPath, RangeEnd, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -24,7 +24,7 @@ use rustc_middle::ty::{self, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::{sym, Symbol}; +use rustc_span::sym; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -1873,13 +1873,6 @@ 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); @@ -1891,12 +1884,38 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { let min_index = usize::min(lindex, rindex); let max_index = usize::max(lindex, rindex); + let mut local_map: FxHashMap = FxHashMap::default(); + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + if_chain! { + if let Some(a_id) = path_to_local(a); + if let Some(b_id) = path_to_local(b); + let entry = match local_map.entry(a_id) { + Entry::Vacant(entry) => entry, + // check if using the same bindings as before + Entry::Occupied(entry) => return *entry.get() == b_id, + }; + // the names technically don't have to match; this makes the lint more conservative + if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id); + if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b)); + if pat_contains_local(lhs.pat, a_id); + if pat_contains_local(rhs.pat, b_id); + then { + entry.insert(b_id); + true + } else { + false + } + } + }; // 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)) + (min_index..=max_index).all(|index| arms[index].guard.is_none()) + && SpanlessEq::new(cx) + .expr_fallback(eq_fallback) + .eq_expr(&lhs.body, &rhs.body) + // these checks could be removed to allow unused bindings + && bindings_eq(lhs.pat, local_map.keys().copied().collect()) + && bindings_eq(rhs.pat, local_map.values().copied().collect()) }; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); @@ -1939,50 +1958,18 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { } } -/// 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); +fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool { + let mut result = false; + pat.walk_short(|p| { + result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id); + !result + }); result } + +/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa +fn bindings_eq(pat: &Pat<'_>, mut ids: FxHashSet) -> bool { + let mut result = true; + pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id)); + result && ids.is_empty() +} -- cgit 1.4.1-3-g733a5 From 6165cccf7e43cf4ca2234e84dcf7060c7d89ef45 Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Wed, 17 Feb 2021 16:41:50 -0500 Subject: Added detailled suggs for new case --- clippy_lints/src/unnecessary_wraps.rs | 100 ++++++++++++++++------------------ tests/ui/unnecessary_wraps.stderr | 62 +++++++++++++++++---- 2 files changed, 97 insertions(+), 65 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 7da42ba3934..b097d531bc4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,13 +1,13 @@ use crate::utils::{ - contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_sugg, - span_lint_and_then, visitors::find_all_ret_expressions, + contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -85,33 +85,23 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } } - // Check if return type is Option or Result. If neither, abort. - let return_ty = return_ty(cx, hir_id); - let (return_type_label, path) = if is_type_diagnostic_item(cx, return_ty, sym::option_type) { - ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty, sym::result_type) { - ("Result", &paths::RESULT_OK) - } else { - return; - }; - - // Take the first inner type of the Option or Result. If can't, abort. - let inner_ty = if_chain! { - // Skip Option or Result and take the first outermost inner type. - if let Some(inner_ty) = return_ty.walk().nth(1); - if let GenericArgKind::Type(inner_ty) = inner_ty.unpack(); - then { - inner_ty + // Get the wrapper and inner types, if can't, abort. + let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { + if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) { + ("Option", &paths::OPTION_SOME, subst.type_at(0)) + } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) { + ("Result", &paths::RESULT_OK, subst.type_at(0)) } else { return; } + } else { + return; }; // Check if all return expression respect the following condition and collect them. let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { - // Abort if in macro. if !in_macro(ret_expr.span); // Check if a function call. if let ExprKind::Call(ref func, ref args) = ret_expr.kind; @@ -119,12 +109,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. if match_qpath(qpath, path); - // Make sure the function call has only one argument. if args.len() == 1; // Make sure the function argument does not contain a return expression. if !contains_return(&args[0]); then { - suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); + suggs.push( + ( + ret_expr.span, + if inner_type.is_unit() { + "".to_string() + } else { + snippet(cx, args[0].span.source_callsite(), "..").to_string() + } + ) + ); true } else { false @@ -133,42 +131,36 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { }); if can_sugg && !suggs.is_empty() { - // Issue 6640: If the inner type is Unit, emit lint similar to clippy::unused_unit. - if inner_ty.is_unit() { - span_lint_and_sugg( - cx, - UNNECESSARY_WRAPS, - fn_decl.output.span(), - "unneeded wrapped unit return type", - format!("remove the `-> {}<[...]>`", return_type_label).as_str(), - String::new(), - Applicability::MaybeIncorrect, - ); + let (lint_msg, return_type_suggestion_msg, return_type_suggestion) = if inner_type.is_unit() { + ( + "this function's return value is unnecessary".to_string(), + "remove the return type...".to_string(), + snippet(cx, fn_decl.output.span(), "..").to_string(), + ) } else { - span_lint_and_then( - cx, - UNNECESSARY_WRAPS, - span, + ( format!( "this function's return value is unnecessarily wrapped by `{}`", return_type_label - ) - .as_str(), - |diag| { - diag.span_suggestion( - fn_decl.output.span(), - format!("remove `{}` from the return type...", return_type_label).as_str(), - inner_ty.to_string(), - Applicability::MaybeIncorrect, - ); - diag.multipart_suggestion( - "...and change the returning expressions", - suggs, - Applicability::MaybeIncorrect, - ); - }, + ), + format!("remove `{}` from the return type...", return_type_label), + inner_type.to_string(), + ) + }; + + span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| { + diag.span_suggestion( + fn_decl.output.span(), + return_type_suggestion_msg.as_str(), + return_type_suggestion, + Applicability::MaybeIncorrect, ); - } + diag.multipart_suggestion( + "...and then change the returning expressions", + suggs, + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index f28981f9e34..40effb89499 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -15,7 +15,7 @@ help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | return 42; LL | } @@ -41,7 +41,7 @@ help: remove `Option` from the return type... | LL | fn func2(a: bool, b: bool) -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | return 10; LL | } @@ -63,7 +63,7 @@ help: remove `Option` from the return type... | LL | fn func5() -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | 1 | @@ -80,7 +80,7 @@ help: remove `Result` from the return type... | LL | fn func7() -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | 1 | @@ -97,22 +97,62 @@ help: remove `Option` from the return type... | LL | fn func12() -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | 1 | -error: unneeded wrapped unit return type - --> $DIR/unnecessary_wraps.rs:120:38 +error: this function's return value is unnecessary + --> $DIR/unnecessary_wraps.rs:120:1 + | +LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { +LL | | if a && b { +LL | | return Some(()); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove the return type... | LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { - | ^^^^^^^^^^ help: remove the `-> Option<[...]>` + | ^^^^^^^^^^ +help: ...and then change the returning expressions + | +LL | return ; +LL | } +LL | if a { +LL | Some(()); +LL | +LL | } else { + ... -error: unneeded wrapped unit return type - --> $DIR/unnecessary_wraps.rs:133:38 +error: this function's return value is unnecessary + --> $DIR/unnecessary_wraps.rs:133:1 + | +LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { +LL | | if a && b { +LL | | return Ok(()); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove the return type... | LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { - | ^^^^^^^^^^^^^^^ help: remove the `-> Result<[...]>` + | ^^^^^^^^^^^^^^^ +help: ...and then change the returning expressions + | +LL | return ; +LL | } +LL | if a { +LL | +LL | } else { +LL | return ; + | error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From a87fa0e35049470aff2dc8f9ddc8399bb6c0718d Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 18 Feb 2021 18:35:25 +0900 Subject: Fix FP of result_unit_err when using type aliases --- clippy_lints/src/functions.rs | 15 +++++++-------- tests/ui/result_unit_error.rs | 23 ++++++++++++++++++++--- tests/ui/result_unit_error.stderr | 18 +++++++++++++----- 3 files changed, 40 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 94200a15420..30814e869e6 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,7 +1,7 @@ use crate::utils::{ 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, path_to_local, return_ty, snippet, snippet_opt, span_lint, - span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, + match_def_path, must_use_attr, path_to_local, 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; @@ -470,12 +470,11 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_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(); + let ty = hir_ty_to_ty(cx.tcx, ty); + if is_type_diagnostic_item(cx, ty, sym::result_type); + if let ty::Adt(_, substs) = ty.kind(); + let err_ty = substs.type_at(1); + if err_ty.is_unit(); then { span_lint_and_help( cx, diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index 5e57c752b5a..a4ec803024e 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,6 +1,4 @@ -#![allow(clippy::unnecessary_wraps)] -#[warn(clippy::result_unit_err)] -#[allow(unused)] +#![warn(clippy::result_unit_err)] pub fn returns_unit_error() -> Result { Err(()) @@ -36,4 +34,23 @@ impl UnitErrorHolder { } } +// https://github.com/rust-lang/rust-clippy/issues/6546 +pub mod issue_6546 { + type ResInv = Result; + + pub fn should_lint() -> ResInv<(), usize> { + Ok(0) + } + + pub fn should_not_lint() -> ResInv { + Ok(()) + } + + type MyRes = Result<(A, B), Box>; + + pub fn should_not_lint2(x: i32) -> MyRes { + Ok((x, ())) + } +} + fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 12901b354f9..41d8b0a7cb7 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,5 +1,5 @@ error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:5:1 + --> $DIR/result_unit_error.rs:3:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn returns_unit_error() -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:14:5 + --> $DIR/result_unit_error.rs:12:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn get_that_error(&self) -> Result; = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:16:5 + --> $DIR/result_unit_error.rs:14:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,12 +24,20 @@ 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:34:5 + --> $DIR/result_unit_error.rs:32:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a custom Error type instead -error: aborting due to 4 previous errors +error: this returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:41:5 + | +LL | pub fn should_lint() -> ResInv<(), usize> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From a78271b8611d7ab7709976dfb94ae36b668ac42b Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Thu, 18 Feb 2021 17:32:55 -0500 Subject: Changed fn body suggestion msg --- clippy_lints/src/unnecessary_wraps.rs | 14 ++++++-------- tests/ui/unnecessary_wraps.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_wraps.stderr | 14 +++++++------- 3 files changed, 31 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index b097d531bc4..607585125a4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -131,11 +131,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { }); if can_sugg && !suggs.is_empty() { - let (lint_msg, return_type_suggestion_msg, return_type_suggestion) = if inner_type.is_unit() { + let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() { ( "this function's return value is unnecessary".to_string(), "remove the return type...".to_string(), snippet(cx, fn_decl.output.span(), "..").to_string(), + "...and then remove returned values", ) } else { ( @@ -145,21 +146,18 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ), format!("remove `{}` from the return type...", return_type_label), inner_type.to_string(), + "...and then change returning expressions", ) }; span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| { diag.span_suggestion( fn_decl.output.span(), - return_type_suggestion_msg.as_str(), - return_type_suggestion, - Applicability::MaybeIncorrect, - ); - diag.multipart_suggestion( - "...and then change the returning expressions", - suggs, + return_type_sugg_msg.as_str(), + return_type_sugg, Applicability::MaybeIncorrect, ); + diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect); }); } } diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index 5aaa99bbe5a..a510263e67d 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -141,6 +141,24 @@ fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { } } +// should not be linted +fn issue_6640_3() -> Option<()> { + if true { + Some(()) + } else { + None + } +} + +// should not be linted +fn issue_6640_4() -> Result<(), ()> { + if true { + Ok(()) + } else { + Err(()) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 40effb89499..9a861c61a46 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -15,7 +15,7 @@ help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | return 42; LL | } @@ -41,7 +41,7 @@ help: remove `Option` from the return type... | LL | fn func2(a: bool, b: bool) -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | return 10; LL | } @@ -63,7 +63,7 @@ help: remove `Option` from the return type... | LL | fn func5() -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | 1 | @@ -80,7 +80,7 @@ help: remove `Result` from the return type... | LL | fn func7() -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | 1 | @@ -97,7 +97,7 @@ help: remove `Option` from the return type... | LL | fn func12() -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | 1 | @@ -118,7 +118,7 @@ help: remove the return type... | LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { | ^^^^^^^^^^ -help: ...and then change the returning expressions +help: ...and then remove returned values | LL | return ; LL | } @@ -144,7 +144,7 @@ help: remove the return type... | LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { | ^^^^^^^^^^^^^^^ -help: ...and then change the returning expressions +help: ...and then remove returned values | LL | return ; LL | } -- cgit 1.4.1-3-g733a5 From 54c6054ec72c7ec8babd95324059713abdfa74f7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 18 Feb 2021 20:02:58 -0600 Subject: Change unnecessary_wraps to pedantic --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/unnecessary_wraps.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 40727980693..102a5f05d73 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1393,6 +1393,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::PTR_AS_PTR), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unused_self::UNUSED_SELF), LintId::of(&wildcard_imports::ENUM_GLOB_USE), @@ -1686,7 +1687,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(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1903,7 +1903,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(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 8ac5dd696b7..0e78c76a7d1 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -48,7 +48,7 @@ declare_clippy_lint! { /// } /// ``` pub UNNECESSARY_WRAPS, - complexity, + pedantic, "functions that only return `Ok` or `Some`" } -- cgit 1.4.1-3-g733a5 From 46c91db1e1c1f497854069d78e522bc5ff6bf0f7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 18 Feb 2021 20:23:49 -0600 Subject: Change known problems --- clippy_lints/src/unnecessary_wraps.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 0e78c76a7d1..9c0c21b9bc7 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -17,8 +17,8 @@ declare_clippy_lint! { /// /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. /// - /// **Known problems:** Since this lint changes function type signature, you may need to - /// adjust some code at callee side. + /// **Known problems:** There can be false positives if the function signature is designed to + /// fit some external requirement. /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From 1f4153aa1e2fc4a59527c17cde43f3feb3088463 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 19 Feb 2021 10:02:17 +0100 Subject: collapsible_match: fix lint message capitalization (see https://rustc-dev-guide.rust-lang.org/diagnostics.html for details) --- clippy_lints/src/collapsible_match.rs | 6 ++-- tests/ui/collapsible_match.stderr | 60 +++++++++++++++++------------------ tests/ui/collapsible_match2.stderr | 30 +++++++++--------- 3 files changed, 48 insertions(+), 48 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 67282cb7900..3c45525684b 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -96,12 +96,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext cx, COLLAPSIBLE_MATCH, expr.span, - "Unnecessary nested match", + "unnecessary nested match", |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); - help_span.push_span_label(binding_span, "Replace this binding".into()); + help_span.push_span_label(binding_span, "replace this binding".into()); help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); - diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern."); + diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 63ac6a1613d..77978884900 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,4 +1,4 @@ -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:7:20 | LL | Ok(val) => match val { @@ -9,15 +9,15 @@ LL | | }, | |_________^ | = note: `-D clippy::collapsible-match` implied by `-D warnings` -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:7:12 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:16:20 | LL | Ok(val) => match val { @@ -27,15 +27,15 @@ LL | | _ => return, LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:16:12 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:25:9 | LL | / if let Some(n) = val { @@ -43,15 +43,15 @@ LL | | take(n); LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:24:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:32:9 | LL | / if let Some(n) = val { @@ -61,15 +61,15 @@ LL | | return; LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:31:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:43:9 | LL | / match val { @@ -78,16 +78,16 @@ LL | | _ => (), LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:42:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:52:13 | LL | / if let Some(n) = val { @@ -95,15 +95,15 @@ LL | | take(n); LL | | } | |_____________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:51:12 | LL | Ok(val) => { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:61:9 | LL | / match val { @@ -112,16 +112,16 @@ LL | | _ => return, LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:60:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:72:13 | LL | / if let Some(n) = val { @@ -131,15 +131,15 @@ LL | | return; LL | | } | |_____________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:71:12 | LL | Ok(val) => { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:83:20 | LL | Ok(val) => match val { @@ -149,15 +149,15 @@ LL | | None => return, LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:83:12 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:92:22 | LL | Some(val) => match val { @@ -167,11 +167,11 @@ LL | | _ => return, LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:92:14 | LL | Some(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index b2eb457d173..c8a445ef369 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -1,4 +1,4 @@ -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:8:34 | LL | Ok(val) if make() => match val { @@ -9,15 +9,15 @@ LL | | }, | |_____________^ | = note: `-D clippy::collapsible-match` implied by `-D warnings` -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:8:16 | LL | Ok(val) if make() => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:15:24 | LL | Ok(val) => match val { @@ -27,15 +27,15 @@ LL | | _ => return, LL | | }, | |_____________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:15:16 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:29:29 | LL | $pat => match $e { @@ -48,16 +48,16 @@ LL | | }, LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ------------------------------------------------- in this macro invocation | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:41:28 | LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ^^^ ^^^^^^^ with this pattern | | - | Replace this binding + | replace this binding = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:46:20 | LL | Some(s) => match *s { @@ -67,15 +67,15 @@ LL | | _ => (), LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:46:14 | LL | Some(s) => match *s { - | ^ Replace this binding + | ^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:55:24 | LL | Some(ref s) => match &*s { @@ -85,11 +85,11 @@ LL | | _ => (), LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:55:14 | LL | Some(ref s) => match &*s { - | ^^^^^ Replace this binding + | ^^^^^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern -- cgit 1.4.1-3-g733a5 From bf55aee7b142ad14d102de3260e314a84bf8c35c Mon Sep 17 00:00:00 2001 From: bool Date: Fri, 19 Feb 2021 19:36:28 +0200 Subject: Updated from_str_radix_10 sugg to be slightly smarter and ran bless --- clippy_lints/src/from_str_radix_10.rs | 26 ++++++++++++++++++++++---- tests/ui/from_str_radix_10.rs | 3 +++ tests/ui/from_str_radix_10.stderr | 10 ++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 993b85ed998..d0a170acb4f 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,9 +1,12 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; +use crate::utils::is_type_diagnostic_item; use crate::utils::span_lint_and_sugg; use crate::utils::sugg::Sugg; @@ -40,8 +43,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if_chain! { if let ExprKind::Call(maybe_path, arguments) = &exp.kind; - if let ExprKind::Path(qpath) = &maybe_path.kind; - if let QPath::TypeRelative(ty, pathseg) = &qpath; + if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind; // check if the first part of the path is some integer primitive if let TyKind::Path(ty_qpath) = &ty.kind; @@ -59,9 +61,20 @@ impl LateLintPass<'tcx> for FromStrRadix10 { if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; then { + let expr = if let ExprKind::AddrOf(_, _, expr) = &arguments[0].kind { + let ty = cx.typeck_results().expr_ty(expr); + if is_ty_stringish(cx, ty) { + expr + } else { + &arguments[0] + } + } else { + &arguments[0] + }; + let sugg = Sugg::hir_with_applicability( cx, - &arguments[0], + expr, "", &mut Applicability::MachineApplicable ).maybe_par(); @@ -79,3 +92,8 @@ impl LateLintPass<'tcx> for FromStrRadix10 { } } } + +/// Checks if a Ty is `String` or `&str` +fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + is_type_diagnostic_item(cx, ty, sym::string_type) || is_type_diagnostic_item(cx, ty, sym::str) +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 0a973128664..2f2ea04847a 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -35,6 +35,9 @@ fn main() -> Result<(), Box> { let string = "300"; i32::from_str_radix(string, 10)?; + let stringier = "400".to_string(); + i32::from_str_radix(&stringier, 10)?; + // none of these should trigger the lint u16::from_str_radix("20", 3)?; i32::from_str_radix("45", 12)?; diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index d6669001915..471bf52a9a7 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -28,7 +28,7 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:32:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&("10".to_owned() + "5")).parse::()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(("10".to_owned() + "5")).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:33:5 @@ -42,5 +42,11 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` -error: aborting due to 7 previous errors +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> $DIR/from_str_radix_10.rs:39:5 + | +LL | i32::from_str_radix(&stringier, 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From c4b8d87ab96cb6ef44685d928adeb9915b06f71f Mon Sep 17 00:00:00 2001 From: bool Date: Fri, 19 Feb 2021 22:00:23 +0200 Subject: Fixed the known problems section --- clippy_lints/src/from_str_radix_10.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index d0a170acb4f..0933f983014 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -19,7 +19,9 @@ declare_clippy_lint! { /// (and in most cases, the turbofish can be removed), which reduces code length /// and complexity. /// - /// **Known problems:** None. + /// **Known problems:** + /// This lint may suggest using (&).parse() instead of .parse() directly + /// in some cases, which is correct but adds unnecessary complexity to the code. /// /// **Example:** /// -- cgit 1.4.1-3-g733a5 From 5af6f96c8fe029ae4f9696084f6a22f5d750f3a9 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Feb 2021 19:48:04 +0100 Subject: Fix camel case postfix for `enum_variant_names` lint --- clippy_lints/src/utils/camel_case.rs | 6 ++++++ tests/ui/enum_variants.rs | 13 +++++++++++++ tests/ui/enum_variants.stderr | 26 +++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/camel_case.rs b/clippy_lints/src/utils/camel_case.rs index 4192a26d3c8..ba1c01ebc9f 100644 --- a/clippy_lints/src/utils/camel_case.rs +++ b/clippy_lints/src/utils/camel_case.rs @@ -55,6 +55,8 @@ pub fn from(s: &str) -> usize { } } else if c.is_lowercase() { down = true; + } else if c.is_uppercase() { + last_i = i; } else { return last_i; } @@ -70,12 +72,16 @@ mod test { fn from_full() { assert_eq!(from("AbcDef"), 0); assert_eq!(from("Abc"), 0); + assert_eq!(from("ABcd"), 0); + assert_eq!(from("ABcdEf"), 0); + assert_eq!(from("AabABcd"), 0); } #[test] fn from_partial() { assert_eq!(from("abcDef"), 3); assert_eq!(from("aDbc"), 1); + assert_eq!(from("aabABcd"), 3); } #[test] diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 89d99dcf0c8..4fefc0b43f1 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -133,4 +133,17 @@ pub enum NetworkLayer { Layer3, } +// should lint suggesting `IData`, not only `Data` (see #4639) +enum IDataRequest { + PutIData(String), + GetIData(String), + DeleteUnpubIData(String), +} + +enum HIDataRequest { + PutHIData(String), + GetHIData(String), + DeleteUnpubHIData(String), +} + fn main() {} diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index b1d481190ff..ab7fff4507a 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -97,5 +97,29 @@ LL | | } = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` = help: remove the prefixes and use full paths to the variants instead of glob imports -error: aborting due to 10 previous errors +error: all variants have the same postfix: `IData` + --> $DIR/enum_variants.rs:137:1 + | +LL | / enum IDataRequest { +LL | | PutIData(String), +LL | | GetIData(String), +LL | | DeleteUnpubIData(String), +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + +error: all variants have the same postfix: `HIData` + --> $DIR/enum_variants.rs:143:1 + | +LL | / enum HIDataRequest { +LL | | PutHIData(String), +LL | | GetHIData(String), +LL | | DeleteUnpubHIData(String), +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From d23038944a0ab7aaf8566196f9680b918f7ec155 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 20 Feb 2021 22:52:56 +0900 Subject: New lint: inconsistent_struct_constructor --- CHANGELOG.md | 1 + .../src/inconsistent_struct_constructor.rs | 118 +++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + tests/ui/inconsistent_struct_constructor.fixed | 61 +++++++++++ tests/ui/inconsistent_struct_constructor.rs | 65 ++++++++++++ tests/ui/inconsistent_struct_constructor.stderr | 20 ++++ 6 files changed, 270 insertions(+) create mode 100644 clippy_lints/src/inconsistent_struct_constructor.rs create mode 100644 tests/ui/inconsistent_struct_constructor.fixed create mode 100644 tests/ui/inconsistent_struct_constructor.rs create mode 100644 tests/ui/inconsistent_struct_constructor.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec8b3d98f8..c51557943e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2109,6 +2109,7 @@ Released 2018-09-13 [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs new file mode 100644 index 00000000000..24dbfbbae2f --- /dev/null +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -0,0 +1,118 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +use if_chain::if_chain; + +use crate::utils::{snippet, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for struct constructors where the order of the field init + /// shorthand in the constructor is inconsistent with the order in the struct definition. + /// + /// **Why is this bad?** It decreases readability and consistency. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct Foo { + /// x: i32, + /// y: i32, + /// } + /// let x = 1; + /// let y = 2; + /// Foo { y, x }; + /// ``` + /// + /// Use instead: + /// ```rust + /// # struct Foo { + /// # x: i32, + /// # y: i32, + /// # } + /// # let x = 1; + /// # let y = 2; + /// Foo { x, y }; + /// ``` + pub INCONSISTENT_STRUCT_CONSTRUCTOR, + style, + "the order of the field init shorthand is inconsistent with the order in the struct definition" +} + +declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); + +impl LateLintPass<'_> for InconsistentStructConstructor { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let ExprKind::Struct(qpath, fields, base) = expr.kind; + if let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); + let ty = cx.tcx.type_of(def_id); + if let Some(adt_def) = ty.ty_adt_def(); + if adt_def.is_struct(); + if let Some(variant) = adt_def.variants.iter().next(); + if fields.iter().all(|f| f.is_shorthand); + then { + let mut def_order_map = FxHashMap::default(); + for (idx, field) in variant.fields.iter().enumerate() { + def_order_map.insert(field.ident.name, idx); + } + + if is_consistent_order(fields, &def_order_map) { + return; + } + + let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); + ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); + + let mut fields_snippet = String::new(); + let (last_ident, idents) = ordered_fields.split_last().unwrap(); + for ident in idents { + fields_snippet.push_str(&format!("{}, ", ident)); + } + fields_snippet.push_str(&format!("{}", last_ident)); + + let base_snippet = if let Some(base) = base { + format!(", ..{}", snippet(cx, base.span, "..")) + } else { + "".to_string() + }; + + let sugg = format!("{} {{ {}{} }}", + snippet(cx, qpath.span(), ".."), + fields_snippet, + base_snippet, + ); + + span_lint_and_sugg( + cx, + INCONSISTENT_STRUCT_CONSTRUCTOR, + expr.span, + "inconsistent struct constructor", + "try", + sugg, + Applicability::MachineApplicable, + ) + } + } + } +} + +// Check whether the order of the fields in the constructor is consistent with the order in the +// definition. +fn is_consistent_order<'tcx>(fields: &'tcx [hir::Field<'tcx>], def_order_map: &FxHashMap) -> bool { + let mut cur_idx = usize::MIN; + for f in fields { + let next_idx = def_order_map[&f.ident.name]; + if cur_idx > next_idx { + return false; + } + cur_idx = next_idx; + } + + true +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 67e490584e8..c846dc187a4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -221,6 +221,7 @@ mod if_let_some_result; mod if_not_else; mod implicit_return; mod implicit_saturating_sub; +mod inconsistent_struct_constructor; mod indexing_slicing; mod infinite_iter; mod inherent_impl; @@ -656,6 +657,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &if_not_else::IF_NOT_ELSE, &implicit_return::IMPLICIT_RETURN, &implicit_saturating_sub::IMPLICIT_SATURATING_SUB, + &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, &indexing_slicing::INDEXING_SLICING, &indexing_slicing::OUT_OF_BOUNDS_INDEXING, &infinite_iter::INFINITE_ITER, @@ -1036,6 +1038,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); + store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1485,6 +1488,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&identity_op::IDENTITY_OP), LintId::of(&if_let_mutex::IF_LET_MUTEX), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&inherent_to_string::INHERENT_TO_STRING), @@ -1737,6 +1741,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed new file mode 100644 index 00000000000..8d9c3110035 --- /dev/null +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] +#![allow(dead_code)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +mod without_base { + use super::Foo; + + fn test() { + let x = 1; + let y = 1; + let z = 1; + + // Should lint. + Foo { x, y, z }; + + // Shoule NOT lint because the order is the same as in the definition. + Foo { x, y, z }; + + // Should NOT lint because z is not a shorthand init. + Foo { y, x, z: z }; + } +} + +mod with_base { + use super::Foo; + + fn test() { + let x = 1; + let z = 1; + + // Should lint. + Foo { x, z, ..Default::default() }; + + // Should NOT lint because the order is consistent with the definition. + Foo { + x, + z, + ..Default::default() + }; + + // Should NOT lint because z is not a shorthand init. + Foo { + z: z, + x, + ..Default::default() + }; + } +} + +fn main() {} diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs new file mode 100644 index 00000000000..63fac910501 --- /dev/null +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -0,0 +1,65 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] +#![allow(dead_code)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +mod without_base { + use super::Foo; + + fn test() { + let x = 1; + let y = 1; + let z = 1; + + // Should lint. + Foo { y, x, z }; + + // Shoule NOT lint because the order is the same as in the definition. + Foo { x, y, z }; + + // Should NOT lint because z is not a shorthand init. + Foo { y, x, z: z }; + } +} + +mod with_base { + use super::Foo; + + fn test() { + let x = 1; + let z = 1; + + // Should lint. + Foo { + z, + x, + ..Default::default() + }; + + // Should NOT lint because the order is consistent with the definition. + Foo { + x, + z, + ..Default::default() + }; + + // Should NOT lint because z is not a shorthand init. + Foo { + z: z, + x, + ..Default::default() + }; + } +} + +fn main() {} diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr new file mode 100644 index 00000000000..d7abe44f254 --- /dev/null +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -0,0 +1,20 @@ +error: inconsistent struct constructor + --> $DIR/inconsistent_struct_constructor.rs:25:9 + | +LL | Foo { y, x, z }; + | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + +error: inconsistent struct constructor + --> $DIR/inconsistent_struct_constructor.rs:43:9 + | +LL | / Foo { +LL | | z, +LL | | x, +LL | | ..Default::default() +LL | | }; + | |_________^ help: try: `Foo { x, z, ..Default::default() }` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From d646aa2ae7a27495dd87344ffdad4b66c1bd4425 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 20 Feb 2021 23:02:18 +0900 Subject: Fix unnecessary_sort_by.rs that fails the dogfood test --- clippy_lints/src/unnecessary_sort_by.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 9b45d38afd4..00a707107bc 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -212,10 +212,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if !expr_borrows(cx, left_expr) { return Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, - unstable, closure_arg, closure_body, - reverse + reverse, + unstable, })); } } -- cgit 1.4.1-3-g733a5 From 19a377510c3de9a7404f690d52a5de761ce2aafd Mon Sep 17 00:00:00 2001 From: Morten Lohne Date: Sat, 20 Feb 2021 23:47:49 +0100 Subject: Fix FP in inherent_to_string when the function has generic parameters --- clippy_lints/src/inherent_to_string.rs | 1 + tests/ui/inherent_to_string.rs | 11 +++++++++++ tests/ui/inherent_to_string.stderr | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index b723d06a688..0b23cdaa9f0 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -106,6 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { let decl = &signature.decl; if decl.implicit_self.has_implicit_self(); if decl.inputs.len() == 1; + if impl_item.generics.params.is_empty(); // Check if return type is String if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::string_type); diff --git a/tests/ui/inherent_to_string.rs b/tests/ui/inherent_to_string.rs index e6cf337d1bb..6e65fdbd04e 100644 --- a/tests/ui/inherent_to_string.rs +++ b/tests/ui/inherent_to_string.rs @@ -14,6 +14,7 @@ struct C; struct D; struct E; struct F; +struct G; impl A { // Should be detected; emit warning @@ -73,6 +74,13 @@ impl F { } } +impl G { + // Should not be detected, as it does not match the function signature + fn to_string(&self) -> String { + "G.to_string()".to_string() + } +} + fn main() { let a = A; a.to_string(); @@ -93,4 +101,7 @@ fn main() { let f = F; f.to_string(1); + + let g = G; + g.to_string::<1>(); } diff --git a/tests/ui/inherent_to_string.stderr b/tests/ui/inherent_to_string.stderr index 4f331f5bec9..f5fcc193b4d 100644 --- a/tests/ui/inherent_to_string.stderr +++ b/tests/ui/inherent_to_string.stderr @@ -1,5 +1,5 @@ error: implementation of inherent method `to_string(&self) -> String` for type `A` - --> $DIR/inherent_to_string.rs:20:5 + --> $DIR/inherent_to_string.rs:21:5 | LL | / fn to_string(&self) -> String { LL | | "A.to_string()".to_string() @@ -10,7 +10,7 @@ LL | | } = help: implement trait `Display` for type `A` instead error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` - --> $DIR/inherent_to_string.rs:44:5 + --> $DIR/inherent_to_string.rs:45:5 | LL | / fn to_string(&self) -> String { LL | | "C.to_string()".to_string() -- cgit 1.4.1-3-g733a5 From bfdf0fa03f14be82d668036df221326374f7a321 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 22 Feb 2021 11:45:25 +0900 Subject: Describe the order of fields in struct ctor doesn't affect the resulted instance --- .../src/inconsistent_struct_constructor.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 24dbfbbae2f..c5afdf530eb 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -13,7 +13,23 @@ declare_clippy_lint! { /// **What it does:** Checks for struct constructors where the order of the field init /// shorthand in the constructor is inconsistent with the order in the struct definition. /// - /// **Why is this bad?** It decreases readability and consistency. + /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the + /// resulted instance as the below example indicates, + /// + /// ```rust + /// #[derive(Debug, PartialEq, Eq)] + /// struct Foo { + /// x: i32, + /// y: i32, + /// } + /// let x = 1; + /// let y = 2; + /// + /// // This assertion never fails. + /// assert_eq!(Foo { x, y }, Foo { y, x }); + /// ``` + /// + /// inconsistent order means nothing and just decreases readability and consistency. /// /// **Known problems:** None. /// @@ -74,12 +90,12 @@ impl LateLintPass<'_> for InconsistentStructConstructor { for ident in idents { fields_snippet.push_str(&format!("{}, ", ident)); } - fields_snippet.push_str(&format!("{}", last_ident)); + fields_snippet.push_str(&last_ident.to_string()); let base_snippet = if let Some(base) = base { format!(", ..{}", snippet(cx, base.span, "..")) } else { - "".to_string() + String::new() }; let sugg = format!("{} {{ {}{} }}", -- cgit 1.4.1-3-g733a5 From efe33f9fe4bf6b644f26f51500d557614a5f2c93 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 10 Jan 2021 09:46:03 -0500 Subject: Add: option_manual_map lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_map.rs | 262 ++++++++++++++++++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 27 +++- tests/ui/manual_map_option.fixed | 61 +++++++++ tests/ui/manual_map_option.rs | 113 ++++++++++++++++ tests/ui/manual_map_option.stderr | 158 +++++++++++++++++++++++ 7 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/manual_map.rs create mode 100644 tests/ui/manual_map_option.fixed create mode 100644 tests/ui/manual_map_option.rs create mode 100644 tests/ui/manual_map_option.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec8b3d98f8..edc5cea1dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2161,6 +2161,7 @@ Released 2018-09-13 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2e0bc4de801..b151c178afa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -241,6 +241,7 @@ mod loops; mod macro_use; mod main_recursion; mod manual_async_fn; +mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; mod manual_strip; @@ -706,6 +707,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, + &manual_map::MANUAL_MAP, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_ok_or::MANUAL_OK_OR, &manual_strip::MANUAL_STRIP, @@ -1262,6 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); + store.register_late_pass(|| box manual_map::ManualMap); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1521,6 +1524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(&manual_map::MANUAL_MAP), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), @@ -1750,6 +1754,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(&manual_map::MANUAL_MAP), 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_map.rs b/clippy_lints/src/manual_map.rs new file mode 100644 index 00000000000..e399c8fda55 --- /dev/null +++ b/clippy_lints/src/manual_map.rs @@ -0,0 +1,262 @@ +use crate::utils::{ + is_type_diagnostic_item, match_def_path, paths, peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, + snippet_with_applicability, span_lint_and_sugg, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::{sym, Ident}; + +declare_clippy_lint! { + /// **What it does:** Checks for usages of `match` which could be implemented using `map` + /// + /// **Why is this bad?** Using the `map` method is clearer and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// match Some(0) { + /// Some(x) => Some(x + 1), + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).map(|x| x + 1); + /// ``` + pub MANUAL_MAP, + style, + "reimplementation of `map`" +} + +declare_lint_pass!(ManualMap => [MANUAL_MAP]); + +impl LateLintPass<'_> for ManualMap { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) = + expr.kind + { + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + || !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type) + { + return; + } + + let (some_expr, some_pat, pat_ref_count, is_wild_none) = + match (try_parse_pattern(cx, arm1.pat), try_parse_pattern(cx, arm2.pat)) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, false) + }, + _ => return, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } + + let some_expr = match get_some_expr(cx, some_expr) { + Some(expr) => expr, + None => return, + }; + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_mutability = explicit_ref.or(if ty_ref_count != pat_ref_count { + Some(ty_mutability) + } else { + None + }); + let as_ref_str = match binding_mutability { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. `as_ref` will be called, + // the type is copyable, or the option is being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let scrutinee_str = snippet_with_applicability(cx, scrutinee.span, "_", &mut app); + let scrutinee_str = if expr.precedence().order() < PREC_POSTFIX { + // Parens are needed to chain method calls. + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; + + let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind { + if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + format!( + "|{}{}| {}", + annotation, + some_binding, + snippet_with_applicability(cx, some_expr.span, "..", &mut app) + ) + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + format!( + "|{}| {}", + snippet_with_applicability(cx, some_pat.span, "..", &mut app), + snippet_with_applicability(cx, some_expr.span, "..", &mut app) + ) + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str), + app, + ); + } + } +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if matches!(arg.kind, + ExprKind::Path(QPath::Resolved(None, Path { segments: [path], ..})) + if path.ident == binding + ) && cx.typeck_results().expr_adjustments(arg).is_empty() => + { + Some(func) + }, + _ => None, + } +} + +enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option> { + fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1), + PatKind::Path(QPath::Resolved(None, path)) + if path + .res + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) => + { + Some(OptionPat::None) + }, + PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _) + if path + .res + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0) +} + +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. +fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(QPath::Resolved(None, path)), + .. + }, + [arg], + ) => { + if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { + Some(arg) + } else { + None + } + }, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + .. + }, + _, + ) => get_some_expr(cx, expr), + _ => None, + } +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(QPath::Resolved(None, path)) => path + .res + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)), + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + .. + }, + _, + ) => is_none_expr(cx, expr), + _ => false, + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index fafa1400156..40771449264 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -32,7 +32,7 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, LitKind}; +use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; @@ -1672,6 +1672,18 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, f(expr, 0, count) } +/// Peels off all references on the expression. Returns the underlying expression and the number of +/// references removed. +pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { + fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { + match expr.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1), + _ => (expr, count), + } + } + f(expr, 0) +} + /// Peels off all references on the type. Returns the underlying type and the number of references /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { @@ -1685,6 +1697,19 @@ pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { peel(ty, 0) } +/// Peels off all references on the type.Returns the underlying type, the number of references +/// removed, and whether the pointer is ultimately mutable or not. +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { + fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { + match ty.kind() { + ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability), + ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not), + _ => (ty, count, mutability), + } + } + f(ty, 0, Mutability::Mut) +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed new file mode 100644 index 00000000000..6cb9a37b230 --- /dev/null +++ b/tests/ui/manual_map_option.fixed @@ -0,0 +1,61 @@ +// run-rustfix + +#![warn(clippy::manual_map)] +#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] + +fn main() { + Some(0).map(|_| 2); + + Some(0).map(|x| x + 1); + + Some("").map(|x| x.is_empty()); + + Some(0).map(|x| !x); + + #[rustfmt::skip] + Some(0).map(std::convert::identity); + + Some(&String::new()).map(|x| str::len(x)); + + match Some(0) { + Some(x) if false => Some(x + 1), + _ => None, + }; + + Some([0, 1]).as_ref().map(|x| x[0]); + + Some(0).map(|x| x * 2); + + Some(String::new()).as_ref().map(|x| x.is_empty()); + + Some(String::new()).as_ref().map(|x| x.len()); + + Some(0).map(|x| x + x); + + Some(String::new()).as_mut().map(|x| x.push_str("")); + + Some(String::new()).as_ref().map(|x| &**x); + + Some(String::new()).as_ref().map(|x| x.is_empty()); + + Some((0, 1, 2)).map(|(x, y, z)| x + y + z); + + Some([1, 2, 3]).map(|[first, ..]| first); + + Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x)); + + match Some((String::new(), 0)) { + Some((ref x, y)) => Some((y, x)), + None => None, + }; + + match Some(Some(0)) { + Some(Some(_)) | Some(None) => Some(0), + None => None, + }; + + match Some(Some((0, 1))) { + Some(Some((x, 1))) => Some(x), + _ => None, + }; +} diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs new file mode 100644 index 00000000000..b9753060b99 --- /dev/null +++ b/tests/ui/manual_map_option.rs @@ -0,0 +1,113 @@ +// run-rustfix + +#![warn(clippy::manual_map)] +#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] + +fn main() { + match Some(0) { + Some(_) => Some(2), + None:: => None, + }; + + match Some(0) { + Some(x) => Some(x + 1), + _ => None, + }; + + match Some("") { + Some(x) => Some(x.is_empty()), + None => None, + }; + + if let Some(x) = Some(0) { + Some(!x) + } else { + None + }; + + #[rustfmt::skip] + match Some(0) { + Some(x) => { Some(std::convert::identity(x)) } + None => { None } + }; + + match Some(&String::new()) { + Some(x) => Some(str::len(x)), + None => None, + }; + + match Some(0) { + Some(x) if false => Some(x + 1), + _ => None, + }; + + match &Some([0, 1]) { + Some(x) => Some(x[0]), + &None => None, + }; + + match &Some(0) { + &Some(x) => Some(x * 2), + None => None, + }; + + match Some(String::new()) { + Some(ref x) => Some(x.is_empty()), + _ => None, + }; + + match &&Some(String::new()) { + Some(x) => Some(x.len()), + _ => None, + }; + + match &&Some(0) { + &&Some(x) => Some(x + x), + &&_ => None, + }; + + match &mut Some(String::new()) { + Some(x) => Some(x.push_str("")), + None => None, + }; + + match &mut Some(String::new()) { + Some(ref x) => Some(&**x), + None => None, + }; + + match &mut &Some(String::new()) { + Some(x) => Some(x.is_empty()), + &mut _ => None, + }; + + match Some((0, 1, 2)) { + Some((x, y, z)) => Some(x + y + z), + None => None, + }; + + match Some([1, 2, 3]) { + Some([first, ..]) => Some(first), + None => None, + }; + + match &Some((String::new(), "test")) { + Some((x, y)) => Some((y, x)), + None => None, + }; + + match Some((String::new(), 0)) { + Some((ref x, y)) => Some((y, x)), + None => None, + }; + + match Some(Some(0)) { + Some(Some(_)) | Some(None) => Some(0), + None => None, + }; + + match Some(Some((0, 1))) { + Some(Some((x, 1))) => Some(x), + _ => None, + }; +} diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr new file mode 100644 index 00000000000..f8e1bda83b2 --- /dev/null +++ b/tests/ui/manual_map_option.stderr @@ -0,0 +1,158 @@ +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:7:5 + | +LL | / match Some(0) { +LL | | Some(_) => Some(2), +LL | | None:: => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|_| 2)` + | + = note: `-D clippy::manual-map` implied by `-D warnings` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:12:5 + | +LL | / match Some(0) { +LL | | Some(x) => Some(x + 1), +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| x + 1)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:17:5 + | +LL | / match Some("") { +LL | | Some(x) => Some(x.is_empty()), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some("").map(|x| x.is_empty())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:22:5 + | +LL | / if let Some(x) = Some(0) { +LL | | Some(!x) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| !x)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:29:5 + | +LL | / match Some(0) { +LL | | Some(x) => { Some(std::convert::identity(x)) } +LL | | None => { None } +LL | | }; + | |_____^ help: try this: `Some(0).map(std::convert::identity)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:34:5 + | +LL | / match Some(&String::new()) { +LL | | Some(x) => Some(str::len(x)), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:44:5 + | +LL | / match &Some([0, 1]) { +LL | | Some(x) => Some(x[0]), +LL | | &None => None, +LL | | }; + | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:49:5 + | +LL | / match &Some(0) { +LL | | &Some(x) => Some(x * 2), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| x * 2)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:54:5 + | +LL | / match Some(String::new()) { +LL | | Some(ref x) => Some(x.is_empty()), +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:59:5 + | +LL | / match &&Some(String::new()) { +LL | | Some(x) => Some(x.len()), +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:64:5 + | +LL | / match &&Some(0) { +LL | | &&Some(x) => Some(x + x), +LL | | &&_ => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| x + x)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:69:5 + | +LL | / match &mut Some(String::new()) { +LL | | Some(x) => Some(x.push_str("")), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:74:5 + | +LL | / match &mut Some(String::new()) { +LL | | Some(ref x) => Some(&**x), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| &**x)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:79:5 + | +LL | / match &mut &Some(String::new()) { +LL | | Some(x) => Some(x.is_empty()), +LL | | &mut _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:84:5 + | +LL | / match Some((0, 1, 2)) { +LL | | Some((x, y, z)) => Some(x + y + z), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:89:5 + | +LL | / match Some([1, 2, 3]) { +LL | | Some([first, ..]) => Some(first), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:94:5 + | +LL | / match &Some((String::new(), "test")) { +LL | | Some((x, y)) => Some((y, x)), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` + +error: aborting due to 17 previous errors + -- cgit 1.4.1-3-g733a5 From 23aa2f880cc0bda7ea3bbef1391f7c4d86467d65 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 19 Feb 2021 14:04:37 -0500 Subject: Fix dogfood errors --- clippy_lints/src/manual_map.rs | 40 +++++++++++++++++++++++++-------------- tests/ui/manual_map_option.fixed | 13 +++++++++++-- tests/ui/manual_map_option.rs | 11 ++++++++++- tests/ui/manual_map_option.stderr | 26 ++++++++++++------------- 4 files changed, 60 insertions(+), 30 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index e399c8fda55..a50a3943bab 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,10 +1,14 @@ -use crate::utils::{ - is_type_diagnostic_item, match_def_path, paths, peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, - snippet_with_applicability, span_lint_and_sugg, +use crate::{ + map_unit_fn::OPTION_MAP_UNIT_FN, + matches::MATCH_AS_REF, + utils::{ + is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs, + peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg, + }, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -37,6 +41,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { + #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) { return; @@ -88,14 +93,17 @@ impl LateLintPass<'_> for ManualMap { None => return, }; + if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit + && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return; + } + // Determine which binding mode to use. let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_mutability = explicit_ref.or(if ty_ref_count != pat_ref_count { - Some(ty_mutability) - } else { - None - }); - let as_ref_str = match binding_mutability { + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + + let as_ref_str = match binding_ref { Some(Mutability::Mut) => ".as_mut()", Some(Mutability::Not) => ".as_ref()", None => "", @@ -118,6 +126,13 @@ impl LateLintPass<'_> for ManualMap { if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() } else { + if match_var(some_expr, some_binding.name) + && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } + // `ref` and `ref mut` annotations were handled earlier. let annotation = if matches!(annotation, BindingAnnotation::Mutable) { "mut " @@ -161,10 +176,7 @@ impl LateLintPass<'_> for ManualMap { fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if matches!(arg.kind, - ExprKind::Path(QPath::Resolved(None, Path { segments: [path], ..})) - if path.ident == binding - ) && cx.typeck_results().expr_adjustments(arg).is_empty() => + if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() => { Some(func) }, diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 6cb9a37b230..19350906758 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -32,9 +32,18 @@ fn main() { Some(0).map(|x| x + x); - Some(String::new()).as_mut().map(|x| x.push_str("")); + #[warn(clippy::option_map_unit_fn)] + match &mut Some(String::new()) { + Some(x) => Some(x.push_str("")), + None => None, + }; - Some(String::new()).as_ref().map(|x| &**x); + #[allow(clippy::option_map_unit_fn)] + { + Some(String::new()).as_mut().map(|x| x.push_str("")); + } + + Some(String::new()).as_ref().map(|x| x.len()); Some(String::new()).as_ref().map(|x| x.is_empty()); diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index b9753060b99..8b8187db0a9 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -66,13 +66,22 @@ fn main() { &&_ => None, }; + #[warn(clippy::option_map_unit_fn)] match &mut Some(String::new()) { Some(x) => Some(x.push_str("")), None => None, }; + #[allow(clippy::option_map_unit_fn)] + { + match &mut Some(String::new()) { + Some(x) => Some(x.push_str("")), + None => None, + }; + } + match &mut Some(String::new()) { - Some(ref x) => Some(&**x), + Some(ref x) => Some(x.len()), None => None, }; diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index f8e1bda83b2..210a30d05d4 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -101,25 +101,25 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:69:5 + --> $DIR/manual_map_option.rs:77:9 | -LL | / match &mut Some(String::new()) { -LL | | Some(x) => Some(x.push_str("")), -LL | | None => None, -LL | | }; - | |_____^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` +LL | / match &mut Some(String::new()) { +LL | | Some(x) => Some(x.push_str("")), +LL | | None => None, +LL | | }; + | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:74:5 + --> $DIR/manual_map_option.rs:83:5 | LL | / match &mut Some(String::new()) { -LL | | Some(ref x) => Some(&**x), +LL | | Some(ref x) => Some(x.len()), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| &**x)` + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:79:5 + --> $DIR/manual_map_option.rs:88:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:84:5 + --> $DIR/manual_map_option.rs:93:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:89:5 + --> $DIR/manual_map_option.rs:98:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:94:5 + --> $DIR/manual_map_option.rs:103:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), -- cgit 1.4.1-3-g733a5 From 0eefa6169df7f55a7cbeeaff77bf9dfbce8bfcff Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 22 Feb 2021 12:30:28 +0100 Subject: upper_case_acronyms: move lint from style to pedantic lint group The lint does point out inconsistency with the Rust naming convention, but the fact that rustc does not warn about the inconsistency by default means that clippy probably should not warn by default either. changelog: move upper_case_acronyms lint from style to pedantic group. --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/upper_case_acronyms.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7dcf7a260c8..a6d07c1f519 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1402,6 +1402,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unused_self::UNUSED_SELF), + LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), @@ -1700,7 +1701,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), - LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), @@ -1819,7 +1819,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&upper_case_acronyms::UPPER_CASE_ACRONYMS), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 61e7031716a..b593fd0a699 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// struct HttpResponse; /// ``` pub UPPER_CASE_ACRONYMS, - style, + pedantic, "capitalized acronyms are against the naming convention" } -- cgit 1.4.1-3-g733a5 From e05965eb13a3e303ffe9001bb73e7d9c87d3bf78 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 22 Feb 2021 23:02:04 +0900 Subject: Remove unneeded blank lines from doc --- clippy_lints/src/write.rs | 3 --- 1 file changed, 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index e40fdca6a99..f21f5180554 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -147,7 +147,6 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); - /// /// // Bad /// writeln!(buf, ""); /// @@ -174,7 +173,6 @@ declare_clippy_lint! { /// # use std::fmt::Write; /// # let mut buf = String::new(); /// # let name = "World"; - /// /// // Bad /// write!(buf, "Hello {}!\n", name); /// @@ -200,7 +198,6 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); - /// /// // Bad /// writeln!(buf, "{}", "foo"); /// -- cgit 1.4.1-3-g733a5 From 09bded443745a05daaeeca76e4b032381ca1a126 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 17 Feb 2021 12:42:21 -0500 Subject: Factor out `clippy_utils` crate --- .gitignore | 1 + clippy_lints/Cargo.toml | 1 + clippy_lints/src/consts.rs | 575 +------ clippy_lints/src/lib.rs | 110 +- clippy_lints/src/utils.rs | 1 + clippy_lints/src/utils/ast_utils.rs | 562 ------- clippy_lints/src/utils/ast_utils/ident_iter.rs | 45 - clippy_lints/src/utils/attrs.rs | 150 -- clippy_lints/src/utils/author.rs | 779 --------- clippy_lints/src/utils/camel_case.rs | 121 -- clippy_lints/src/utils/comparisons.rs | 36 - clippy_lints/src/utils/conf.rs | 257 --- clippy_lints/src/utils/diagnostics.rs | 226 --- clippy_lints/src/utils/eager_or_lazy.rs | 134 -- clippy_lints/src/utils/higher.rs | 268 ---- clippy_lints/src/utils/hir_utils.rs | 852 ---------- clippy_lints/src/utils/inspector.rs | 578 ------- clippy_lints/src/utils/internal_lints.rs | 1070 ------------- clippy_lints/src/utils/mod.rs | 1862 ---------------------- clippy_lints/src/utils/numeric_literal.rs | 228 --- clippy_lints/src/utils/paths.rs | 181 --- clippy_lints/src/utils/ptr.rs | 86 - clippy_lints/src/utils/qualify_min_const_fn.rs | 347 ---- clippy_lints/src/utils/sugg.rs | 683 -------- clippy_lints/src/utils/sym_helper.rs | 7 - clippy_lints/src/utils/usage.rs | 198 --- clippy_lints/src/utils/visitors.rs | 190 --- clippy_utils/Cargo.toml | 24 + clippy_utils/src/ast_utils.rs | 562 +++++++ clippy_utils/src/ast_utils/ident_iter.rs | 45 + clippy_utils/src/attrs.rs | 150 ++ clippy_utils/src/author.rs | 779 +++++++++ clippy_utils/src/camel_case.rs | 121 ++ clippy_utils/src/comparisons.rs | 36 + clippy_utils/src/conf.rs | 257 +++ clippy_utils/src/constants.rs | 13 + clippy_utils/src/consts.rs | 574 +++++++ clippy_utils/src/diagnostics.rs | 226 +++ clippy_utils/src/eager_or_lazy.rs | 134 ++ clippy_utils/src/higher.rs | 268 ++++ clippy_utils/src/hir_utils.rs | 852 ++++++++++ clippy_utils/src/inspector.rs | 578 +++++++ clippy_utils/src/internal_lints.rs | 1070 +++++++++++++ clippy_utils/src/lib.rs | 1998 ++++++++++++++++++++++++ clippy_utils/src/numeric_literal.rs | 228 +++ clippy_utils/src/paths.rs | 181 +++ clippy_utils/src/ptr.rs | 86 + clippy_utils/src/qualify_min_const_fn.rs | 347 ++++ clippy_utils/src/sugg.rs | 683 ++++++++ clippy_utils/src/sym_helper.rs | 7 + clippy_utils/src/usage.rs | 198 +++ clippy_utils/src/visitors.rs | 190 +++ 52 files changed, 9626 insertions(+), 9529 deletions(-) create mode 100644 clippy_lints/src/utils.rs delete mode 100644 clippy_lints/src/utils/ast_utils.rs delete mode 100644 clippy_lints/src/utils/ast_utils/ident_iter.rs delete mode 100644 clippy_lints/src/utils/attrs.rs delete mode 100644 clippy_lints/src/utils/author.rs delete mode 100644 clippy_lints/src/utils/camel_case.rs delete mode 100644 clippy_lints/src/utils/comparisons.rs delete mode 100644 clippy_lints/src/utils/conf.rs delete mode 100644 clippy_lints/src/utils/diagnostics.rs delete mode 100644 clippy_lints/src/utils/eager_or_lazy.rs delete mode 100644 clippy_lints/src/utils/higher.rs delete mode 100644 clippy_lints/src/utils/hir_utils.rs delete mode 100644 clippy_lints/src/utils/inspector.rs delete mode 100644 clippy_lints/src/utils/internal_lints.rs delete mode 100644 clippy_lints/src/utils/mod.rs delete mode 100644 clippy_lints/src/utils/numeric_literal.rs delete mode 100644 clippy_lints/src/utils/paths.rs delete mode 100644 clippy_lints/src/utils/ptr.rs delete mode 100644 clippy_lints/src/utils/qualify_min_const_fn.rs delete mode 100644 clippy_lints/src/utils/sugg.rs delete mode 100644 clippy_lints/src/utils/sym_helper.rs delete mode 100644 clippy_lints/src/utils/usage.rs delete mode 100644 clippy_lints/src/utils/visitors.rs create mode 100644 clippy_utils/Cargo.toml create mode 100644 clippy_utils/src/ast_utils.rs create mode 100644 clippy_utils/src/ast_utils/ident_iter.rs create mode 100644 clippy_utils/src/attrs.rs create mode 100644 clippy_utils/src/author.rs create mode 100644 clippy_utils/src/camel_case.rs create mode 100644 clippy_utils/src/comparisons.rs create mode 100644 clippy_utils/src/conf.rs create mode 100644 clippy_utils/src/constants.rs create mode 100644 clippy_utils/src/consts.rs create mode 100644 clippy_utils/src/diagnostics.rs create mode 100644 clippy_utils/src/eager_or_lazy.rs create mode 100644 clippy_utils/src/higher.rs create mode 100644 clippy_utils/src/hir_utils.rs create mode 100644 clippy_utils/src/inspector.rs create mode 100644 clippy_utils/src/internal_lints.rs create mode 100644 clippy_utils/src/lib.rs create mode 100644 clippy_utils/src/numeric_literal.rs create mode 100644 clippy_utils/src/paths.rs create mode 100644 clippy_utils/src/ptr.rs create mode 100644 clippy_utils/src/qualify_min_const_fn.rs create mode 100644 clippy_utils/src/sugg.rs create mode 100644 clippy_utils/src/sym_helper.rs create mode 100644 clippy_utils/src/usage.rs create mode 100644 clippy_utils/src/visitors.rs (limited to 'clippy_lints/src') diff --git a/.gitignore b/.gitignore index adf5e8feddf..139129d55e3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ out *Cargo.lock /target /clippy_lints/target +/clippy_utils/target /clippy_workspace_tests/target /clippy_dev/target /rustc_tools_util/target diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 840341fefc6..f09df3393d7 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,6 +18,7 @@ edition = "2018" [dependencies] cargo_metadata = "0.12" +clippy_utils = { path = "../clippy_utils" } if_chain = "1.0.0" itertools = "0.9" pulldown-cmark = { version = "0.8", default-features = false } diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 1b89d0bbe38..7e87f53e3fb 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -1,574 +1 @@ -#![allow(clippy::float_cmp)] - -use crate::utils::{clip, sext, unsext}; -use if_chain::if_chain; -use rustc_ast::ast::{self, LitFloatType, LitKind}; -use rustc_data_structures::sync::Lrc; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; -use rustc_lint::LateContext; -use rustc_middle::mir::interpret::Scalar; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt}; -use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::Symbol; -use std::cmp::Ordering::{self, Equal}; -use std::convert::TryInto; -use std::hash::{Hash, Hasher}; - -/// A `LitKind`-like enum to fold constant `Expr`s into. -#[derive(Debug, Clone)] -pub enum Constant { - /// A `String` (e.g., "abc"). - Str(String), - /// A binary string (e.g., `b"abc"`). - Binary(Lrc<[u8]>), - /// A single `char` (e.g., `'a'`). - Char(char), - /// An integer's bit representation. - Int(u128), - /// An `f32`. - F32(f32), - /// An `f64`. - F64(f64), - /// `true` or `false`. - Bool(bool), - /// An array of constants. - Vec(Vec), - /// Also an array, but with only one constant, repeated N times. - Repeat(Box, u64), - /// A tuple of constants. - Tuple(Vec), - /// A raw pointer. - RawPtr(u128), - /// A reference - Ref(Box), - /// A literal with syntax error. - Err(Symbol), -} - -impl PartialEq for Constant { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, - (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, - (&Self::Char(l), &Self::Char(r)) => l == r, - (&Self::Int(l), &Self::Int(r)) => l == r, - (&Self::F64(l), &Self::F64(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - l.to_bits() == r.to_bits() - }, - (&Self::F32(l), &Self::F32(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - f64::from(l).to_bits() == f64::from(r).to_bits() - }, - (&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, - } - } -} - -impl Hash for Constant { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - std::mem::discriminant(self).hash(state); - match *self { - Self::Str(ref s) => { - s.hash(state); - }, - Self::Binary(ref b) => { - b.hash(state); - }, - Self::Char(c) => { - c.hash(state); - }, - Self::Int(i) => { - i.hash(state); - }, - Self::F32(f) => { - f64::from(f).to_bits().hash(state); - }, - Self::F64(f) => { - f.to_bits().hash(state); - }, - Self::Bool(b) => { - b.hash(state); - }, - Self::Vec(ref v) | Self::Tuple(ref v) => { - v.hash(state); - }, - Self::Repeat(ref c, l) => { - c.hash(state); - l.hash(state); - }, - Self::RawPtr(u) => { - u.hash(state); - }, - Self::Ref(ref r) => { - r.hash(state); - }, - Self::Err(ref s) => { - s.hash(state); - }, - } - } -} - -impl Constant { - pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { - match (left, right) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), - (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), - (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = *cmp_type.kind() { - Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) - } else { - Some(l.cmp(&r)) - } - }, - (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), - (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), - (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l - .iter() - .zip(r.iter()) - .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) - .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)) => { - match Self::partial_cmp(tcx, cmp_type, lv, rv) { - Some(Equal) => Some(ls.cmp(rs)), - 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, - } - } -} - -/// Parses a `LitKind` to a `Constant`. -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::Char(c) => Constant::Char(c), - LitKind::Int(n, _) => Constant::Int(n), - LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { - ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), - ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), - }, - LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { - ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), - ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), - _ => bug!(), - }, - LitKind::Bool(b) => Constant::Bool(b), - LitKind::Err(s) => Constant::Err(s), - } -} - -pub fn constant<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<(Constant, bool)> { - let mut cx = ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: lcx.tcx.intern_substs(&[]), - }; - cx.expr(e).map(|cst| (cst, cx.needed_resolution)) -} - -pub fn constant_simple<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option { - constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) -} - -/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`. -pub fn constant_context<'a, 'tcx>( - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, -) -> ConstEvalLateContext<'a, 'tcx> { - ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: lcx.tcx.intern_substs(&[]), - } -} - -pub struct ConstEvalLateContext<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - param_env: ty::ParamEnv<'tcx>, - needed_resolution: bool, - substs: SubstsRef<'tcx>, -} - -impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { - /// Simple constant folding: Insert an expression, get a constant or none. - pub fn expr(&mut self, e: &Expr<'_>) -> Option { - match e.kind { - ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), - ExprKind::Block(ref block, _) => self.block(block), - ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))), - ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), - ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), - ExprKind::Repeat(ref value, _) => { - let n = match self.typeck_results.expr_ty(e).kind() { - ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, - _ => span_bug!(e.span, "typeck error"), - }; - self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) - }, - ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { - UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), - UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), - UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), - }), - ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), - ExprKind::Call(ref callee, ref args) => { - // We only handle a few const functions for now. - if_chain! { - if args.is_empty(); - if let ExprKind::Path(qpath) = &callee.kind; - let res = self.typeck_results.qpath_res(qpath, callee.hir_id); - if let Some(def_id) = res.opt_def_id(); - let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect(); - let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect(); - if let ["core", "num", int_impl, "max_value"] = *def_path; - then { - let value = match int_impl { - "" => i8::MAX as u128, - "" => i16::MAX as u128, - "" => i32::MAX as u128, - "" => i64::MAX as u128, - "" => i128::MAX as u128, - _ => return None, - }; - Some(Constant::Int(value)) - } - else { - None - } - } - }, - 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, - } - } - - #[allow(clippy::cast_possible_wrap)] - fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { - use self::Constant::{Bool, Int}; - match *o { - Bool(b) => Some(Bool(!b)), - Int(value) => { - let value = !value; - match *ty.kind() { - ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), - ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), - _ => None, - } - }, - _ => None, - } - } - - fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { - use self::Constant::{Int, F32, F64}; - match *o { - Int(value) => { - let ity = match *ty.kind() { - ty::Int(ity) => ity, - _ => return None, - }; - // sign extend - let value = sext(self.lcx.tcx, value, ity); - let value = value.checked_neg()?; - // clear unused bits - Some(Int(unsext(self.lcx.tcx, value, ity))) - }, - F32(f) => Some(F32(-f)), - F64(f) => Some(F64(-f)), - _ => None, - } - } - - /// Create `Some(Vec![..])` of all constants, unless there is any - /// non-constant part. - fn multi(&mut self, vec: &[Expr<'_>]) -> Option> { - vec.iter().map(|elem| self.expr(elem)).collect::>() - } - - /// Lookup a possibly constant expression from a `ExprKind::Path`. - fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option { - let res = self.typeck_results.qpath_res(qpath, id); - match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - let substs = self.typeck_results.node_substs(id); - let substs = if self.substs.is_empty() { - substs - } else { - substs.subst(self.lcx.tcx, self.substs) - }; - - let result = self - .lcx - .tcx - .const_eval_resolve( - self.param_env, - ty::WithOptConstParam::unknown(def_id), - substs, - None, - None, - ) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?; - let result = miri_to_const(&result); - if result.is_some() { - self.needed_resolution = true; - } - result - }, - // FIXME: cover all usable cases. - _ => None, - } - } - - fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { - let lhs = self.expr(lhs); - let index = self.expr(index); - - match (lhs, index) { - (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(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, - } - } else { - None - } - }, - _ => None, - } - } - - /// A block can only yield a constant if it only has one constant expression. - fn block(&mut self, block: &Block<'_>) -> Option { - if block.stmts.is_empty() { - block.expr.as_ref().and_then(|b| self.expr(b)) - } else { - None - } - } - - fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { - if let Some(Constant::Bool(b)) = self.expr(cond) { - if b { - self.expr(&*then) - } else { - otherwise.as_ref().and_then(|expr| self.expr(expr)) - } - } else { - None - } - } - - fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option { - let l = self.expr(left)?; - let r = self.expr(right); - match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { - ty::Int(ity) => { - let l = sext(self.lcx.tcx, l, ity); - let r = sext(self.lcx.tcx, r, ity); - let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); - match op.node { - BinOpKind::Add => l.checked_add(r).map(zext), - BinOpKind::Sub => l.checked_sub(r).map(zext), - BinOpKind::Mul => l.checked_mul(r).map(zext), - BinOpKind::Div if r != 0 => l.checked_div(r).map(zext), - BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext), - BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext), - BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext), - BinOpKind::BitXor => Some(zext(l ^ r)), - BinOpKind::BitOr => Some(zext(l | r)), - BinOpKind::BitAnd => Some(zext(l & r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - } - }, - ty::Uint(_) => match op.node { - BinOpKind::Add => l.checked_add(r).map(Constant::Int), - BinOpKind::Sub => l.checked_sub(r).map(Constant::Int), - BinOpKind::Mul => l.checked_mul(r).map(Constant::Int), - BinOpKind::Div => l.checked_div(r).map(Constant::Int), - BinOpKind::Rem => l.checked_rem(r).map(Constant::Int), - BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int), - BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int), - BinOpKind::BitXor => Some(Constant::Int(l ^ r)), - BinOpKind::BitOr => Some(Constant::Int(l | r)), - BinOpKind::BitAnd => Some(Constant::Int(l & r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - }, - _ => None, - }, - (Constant::F32(l), Some(Constant::F32(r))) => match op.node { - BinOpKind::Add => Some(Constant::F32(l + r)), - BinOpKind::Sub => Some(Constant::F32(l - r)), - BinOpKind::Mul => Some(Constant::F32(l * r)), - BinOpKind::Div => Some(Constant::F32(l / r)), - BinOpKind::Rem => Some(Constant::F32(l % r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - }, - (Constant::F64(l), Some(Constant::F64(r))) => match op.node { - BinOpKind::Add => Some(Constant::F64(l + r)), - BinOpKind::Sub => Some(Constant::F64(l - r)), - BinOpKind::Mul => Some(Constant::F64(l * r)), - BinOpKind::Div => Some(Constant::F64(l / r)), - BinOpKind::Rem => Some(Constant::F64(l % r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - }, - (l, r) => match (op.node, l, r) { - (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)), - (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)), - (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => { - Some(r) - }, - (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)), - (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)), - (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)), - _ => None, - }, - } - } -} - -pub fn miri_to_const(result: &ty::Const<'_>) -> Option { - use rustc_middle::mir::interpret::ConstValue; - match result.val { - ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { - match result.ty.kind() { - ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), - ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), - ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( - int.try_into().expect("invalid f32 bit representation"), - ))), - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( - int.try_into().expect("invalid f64 bit representation"), - ))), - ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind() { - return Some(Constant::RawPtr(int.assert_bits(int.size()))); - } - None - }, - // FIXME: implement other conversions. - _ => None, - } - }, - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() { - ty::Ref(_, tam, _) => match tam.kind() { - ty::Str => String::from_utf8( - data.inspect_with_uninit_and_ptr_outside_interpreter(start..end) - .to_owned(), - ) - .ok() - .map(Constant::Str), - _ => None, - }, - _ => None, - }, - ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() { - ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match miri_to_const(len) { - Some(Constant::Int(len)) => alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) - .to_owned() - .chunks(4) - .map(|chunk| { - Some(Constant::F32(f32::from_le_bytes( - chunk.try_into().expect("this shouldn't happen"), - ))) - }) - .collect::>>() - .map(Constant::Vec), - _ => None, - }, - ty::Float(FloatTy::F64) => match miri_to_const(len) { - Some(Constant::Int(len)) => alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize)) - .to_owned() - .chunks(8) - .map(|chunk| { - Some(Constant::F64(f64::from_le_bytes( - chunk.try_into().expect("this shouldn't happen"), - ))) - }) - .collect::>>() - .map(Constant::Vec), - _ => None, - }, - // FIXME: implement other array type conversions. - _ => None, - }, - _ => None, - }, - // FIXME: implement other conversions. - _ => None, - } -} +pub use clippy_utils::consts::*; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d66e3720be0..381e6f9369f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -50,103 +50,23 @@ use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the -/// lint is triggered. -/// -/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. -/// As said in the README.md of this repository, if the lint level mapping changes, please update -/// README.md. -/// -/// # Example -/// -/// ``` -/// #![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). -/// /// -/// /// **Why is this bad?** Supply the reason for linting the code. -/// /// -/// /// **Known problems:** None. (Or describe where it could go wrong.) -/// /// -/// /// **Example:** -/// /// -/// /// ```rust -/// /// // Bad -/// /// Insert a short example of code that triggers the lint -/// /// -/// /// // Good -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints #[macro_export] macro_rules! declare_clippy_lint { - { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; + ( $($x:tt)* ) => { clippy_utils::declare_clippy_lint!($($x)*); } +} + +#[macro_export] +macro_rules! sym { + ( $($x:tt)* ) => { clippy_utils::sym!($($x)*); } +} + +#[macro_export] +macro_rules! unwrap_cargo_metadata { + ( $($x:tt)* ) => { clippy_utils::unwrap_cargo_metadata!($($x)*); } +} + +macro_rules! extract_msrv_attr { + ( $($x:tt)* ) => { clippy_utils::extract_msrv_attr!($($x)*); } } mod consts; diff --git a/clippy_lints/src/utils.rs b/clippy_lints/src/utils.rs new file mode 100644 index 00000000000..bf54e39769c --- /dev/null +++ b/clippy_lints/src/utils.rs @@ -0,0 +1 @@ +pub use clippy_utils::*; diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs deleted file mode 100644 index 44eb3968ae7..00000000000 --- a/clippy_lints/src/utils/ast_utils.rs +++ /dev/null @@ -1,562 +0,0 @@ -//! 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::ptr::P; -use rustc_ast::{self as ast, *}; -use rustc_span::symbol::Ident; -use std::mem; - -pub mod ident_iter; -pub use ident_iter::IdentIter; - -pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { - use BinOpKind::*; - matches!( - kind, - Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr - ) -} - -/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. -pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { - left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) -} - -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_struct_rest(l: &StructRest, r: &StructRest) -> bool { - match (l, r) { - (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), - (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, - _ => false, - } -} - -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_struct_rest(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: &Option

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

)` instead", - ); - } -} - /// lint use of `filter().map()` or `find().map()` for `Iterators` fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { if_chain! { diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs new file mode 100644 index 00000000000..8ba6ae95200 --- /dev/null +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -0,0 +1,20 @@ +use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SKIP_WHILE_NEXT; + +/// lint use of `skip_while().next()` for `Iterators` +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, _skip_while_args: &'tcx [hir::Expr<'_>]) { + // lint if caller of `.skip_while().next()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + span_lint_and_help( + cx, + SKIP_WHILE_NEXT, + expr.span, + "called `skip_while(

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

)` instead", + ); + } +} -- cgit 1.4.1-3-g733a5 From 110dac7f58257a0186daf13688aa8b02fba67bdb Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 3 Mar 2021 00:43:27 +0900 Subject: move option_as_ref_deref to its own module --- clippy_lints/src/methods/mod.rs | 114 +--------------------- clippy_lints/src/methods/option_as_ref_deref.rs | 122 ++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 111 deletions(-) create mode 100644 clippy_lints/src/methods/option_as_ref_deref.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 34def45a8dd..c6b6ef34bc4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -10,6 +10,7 @@ mod inspect_for_each; mod iter_count; mod manual_saturating_arithmetic; mod ok_expect; +mod option_as_ref_deref; mod option_map_unwrap_or; mod skip_while_next; mod unnecessary_filter_map; @@ -1725,10 +1726,10 @@ 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, self.msrv.as_ref()) + option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) }, ["map", "as_mut"] => { - lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) + option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) }, ["unwrap_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "get_or_insert"), @@ -3584,115 +3585,6 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { ); } -const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - -/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s -fn lint_option_as_ref_deref<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - as_ref_args: &[hir::Expr<'_>], - map_args: &[hir::Expr<'_>], - is_mut: bool, - msrv: Option<&RustcVersion>, -) { - if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { - return; - } - - let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); - - let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); - if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { - return; - } - - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, - &paths::DEREF_MUT_TRAIT_METHOD, - &paths::CSTRING_AS_C_STR, - &paths::OS_STRING_AS_OS_STR, - &paths::PATH_BUF_AS_PATH, - &paths::STRING_AS_STR, - &paths::STRING_AS_MUT_STR, - &paths::VEC_AS_SLICE, - &paths::VEC_AS_MUT_SLICE, - ]; - - let is_deref = match map_args[1].kind { - 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); - - match &closure_expr.kind { - hir::ExprKind::MethodCall(_, _, args, _) => { - if_chain! { - if args.len() == 1; - if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id); - let adj = cx - .typeck_results() - .expr_adjustments(&args[0]) - .iter() - .map(|x| &x.kind) - .collect::>(); - if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; - then { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) - } else { - false - } - } - }, - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => { - if_chain! { - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind; - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind; - then { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) - } else { - false - } - } - }, - _ => false, - } - }, - _ => false, - }; - - if is_deref { - let current_method = if is_mut { - format!(".as_mut().map({})", snippet(cx, map_args[1].span, "..")) - } else { - format!(".as_ref().map({})", snippet(cx, map_args[1].span, "..")) - }; - let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; - let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint); - let suggestion = format!("try using {} instead", method_hint); - - let msg = format!( - "called `{0}` on an Option value. This can be done more directly \ - by calling `{1}` instead", - current_method, hint - ); - span_lint_and_sugg( - cx, - OPTION_AS_REF_DEREF, - expr.span, - &msg, - &suggestion, - hint, - Applicability::MachineApplicable, - ); - } -} - fn lint_map_collect( cx: &LateContext<'_>, expr: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs new file mode 100644 index 00000000000..89067dbfe0e --- /dev/null +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -0,0 +1,122 @@ +use crate::utils::{ + is_type_diagnostic_item, match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, snippet, + span_lint_and_sugg, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_semver::RustcVersion; +use rustc_span::sym; + +use super::OPTION_AS_REF_DEREF; + +const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); + +/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + as_ref_args: &[hir::Expr<'_>], + map_args: &[hir::Expr<'_>], + is_mut: bool, + msrv: Option<&RustcVersion>, +) { + if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + return; + } + + let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); + + let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); + if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { + return; + } + + let deref_aliases: [&[&str]; 9] = [ + &paths::DEREF_TRAIT_METHOD, + &paths::DEREF_MUT_TRAIT_METHOD, + &paths::CSTRING_AS_C_STR, + &paths::OS_STRING_AS_OS_STR, + &paths::PATH_BUF_AS_PATH, + &paths::STRING_AS_STR, + &paths::STRING_AS_MUT_STR, + &paths::VEC_AS_SLICE, + &paths::VEC_AS_MUT_SLICE, + ]; + + let is_deref = match map_args[1].kind { + 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); + + match &closure_expr.kind { + hir::ExprKind::MethodCall(_, _, args, _) => { + if_chain! { + if args.len() == 1; + if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id); + let adj = cx + .typeck_results() + .expr_adjustments(&args[0]) + .iter() + .map(|x| &x.kind) + .collect::>(); + if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; + then { + let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); + deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + } else { + false + } + } + }, + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => { + if_chain! { + if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind; + if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind; + then { + path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + } else { + false + } + } + }, + _ => false, + } + }, + _ => false, + }; + + if is_deref { + let current_method = if is_mut { + format!(".as_mut().map({})", snippet(cx, map_args[1].span, "..")) + } else { + format!(".as_ref().map({})", snippet(cx, map_args[1].span, "..")) + }; + let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; + let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint); + let suggestion = format!("try using {} instead", method_hint); + + let msg = format!( + "called `{0}` on an Option value. This can be done more directly \ + by calling `{1}` instead", + current_method, hint + ); + span_lint_and_sugg( + cx, + OPTION_AS_REF_DEREF, + expr.span, + &msg, + &suggestion, + hint, + Applicability::MachineApplicable, + ); + } +} -- cgit 1.4.1-3-g733a5 From 60a053725e5c5c3a78e3d0918ca00e9e98324a95 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 3 Mar 2021 00:48:21 +0900 Subject: move suspicious_map to its own module --- clippy_lints/src/methods/mod.rs | 14 ++------------ clippy_lints/src/methods/suspicious_map.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 clippy_lints/src/methods/suspicious_map.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c6b6ef34bc4..a807afb6051 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -13,6 +13,7 @@ mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; mod skip_while_next; +mod suspicious_map; mod unnecessary_filter_map; mod unnecessary_lazy_eval; mod unwrap_used; @@ -1716,7 +1717,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { unnecessary_filter_map::lint(cx, expr, arg_lists[0]); filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); }, - ["count", "map"] => lint_suspicious_map(cx, expr), + ["count", "map"] => suspicious_map::check(cx, expr), ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr), ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) @@ -3574,17 +3575,6 @@ fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { } } -fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - span_lint_and_help( - cx, - SUSPICIOUS_MAP, - expr.span, - "this call to `map()` won't have an effect on the call to `count()`", - None, - "make sure you did not confuse `map` with `filter` or `for_each`", - ); -} - fn lint_map_collect( cx: &LateContext<'_>, expr: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs new file mode 100644 index 00000000000..e135a826dc4 --- /dev/null +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -0,0 +1,16 @@ +use crate::utils::span_lint_and_help; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SUSPICIOUS_MAP; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + expr.span, + "this call to `map()` won't have an effect on the call to `count()`", + None, + "make sure you did not confuse `map` with `filter` or `for_each`", + ); +} -- cgit 1.4.1-3-g733a5 From 8623b331eebfa097bb33dff1df5efa324e6a780a Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 3 Mar 2021 00:55:19 +0900 Subject: move filetype_is_file to its own module --- clippy_lints/src/methods/filetype_is_file.rs | 39 ++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 35 ++----------------------- 2 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 clippy_lints/src/methods/filetype_is_file.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs new file mode 100644 index 00000000000..b03835f97e6 --- /dev/null +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -0,0 +1,39 @@ +use crate::utils::{get_parent_expr, match_type, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +use super::FILETYPE_IS_FILE; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let ty = cx.typeck_results().expr_ty(&args[0]); + + if !match_type(cx, ty, &paths::FILE_TYPE) { + return; + } + + let span: Span; + let verb: &str; + let lint_unary: &str; + let help_unary: &str; + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::Unary(op, _) = parent.kind; + if op == hir::UnOp::Not; + then { + lint_unary = "!"; + verb = "denies"; + help_unary = ""; + span = parent.span; + } else { + lint_unary = ""; + verb = "covers"; + help_unary = "!"; + span = expr.span; + } + } + let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); + let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); + span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a807afb6051..54a6a4cf934 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,6 +1,7 @@ mod bind_instead_of_map; mod bytes_nth; mod expect_used; +mod filetype_is_file; mod filter_map_identity; mod filter_next; mod get_unwrap; @@ -1725,7 +1726,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => { check_pointer_offset(cx, expr, arg_lists[0]) }, - ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), + ["is_file", ..] => filetype_is_file::check(cx, expr, arg_lists[0]), ["map", "as_ref"] => { option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) }, @@ -3859,38 +3860,6 @@ fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir: } } -fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let ty = cx.typeck_results().expr_ty(&args[0]); - - if !match_type(cx, ty, &paths::FILE_TYPE) { - return; - } - - let span: Span; - let verb: &str; - let lint_unary: &str; - let help_unary: &str; - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::Unary(op, _) = parent.kind; - if op == hir::UnOp::Not; - then { - lint_unary = "!"; - verb = "denies"; - help_unary = ""; - span = parent.span; - } else { - lint_unary = ""; - verb = "covers"; - help_unary = "!"; - span = expr.span; - } - } - let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); - let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); - span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); -} - fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); -- cgit 1.4.1-3-g733a5 From 35147d4cf342ee479d50afcb70aceab4ea506c2d Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 3 Mar 2021 01:03:47 +0900 Subject: move uninit_assumed_init to its own module --- clippy_lints/src/methods/mod.rs | 31 ++-------------------- clippy_lints/src/methods/uninit_assumed_init.rs | 35 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 clippy_lints/src/methods/uninit_assumed_init.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 54a6a4cf934..acb8318970e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,6 +15,7 @@ mod option_as_ref_deref; mod option_map_unwrap_or; mod skip_while_next; mod suspicious_map; +mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_lazy_eval; mod unwrap_used; @@ -1719,7 +1720,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); }, ["count", "map"] => suspicious_map::check(cx, expr), - ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr), + ["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr), ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) }, @@ -3548,34 +3549,6 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_ } } -/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) -fn lint_maybe_uninit(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Call(ref callee, ref args) = expr.kind; - if args.is_empty(); - if let hir::ExprKind::Path(ref path) = callee.kind; - if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); - if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(outer)); - then { - span_lint( - cx, - UNINIT_ASSUMED_INIT, - outer.span, - "this call for this type may be undefined behavior" - ); - } - } -} - -fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), - ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), - ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), - _ => false, - } -} - fn lint_map_collect( cx: &LateContext<'_>, expr: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs new file mode 100644 index 00000000000..798b66192c8 --- /dev/null +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -0,0 +1,35 @@ +use crate::utils::{match_def_path, match_qpath, paths, span_lint}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::UNINIT_ASSUMED_INIT; + +/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::Call(ref callee, ref args) = expr.kind; + if args.is_empty(); + if let hir::ExprKind::Path(ref path) = callee.kind; + if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(outer)); + then { + span_lint( + cx, + UNINIT_ASSUMED_INIT, + outer.span, + "this call for this type may be undefined behavior" + ); + } + } +} + +fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), + ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), + ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), + _ => false, + } +} -- cgit 1.4.1-3-g733a5 From 45ee914df0c1d1a6a15272bd6833023dd4185b9b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 3 Mar 2021 01:09:33 +0900 Subject: move iter_cloned_collect to its own module --- clippy_lints/src/methods/iter_cloned_collect.rs | 30 +++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 24 ++------------------ 2 files changed, 32 insertions(+), 22 deletions(-) create mode 100644 clippy_lints/src/methods/iter_cloned_collect.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs new file mode 100644 index 00000000000..c3e48ffa5fa --- /dev/null +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -0,0 +1,30 @@ +use crate::methods::derefs_to_slice; +use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITER_CLONED_COLLECT; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { + if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); + if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])); + if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); + + then { + span_lint_and_sugg( + cx, + 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", + "try", + ".to_vec()".to_string(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index acb8318970e..92d8077c1ab 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -8,6 +8,7 @@ mod get_unwrap; mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; +mod iter_cloned_collect; mod iter_count; mod manual_saturating_arithmetic; mod ok_expect; @@ -1711,7 +1712,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["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, arg_lists[1]), - ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), + ["collect", "cloned"] => iter_cloned_collect::check(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]), ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]), @@ -2494,27 +2495,6 @@ fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_> } } -fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); - if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])); - if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); - - then { - span_lint_and_sugg( - cx, - 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", - "try", - ".to_vec()".to_string(), - Applicability::MachineApplicable, - ); - } - } -} - fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { fn check_fold_with_op( cx: &LateContext<'_>, -- cgit 1.4.1-3-g733a5 From 6376da70be18d6cb9dbe22b5a661896cf916bc51 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 3 Mar 2021 01:16:16 +0900 Subject: replace `lints` and `lint` with `check` --- clippy_lints/src/methods/bind_instead_of_map.rs | 2 +- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/inspect_for_each.rs | 2 +- clippy_lints/src/methods/iter_count.rs | 2 +- .../src/methods/manual_saturating_arithmetic.rs | 2 +- clippy_lints/src/methods/mod.rs | 22 +++++++++++----------- clippy_lints/src/methods/unnecessary_filter_map.rs | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 540a1484a85..5decb81d9f2 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -158,7 +158,7 @@ pub(crate) trait BindInsteadOfMap { } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { + fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) { return false; } diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index defc50ede22..71a7e195e41 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -7,7 +7,7 @@ use rustc_span::sym; use super::BYTES_NTH; -pub(super) fn lints<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>]) { if_chain! { if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; let ty = cx.typeck_results().expr_ty(&iter_args[0]).peel_refs(); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index c83b6f2c329..3045b09c238 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::sym; /// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { if_chain! { if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index 6d41ee38a27..959457a5bfc 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -7,7 +7,7 @@ use crate::utils::{match_trait_method, paths, span_lint_and_help}; use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` -pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { if match_trait_method(cx, expr, &paths::ITERATOR) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 71d65a01d3e..869440e0165 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -8,7 +8,7 @@ use rustc_span::sym; use super::ITER_COUNT; -pub(crate) fn lints<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], iter_method: &str) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], iter_method: &str) { let ty = cx.typeck_results().expr_ty(&iter_args[0]); let caller_type = if derefs_to_slice(cx, &iter_args[0], ty).is_some() { "slice" diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index eaa604c2ae6..0b414e0eb95 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_lint::LateContext; use rustc_target::abi::LayoutOf; -pub fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) { +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) { let unwrap_arg = &args[0][1]; let arith_lhs = &args[1][0]; let arith_rhs = &args[1][1]; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 92d8077c1ab..639d013f786 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1673,14 +1673,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { - 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]); + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, arg_lists[0]); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, arg_lists[0]); if !biom_option_linted && !biom_result_linted { unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "and"); } }, ["or_else", ..] => { - if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) { + if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, arg_lists[0]) { unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "or"); } }, @@ -1703,12 +1703,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) }, ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), - ["count", "into_iter"] => iter_count::lints(cx, expr, &arg_lists[1], "into_iter"), - ["count", "iter"] => iter_count::lints(cx, expr, &arg_lists[1], "iter"), - ["count", "iter_mut"] => iter_count::lints(cx, expr, &arg_lists[1], "iter_mut"), + ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), + ["count", "iter"] => iter_count::check(cx, expr, &arg_lists[1], "iter"), + ["count", "iter_mut"] => iter_count::check(cx, expr, &arg_lists[1], "iter_mut"), ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), - ["nth", "bytes"] => bytes_nth::lints(cx, expr, &arg_lists[1]), + ["nth", "bytes"] => bytes_nth::check(cx, expr, &arg_lists[1]), ["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, arg_lists[1]), @@ -1717,13 +1717,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]), ["filter_map", ..] => { - unnecessary_filter_map::lint(cx, expr, arg_lists[0]); + unnecessary_filter_map::check(cx, expr, arg_lists[0]); filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); }, ["count", "map"] => suspicious_map::check(cx, expr), ["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr), ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { - manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) + manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..]) }, ["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => { check_pointer_offset(cx, expr, arg_lists[0]) @@ -1739,7 +1739,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["get_or_insert_with", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "ok_or"), ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), - ["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]), + ["for_each", "inspect"] => inspect_for_each::check(cx, expr, method_spans[1]), ["to_owned", ..] => implicit_clone::check(cx, expr, sym::ToOwned), ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), @@ -1765,7 +1765,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_clone_on_ref_ptr(cx, expr, &args[0]); } if args.len() == 1 && method_call.ident.name == sym!(to_string) { - inefficient_to_string::lint(cx, expr, &args[0], self_ty); + inefficient_to_string::check(cx, expr, &args[0], self_ty); } if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 5691fcb88e9..12b2cf0a165 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -9,7 +9,7 @@ use if_chain::if_chain; use super::UNNECESSARY_FILTER_MAP; -pub(super) fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if !match_trait_method(cx, expr, &paths::ITERATOR) { return; } -- cgit 1.4.1-3-g733a5 From 9b0a42b67d66cb79bdd09379d0b0e7959fc1505a Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 4 Mar 2021 19:46:44 +0900 Subject: move zst_offset to its own module --- clippy_lints/src/methods/mod.rs | 15 ++------------- clippy_lints/src/methods/zst_offset.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 clippy_lints/src/methods/zst_offset.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 639d013f786..c944568dc61 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -20,6 +20,7 @@ mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_lazy_eval; mod unwrap_used; +mod zst_offset; use std::borrow::Cow; use std::fmt; @@ -1726,7 +1727,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..]) }, ["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => { - check_pointer_offset(cx, expr, arg_lists[0]) + zst_offset::check(cx, expr, arg_lists[0]) }, ["is_file", ..] => filetype_is_file::check(cx, expr, arg_lists[0]), ["map", "as_ref"] => { @@ -3801,18 +3802,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } } -fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - if_chain! { - if args.len() == 2; - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind(); - if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); - if layout.is_zst(); - then { - span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); - } - } -} - fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs new file mode 100644 index 00000000000..f1335726736 --- /dev/null +++ b/clippy_lints/src/methods/zst_offset.rs @@ -0,0 +1,19 @@ +use crate::utils::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::ZST_OFFSET; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if_chain! { + if args.len() == 2; + if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind(); + if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); + if layout.is_zst(); + then { + span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); + } + } +} -- cgit 1.4.1-3-g733a5 From db91d9cf9a620cc258ab84a152a9333351ac166e Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 4 Mar 2021 19:47:06 +0900 Subject: move map_collect_result_unit to its own module --- .../src/methods/map_collect_result_unit.rs | 45 ++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 39 +------------------ 2 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 clippy_lints/src/methods/map_collect_result_unit.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs new file mode 100644 index 00000000000..5b20e268d9f --- /dev/null +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -0,0 +1,45 @@ +use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::MAP_COLLECT_RESULT_UNIT; + +pub(super) fn check( + 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, + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c944568dc61..baa342ab3bb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -11,6 +11,7 @@ mod inspect_for_each; mod iter_cloned_collect; mod iter_count; mod manual_saturating_arithmetic; +mod map_collect_result_unit; mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; @@ -1739,7 +1740,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "ok_or"), - ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), + ["collect", "map"] => map_collect_result_unit::check(cx, expr, arg_lists[1], arg_lists[0]), ["for_each", "inspect"] => inspect_for_each::check(cx, expr, method_spans[1]), ["to_owned", ..] => implicit_clone::check(cx, expr, sym::ToOwned), ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), @@ -3530,42 +3531,6 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_ } } -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, - ); - } - } -} - enum Convention { Eq(&'static str), StartsWith(&'static str), -- cgit 1.4.1-3-g733a5 From 3fe099ba8d2f6f4d085e7480f020f9f87e6b39e8 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 4 Mar 2021 22:50:53 +0900 Subject: move iter_next_slice to its own module --- clippy_lints/src/methods/iter_next_slice.rs | 68 +++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 66 +++------------------------- 2 files changed, 73 insertions(+), 61 deletions(-) create mode 100644 clippy_lints/src/methods/iter_next_slice.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs new file mode 100644 index 00000000000..3c03a949cfe --- /dev/null +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -0,0 +1,68 @@ +use crate::methods::derefs_to_slice; +use crate::utils::{get_parent_expr, higher, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::ITER_NEXT_SLICE; + +pub(super) fn check<'tcx>(cx: &LateContext<'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.typeck_results().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(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.typeck_results().expr_ty(caller_expr), sym::vec_type) + || matches!( + &cx.typeck_results().expr_ty(caller_expr).peel_refs().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, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index baa342ab3bb..46c9524af55 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -10,6 +10,7 @@ mod inefficient_to_string; mod inspect_for_each; mod iter_cloned_collect; mod iter_count; +mod iter_next_slice; mod manual_saturating_arithmetic; mod map_collect_result_unit; mod ok_expect; @@ -46,9 +47,9 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_return, contains_ty, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, - match_qpath, match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, + contains_return, contains_ty, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, + is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, + match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth, SpanlessEq, @@ -1688,7 +1689,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ["next", "filter"] => filter_next::check(cx, expr, arg_lists[1]), ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), - ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), + ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, false), ["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], self.msrv.as_ref()), @@ -2598,63 +2599,6 @@ fn lint_step_by<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx } } -fn lint_iter_next<'tcx>(cx: &LateContext<'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.typeck_results().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(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.typeck_results().expr_ty(caller_expr), sym::vec_type) - || matches!( - &cx.typeck_results().expr_ty(caller_expr).peel_refs().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<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, -- cgit 1.4.1-3-g733a5 From 5912ca986c52041d01384393cbf2da10270738ee Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 4 Mar 2021 23:06:05 +0900 Subject: move iter_nth, iter_nth_zero and iterator_step_by_zero to their own module --- clippy_lints/src/methods/iter_nth.rs | 38 ++++++++++++ clippy_lints/src/methods/iter_nth_zero.rs | 27 +++++++++ clippy_lints/src/methods/iterator_step_by_zero.rs | 19 ++++++ clippy_lints/src/methods/mod.rs | 74 +++-------------------- 4 files changed, 91 insertions(+), 67 deletions(-) create mode 100644 clippy_lints/src/methods/iter_nth.rs create mode 100644 clippy_lints/src/methods/iter_nth_zero.rs create mode 100644 clippy_lints/src/methods/iterator_step_by_zero.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs new file mode 100644 index 00000000000..c8adea9536b --- /dev/null +++ b/clippy_lints/src/methods/iter_nth.rs @@ -0,0 +1,38 @@ +use crate::methods::derefs_to_slice; +use crate::methods::iter_nth_zero; +use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::ITER_NTH; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]], + is_mut: bool, +) { + let iter_args = nth_and_iter_args[1]; + let mut_str = if is_mut { "_mut" } else { "" }; + let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { + "slice" + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { + "Vec" + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { + "VecDeque" + } else { + let nth_args = nth_and_iter_args[0]; + iter_nth_zero::check(cx, expr, &nth_args); + return; // caller is not a type that we want to lint + }; + + span_lint_and_help( + cx, + ITER_NTH, + expr.span, + &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), + None, + &format!("calling `.get{}()` is both faster and more readable", mut_str), + ); +} diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs new file mode 100644 index 00000000000..247192d81f3 --- /dev/null +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -0,0 +1,27 @@ +use crate::consts::{constant, Constant}; +use crate::utils::{match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::ITER_NTH_ZERO; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) { + if_chain! { + if match_trait_method(cx, expr, &paths::ITERATOR); + if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NTH_ZERO, + expr.span, + "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/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs new file mode 100644 index 00000000000..3e05d7f76b7 --- /dev/null +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -0,0 +1,19 @@ +use crate::consts::{constant, Constant}; +use crate::utils::{match_trait_method, paths, span_lint}; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::ITERATOR_STEP_BY_ZERO; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) { + span_lint( + cx, + ITERATOR_STEP_BY_ZERO, + expr.span, + "`Iterator::step_by(0)` will panic at runtime", + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 46c9524af55..259d370550c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -11,6 +11,9 @@ mod inspect_for_each; mod iter_cloned_collect; mod iter_count; mod iter_next_slice; +mod iter_nth; +mod iter_nth_zero; +mod iterator_step_by_zero; mod manual_saturating_arithmetic; mod map_collect_result_unit; mod ok_expect; @@ -43,7 +46,6 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol, SymbolStr}; use rustc_typeck::hir_ty_to_ty; -use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ @@ -1709,11 +1711,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), ["count", "iter"] => iter_count::check(cx, expr, &arg_lists[1], "iter"), ["count", "iter_mut"] => iter_count::check(cx, expr, &arg_lists[1], "iter_mut"), - ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), - ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), + ["nth", "iter"] => iter_nth::check(cx, expr, &arg_lists, false), + ["nth", "iter_mut"] => iter_nth::check(cx, expr, &arg_lists, true), ["nth", "bytes"] => bytes_nth::check(cx, expr, &arg_lists[1]), - ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), - ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), + ["nth", ..] => iter_nth_zero::check(cx, expr, arg_lists[0]), + ["step_by", ..] => iterator_step_by_zero::check(cx, expr, arg_lists[0]), ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), ["collect", "cloned"] => iter_cloned_collect::check(cx, expr, arg_lists[1]), ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), @@ -2586,68 +2588,6 @@ fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: } } -fn lint_step_by<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) { - span_lint( - cx, - ITERATOR_STEP_BY_ZERO, - expr.span, - "`Iterator::step_by(0)` will panic at runtime", - ); - } - } -} - -fn lint_iter_nth<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]], - is_mut: bool, -) { - let iter_args = nth_and_iter_args[1]; - let mut_str = if is_mut { "_mut" } else { "" }; - let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { - "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { - "Vec" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vecdeque_type) { - "VecDeque" - } else { - let nth_args = nth_and_iter_args[0]; - lint_iter_nth_zero(cx, expr, &nth_args); - return; // caller is not a type that we want to lint - }; - - span_lint_and_help( - cx, - ITER_NTH, - expr.span, - &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), - None, - &format!("calling `.get{}()` is both faster and more readable", mut_str), - ); -} - -fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) { - if_chain! { - if match_trait_method(cx, expr, &paths::ITERATOR); - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - ITER_NTH_ZERO, - expr.span, - "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, - ); - } - } -} - 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) { -- cgit 1.4.1-3-g733a5 From b5d809a660240d5c826989839bd5eaab63fbb312 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 4 Mar 2021 23:47:30 +0900 Subject: move wrong_self_convention to its own module --- clippy_lints/src/methods/mod.rs | 81 +++-------------------- clippy_lints/src/methods/wrong_self_convention.rs | 76 +++++++++++++++++++++ 2 files changed, 86 insertions(+), 71 deletions(-) create mode 100644 clippy_lints/src/methods/wrong_self_convention.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 259d370550c..3cd720bc9ca 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -25,10 +25,10 @@ mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_lazy_eval; mod unwrap_used; +mod wrong_self_convention; mod zst_offset; use std::borrow::Cow; -use std::fmt; use std::iter; use bind_instead_of_map::BindInsteadOfMap; @@ -1868,7 +1868,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - lint_wrong_self_convention( + wrong_self_convention::check( cx, &name, item.vis.node.is_pub(), @@ -1924,7 +1924,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty(); then { - lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span); + wrong_self_convention::check( + cx, + &item.ident.name.as_str(), + false, + self_ty, + first_arg_ty, + first_arg_span + ); } } @@ -1949,39 +1956,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -fn lint_wrong_self_convention<'tcx>( - cx: &LateContext<'tcx>, - item_name: &str, - is_pub: bool, - self_ty: &'tcx TyS<'tcx>, - first_arg_ty: &'tcx TyS<'tcx>, - first_arg_span: Span, -) { - let lint = if is_pub { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; - if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( - cx, - lint, - first_arg_span, - &format!( - "methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } -} - /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( @@ -3415,22 +3389,6 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_ } } -enum Convention { - Eq(&'static str), - StartsWith(&'static str), -} - -#[rustfmt::skip] -const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [ - (Convention::Eq("new"), &[SelfKind::No]), - (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]), - (Convention::StartsWith("from_"), &[SelfKind::No]), - (Convention::StartsWith("into_"), &[SelfKind::Value]), - (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]), - (Convention::Eq("to_mut"), &[SelfKind::RefMut]), - (Convention::StartsWith("to_"), &[SelfKind::Ref]), -]; - const FN_HEADER: hir::FnHeader = hir::FnHeader { unsafety: hir::Unsafety::Normal, constness: hir::Constness::NotConst, @@ -3602,25 +3560,6 @@ impl SelfKind { } } -impl Convention { - #[must_use] - fn check(&self, other: &str) -> bool { - match *self { - Self::Eq(this) => this == other, - Self::StartsWith(this) => other.starts_with(this) && this != other, - } - } -} - -impl fmt::Display for Convention { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - Self::Eq(this) => this.fmt(f), - Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), - } - } -} - #[derive(Clone, Copy)] enum OutType { Unit, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs new file mode 100644 index 00000000000..90fab577436 --- /dev/null +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -0,0 +1,76 @@ +use crate::methods::SelfKind; +use crate::utils::span_lint; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; +use rustc_span::source_map::Span; +use std::fmt; + +use super::WRONG_PUB_SELF_CONVENTION; +use super::WRONG_SELF_CONVENTION; + +#[rustfmt::skip] +const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [ + (Convention::Eq("new"), &[SelfKind::No]), + (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]), + (Convention::StartsWith("from_"), &[SelfKind::No]), + (Convention::StartsWith("into_"), &[SelfKind::Value]), + (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]), + (Convention::Eq("to_mut"), &[SelfKind::RefMut]), + (Convention::StartsWith("to_"), &[SelfKind::Ref]), +]; +enum Convention { + Eq(&'static str), + StartsWith(&'static str), +} + +impl Convention { + #[must_use] + fn check(&self, other: &str) -> bool { + match *self { + Self::Eq(this) => this == other, + Self::StartsWith(this) => other.starts_with(this) && this != other, + } + } +} + +impl fmt::Display for Convention { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match *self { + Self::Eq(this) => this.fmt(f), + Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), + } + } +} + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item_name: &str, + is_pub: bool, + self_ty: &'tcx TyS<'tcx>, + first_arg_ty: &'tcx TyS<'tcx>, + first_arg_span: Span, +) { + let lint = if is_pub { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + lint, + first_arg_span, + &format!( + "methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } +} -- cgit 1.4.1-3-g733a5 From 2ade32ddf2ec13a6ec2e03c14e7393ce784d4d18 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 5 Mar 2021 00:16:43 +0900 Subject: move string_extend_chars and clone_on_ref_ptr to their own module --- clippy_lints/src/methods/clone_on_ref_ptr.rs | 36 +++++++++++++ clippy_lints/src/methods/mod.rs | 72 ++----------------------- clippy_lints/src/methods/string_extend_chars.rs | 42 +++++++++++++++ 3 files changed, 82 insertions(+), 68 deletions(-) create mode 100644 clippy_lints/src/methods/clone_on_ref_ptr.rs create mode 100644 clippy_lints/src/methods/string_extend_chars.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs new file mode 100644 index 00000000000..3d5a68d69d7 --- /dev/null +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -0,0 +1,36 @@ +use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_macro_callsite, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::CLONE_ON_REF_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + 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) { + "Rc" + } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { + "Arc" + } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) { + "Weak" + } else { + return; + }; + + let snippet = snippet_with_macro_callsite(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), + Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3cd720bc9ca..480f32a4340 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,5 +1,6 @@ mod bind_instead_of_map; mod bytes_nth; +mod clone_on_ref_ptr; mod expect_used; mod filetype_is_file; mod filter_map_identity; @@ -20,6 +21,7 @@ mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; mod skip_while_next; +mod string_extend_chars; mod suspicious_map; mod uninit_assumed_init; mod unnecessary_filter_map; @@ -1707,7 +1709,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_some", "rposition"] => { lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) }, - ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), + ["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]), ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), ["count", "iter"] => iter_count::check(cx, expr, &arg_lists[1], "iter"), ["count", "iter_mut"] => iter_count::check(cx, expr, &arg_lists[1], "iter_mut"), @@ -1767,7 +1769,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); if args.len() == 1 && method_call.ident.name == sym::clone { lint_clone_on_copy(cx, expr, &args[0], self_ty); - lint_clone_on_ref_ptr(cx, expr, &args[0]); + clone_on_ref_ptr::check(cx, expr, &args[0]); } if args.len() == 1 && method_call.ident.name == sym!(to_string) { inefficient_to_string::check(cx, expr, &args[0], self_ty); @@ -2408,72 +2410,6 @@ 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 = 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) { - "Rc" - } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { - "Arc" - } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) { - "Weak" - } else { - return; - }; - - let snippet = snippet_with_macro_callsite(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), - Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak - ); - } -} - -fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let arg = &args[1]; - if let Some(arglists) = method_chain_args(arg, &["chars"]) { - let target = &arglists[0][0]; - let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); - let ref_str = if *self_ty.kind() == ty::Str { - "" - } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { - "&" - } else { - return; - }; - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - STRING_EXTEND_CHARS, - expr.span, - "calling `.extend(_.chars())`", - "try this", - format!( - "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "..", &mut applicability), - ref_str, - snippet_with_applicability(cx, target.span, "..", &mut applicability) - ), - applicability, - ); - } -} - -fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - if is_type_diagnostic_item(cx, obj_ty, sym::string_type) { - lint_string_extend(cx, expr, args); - } -} - fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { fn check_fold_with_op( cx: &LateContext<'_>, diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs new file mode 100644 index 00000000000..0a08ea26175 --- /dev/null +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -0,0 +1,42 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::STRING_EXTEND_CHARS; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + if is_type_diagnostic_item(cx, obj_ty, sym::string_type) { + let arg = &args[1]; + if let Some(arglists) = method_chain_args(arg, &["chars"]) { + let target = &arglists[0][0]; + let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); + let ref_str = if *self_ty.kind() == ty::Str { + "" + } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { + "&" + } else { + return; + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + STRING_EXTEND_CHARS, + expr.span, + "calling `.extend(_.chars())`", + "try this", + format!( + "{}.push_str({}{})", + snippet_with_applicability(cx, args[0].span, "..", &mut applicability), + ref_str, + snippet_with_applicability(cx, target.span, "..", &mut applicability) + ), + applicability, + ); + } + } +} -- cgit 1.4.1-3-g733a5 From 8006dab817e6bfa0a71a593d46c5569e8a98a6b7 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 15:29:02 +0900 Subject: move single_char_insert_string to its own module --- clippy_lints/src/methods/mod.rs | 23 ++---------------- .../src/methods/single_char_insert_string.rs | 27 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 clippy_lints/src/methods/single_char_insert_string.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 480f32a4340..ef671e41cb5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -20,6 +20,7 @@ mod map_collect_result_unit; mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; +mod single_char_insert_string; mod skip_while_next; mod string_extend_chars; mod suspicious_map; @@ -1779,7 +1780,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { lint_single_char_push_string(cx, expr, args); } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { - lint_single_char_insert_string(cx, expr, args); + single_char_insert_string::check(cx, expr, args); } } @@ -3235,26 +3236,6 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args } } -/// lint for length-1 `str`s as argument for `insert_str` -fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { - let base_string_snippet = - snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); - let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character string literal", - "consider using `insert` with a character literal", - sugg, - applicability, - ); - } -} - /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs new file mode 100644 index 00000000000..0ce8b66978d --- /dev/null +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -0,0 +1,27 @@ +use crate::methods::get_hint_if_single_char_arg; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SINGLE_CHAR_ADD_STR; + +/// lint for length-1 `str`s as argument for `insert_str` +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); + let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); + let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `insert_str()` using a single-character string literal", + "consider using `insert` with a character literal", + sugg, + applicability, + ); + } +} -- cgit 1.4.1-3-g733a5 From 805aa47f43ab9b61a69f63682176604cdcda8fae Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 15:39:28 +0900 Subject: move single_char_push_string to its own module --- clippy_lints/src/methods/mod.rs | 22 ++---------------- .../src/methods/single_char_push_string.rs | 26 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 clippy_lints/src/methods/single_char_push_string.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ef671e41cb5..24d610cf5f8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -21,6 +21,7 @@ mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; mod single_char_insert_string; +mod single_char_push_string; mod skip_while_next; mod string_extend_chars; mod suspicious_map; @@ -1778,7 +1779,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { - lint_single_char_push_string(cx, expr, args); + single_char_push_string::check(cx, expr, args); } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { single_char_insert_string::check(cx, expr, args); } @@ -3217,25 +3218,6 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h } } -/// lint for length-1 `str`s as argument for `push_str` -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.source_callsite(), "..", &mut applicability); - let sugg = format!("{}.push({})", base_string_snippet, extension_string); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_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/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs new file mode 100644 index 00000000000..deacc70b713 --- /dev/null +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -0,0 +1,26 @@ +use crate::methods::get_hint_if_single_char_arg; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SINGLE_CHAR_ADD_STR; + +/// lint for length-1 `str`s as argument for `push_str` +pub(super) fn check(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.source_callsite(), "..", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} -- cgit 1.4.1-3-g733a5 From 0c8d143515a19b04a404765ecb8145a183dc2186 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 17:28:14 +0900 Subject: move into_iter_on_ref and single_char_pattern to their own modules --- clippy_lints/src/methods/into_iter_on_ref.rs | 42 ++++++++++++++++ clippy_lints/src/methods/mod.rs | 67 ++++--------------------- clippy_lints/src/methods/single_char_pattern.rs | 23 +++++++++ 3 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 clippy_lints/src/methods/into_iter_on_ref.rs create mode 100644 clippy_lints/src/methods/single_char_pattern.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs new file mode 100644 index 00000000000..d94b243404c --- /dev/null +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -0,0 +1,42 @@ +use crate::utils::{has_iter_method, match_trait_method, paths, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::source_map::Span; + +use super::INTO_ITER_ON_REF; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) { + if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) { + return; + } + if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) { + span_lint_and_sugg( + cx, + INTO_ITER_ON_REF, + method_span, + &format!( + "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", + method_name, kind, + ), + "call directly", + method_name.to_string(), + Applicability::MachineApplicable, + ); + } +} + +fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> { + has_iter_method(cx, self_ref_ty).map(|ty_name| { + let mutbl = match self_ref_ty.kind() { + ty::Ref(_, _, mutbl) => mutbl, + _ => unreachable!(), + }; + let method_name = match mutbl { + hir::Mutability::Not => "iter", + hir::Mutability::Mut => "iter_mut", + }; + (ty_name, method_name) + }) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 24d610cf5f8..28e2d896e6c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -9,6 +9,7 @@ mod get_unwrap; mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; +mod into_iter_on_ref; mod iter_cloned_collect; mod iter_count; mod iter_next_slice; @@ -21,6 +22,7 @@ mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; mod single_char_insert_string; +mod single_char_pattern; mod single_char_push_string; mod skip_while_next; mod string_extend_chars; @@ -53,12 +55,11 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_return, contains_ty, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, - is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, - match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth, - SpanlessEq, + contains_return, contains_ty, get_parent_expr, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, + is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, + match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty, + single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1789,12 +1790,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { for &(method, pos) in &PATTERN_METHODS { if method_call.ident.name.as_str() == method && args.len() > pos { - lint_single_char_pattern(cx, expr, &args[pos]); + single_char_pattern::check(cx, expr, &args[pos]); } } }, ty::Ref(..) if method_call.ident.name == sym::into_iter => { - lint_into_iter(cx, expr, self_ty, *method_span); + into_iter_on_ref::check(cx, expr, self_ty, *method_span); }, _ => (), } @@ -3202,22 +3203,6 @@ fn get_hint_if_single_char_arg( } } -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -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( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - 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" @@ -3254,40 +3239,6 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re } } -fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { - has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind() { - ty::Ref(_, _, mutbl) => mutbl, - _ => unreachable!(), - }; - let method_name = match mutbl { - hir::Mutability::Not => "iter", - hir::Mutability::Mut => "iter_mut", - }; - (ty_name, method_name) - }) -} - -fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) { - if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) { - return; - } - if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) { - span_lint_and_sugg( - cx, - INTO_ITER_ON_REF, - method_span, - &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", - method_name, kind, - ), - "call directly", - method_name.to_string(), - Applicability::MachineApplicable, - ); - } -} - const FN_HEADER: hir::FnHeader = hir::FnHeader { unsafety: hir::Unsafety::Normal, constness: hir::Constness::NotConst, diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs new file mode 100644 index 00000000000..61cbc9d2f0a --- /dev/null +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -0,0 +1,23 @@ +use crate::methods::get_hint_if_single_char_arg; +use crate::utils::span_lint_and_sugg; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SINGLE_CHAR_PATTERN; + +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +pub(super) fn check(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( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } +} -- cgit 1.4.1-3-g733a5 From 2561b7ea062f0c07f27d9ebdf0c355ab29a4edf9 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 17:46:50 +0900 Subject: move from_iter_insteam_of_collect to its own module --- .../src/methods/from_iter_instead_of_collect.rs | 67 ++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 62 +------------------- 2 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 clippy_lints/src/methods/from_iter_instead_of_collect.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs new file mode 100644 index 00000000000..e50d0a33400 --- /dev/null +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -0,0 +1,67 @@ +use crate::utils::{get_trait_def_id, implements_trait, paths, span_lint_and_sugg, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::Ty; + +use super::FROM_ITER_INSTEAD_OF_COLLECT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let ty = cx.typeck_results().expr_ty(expr); + let arg_ty = cx.typeck_results().expr_ty(&args[0]); + + if_chain! { + if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); + if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR); + + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + then { + // `expr` implements `FromIterator` trait + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let turbofish = extract_turbofish(cx, expr, ty); + let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); + span_lint_and_sugg( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "usage of `FromIterator::from_iter`", + "use `.collect()` instead of `::from_iter()`", + sugg, + Applicability::MaybeIncorrect, + ); + } + } +} + +fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { + if_chain! { + let call_site = expr.span.source_callsite(); + if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); + let snippet_split = snippet.split("::").collect::>(); + if let Some((_, elements)) = snippet_split.split_last(); + + then { + // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) + if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) { + // remove the type specifier from the path elements + let without_ts = elements.iter().filter_map(|e| { + if e == type_specifier { None } else { Some((*e).to_string()) } + }).collect::>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) + format!("{}{}", without_ts.join("::"), type_specifier) + } else { + // type is not explicitly specified so wildcards are needed + // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` + let ty_str = ty.to_string(); + let start = ty_str.find('<').unwrap_or(0); + let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); + let nb_wildcard = ty_str[start..end].split(',').count(); + let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); + format!("{}<{}>", elements.join("::"), wildcards) + } + } else { + ty.to_string() + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 28e2d896e6c..9a0ef6c01c1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5,6 +5,7 @@ mod expect_used; mod filetype_is_file; mod filter_map_identity; mod filter_next; +mod from_iter_instead_of_collect; mod get_unwrap; mod implicit_clone; mod inefficient_to_string; @@ -1761,7 +1762,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { hir::ExprKind::Call(ref func, ref args) => { if let hir::ExprKind::Path(path) = &func.kind { if match_qpath(path, &["from_iter"]) { - lint_from_iter(cx, expr, args); + from_iter_instead_of_collect::check(cx, expr, args); } } }, @@ -3440,65 +3441,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } } -fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let ty = cx.typeck_results().expr_ty(expr); - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - - if_chain! { - if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); - if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR); - - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); - then { - // `expr` implements `FromIterator` trait - let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); - let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); - span_lint_and_sugg( - cx, - FROM_ITER_INSTEAD_OF_COLLECT, - expr.span, - "usage of `FromIterator::from_iter`", - "use `.collect()` instead of `::from_iter()`", - sugg, - Applicability::MaybeIncorrect, - ); - } - } -} - -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { - if_chain! { - let call_site = expr.span.source_callsite(); - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); - let snippet_split = snippet.split("::").collect::>(); - if let Some((_, elements)) = snippet_split.split_last(); - - then { - // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) { - // remove the type specifier from the path elements - let without_ts = elements.iter().filter_map(|e| { - if e == type_specifier { None } else { Some((*e).to_string()) } - }).collect::>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{}", without_ts.join("::"), type_specifier) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) - } - } else { - ty.to_string() - } - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety -- cgit 1.4.1-3-g733a5 From 183daeb961c83d5be108cfa5ee463bba412dedc4 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 17:55:29 +0900 Subject: move filter_map to its own module --- clippy_lints/src/methods/filter_map.rs | 85 ++++++++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 81 ++------------------------------ 2 files changed, 89 insertions(+), 77 deletions(-) create mode 100644 clippy_lints/src/methods/filter_map.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs new file mode 100644 index 00000000000..f559160004c --- /dev/null +++ b/clippy_lints/src/methods/filter_map.rs @@ -0,0 +1,85 @@ +use crate::utils::{match_trait_method, path_to_local_id, paths, snippet, span_lint_and_sugg, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Expr, ExprKind, PatKind, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; +use rustc_span::symbol::sym; + +use super::MANUAL_FILTER_MAP; +use super::MANUAL_FIND_MAP; + +/// lint use of `filter().map()` or `find().map()` for `Iterators` +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { + if_chain! { + if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; + if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; + if match_trait_method(cx, map_recv, &paths::ITERATOR); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + if_chain! { + if path_to_local_id(a_path, filter_param_id); + if path_to_local_id(b, map_param_id); + if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); + then { + return true; + } + } + false + }; + if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + then { + let span = filter_span.to(map_span); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); + let to_opt = if is_result { ".ok()" } else { "" }; + let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, + snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9a0ef6c01c1..61d562cbc28 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod bytes_nth; mod clone_on_ref_ptr; mod expect_used; mod filetype_is_file; +mod filter_map; mod filter_map_identity; mod filter_next; mod from_iter_instead_of_collect; @@ -43,7 +44,7 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, PatKind, TraitItem, TraitItemKind, UnOp}; +use rustc_hir::{PatKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; @@ -1698,10 +1699,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "filter"] => filter_next::check(cx, expr, arg_lists[1]), ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), - ["map", "filter"] => lint_filter_map(cx, expr, false), + ["map", "filter"] => filter_map::check(cx, expr, false), ["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], self.msrv.as_ref()), - ["map", "find"] => lint_filter_map(cx, expr, true), + ["map", "find"] => filter_map::check(cx, expr, true), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), @@ -2750,80 +2751,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map ); } -/// lint use of `filter().map()` or `find().map()` for `Iterators` -fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { - if_chain! { - if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; - if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if match_trait_method(cx, map_recv, &paths::ITERATOR); - - // filter(|x| ...is_some())... - if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) - }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { - Some(true) - } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - if_chain! { - if path_to_local_id(a_path, filter_param_id); - if path_to_local_id(b, map_param_id); - if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); - then { - return true; - } - } - false - }; - if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); - then { - let span = filter_span.to(map_span); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); - let to_opt = if is_result { ".ok()" } else { "" }; - let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, - snippet(cx, map_arg.span, ".."), to_opt); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); - } - } -} - const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); /// lint use of `filter_map().next()` for `Iterators` -- cgit 1.4.1-3-g733a5 From 805dcd12d41e72c16bbca83496f4b89ba49eed64 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:05:15 +0900 Subject: move filter_map_map to its own module --- clippy_lints/src/methods/filter_map_map.rs | 20 ++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 18 ++---------------- 2 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 clippy_lints/src/methods/filter_map_map.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs new file mode 100644 index 00000000000..d015b4c7b38 --- /dev/null +++ b/clippy_lints/src/methods/filter_map_map.rs @@ -0,0 +1,20 @@ +use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::FILTER_MAP; + +/// lint use of `filter_map().map()` for `Iterators` +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter_map().map()` is an Iterator + if match_trait_method(cx, expr, &paths::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); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 61d562cbc28..2e5dc857b44 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5,6 +5,7 @@ mod expect_used; mod filetype_is_file; mod filter_map; mod filter_map_identity; +mod filter_map_map; mod filter_next; mod from_iter_instead_of_collect; mod get_unwrap; @@ -1700,7 +1701,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), ["map", "filter"] => filter_map::check(cx, expr, false), - ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), + ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => filter_map::check(cx, expr, true), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2785,21 +2786,6 @@ fn lint_filter_map_next<'tcx>( } } -/// lint use of `filter_map().map()` for `Iterators` -fn lint_filter_map_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter_map().map()` is an Iterator - if match_trait_method(cx, expr, &paths::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); - } -} - /// lint use of `filter().flat_map()` for `Iterators` fn lint_filter_flat_map<'tcx>( cx: &LateContext<'tcx>, -- cgit 1.4.1-3-g733a5 From 2baf043c374396dea3354eb8bc56af7539bf30b4 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:11:42 +0900 Subject: move filter_map_next to its own module --- clippy_lints/src/methods/filter_map_next.rs | 40 +++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 37 ++------------------------ 2 files changed, 42 insertions(+), 35 deletions(-) create mode 100644 clippy_lints/src/methods/filter_map_next.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs new file mode 100644 index 00000000000..a789df922ff --- /dev/null +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -0,0 +1,40 @@ +use crate::utils::{match_trait_method, meets_msrv, paths, snippet, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; + +use super::FILTER_MAP_NEXT; + +const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + return; + } + + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find_map(..)` instead"; + let filter_snippet = snippet(cx, filter_args[1].span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, filter_args[0].span, ".."); + span_lint_and_sugg( + cx, + FILTER_MAP_NEXT, + expr.span, + msg, + "try this", + format!("{}.find_map({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2e5dc857b44..fe1327564b2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -6,6 +6,7 @@ mod filetype_is_file; mod filter_map; mod filter_map_identity; mod filter_map_map; +mod filter_map_next; mod filter_next; mod from_iter_instead_of_collect; mod get_unwrap; @@ -1702,7 +1703,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), ["map", "filter"] => filter_map::check(cx, expr, false), ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]), - ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), + ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => filter_map::check(cx, expr, true), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2752,40 +2753,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map ); } -const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); - -/// lint use of `filter_map().next()` for `Iterators` -fn lint_filter_map_next<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - filter_args: &'tcx [hir::Expr<'_>], - msrv: Option<&RustcVersion>, -) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { - return; - } - - let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find_map(..)` instead"; - let filter_snippet = snippet(cx, filter_args[1].span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, filter_args[0].span, ".."); - span_lint_and_sugg( - cx, - FILTER_MAP_NEXT, - expr.span, - msg, - "try this", - format!("{}.find_map({})", iter_snippet, filter_snippet), - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); - } - } -} - /// lint use of `filter().flat_map()` for `Iterators` fn lint_filter_flat_map<'tcx>( cx: &LateContext<'tcx>, -- cgit 1.4.1-3-g733a5 From 6d941616f925298124eabe3ca10e48ec10bb33a9 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:19:18 +0900 Subject: move filter_flat_map to its own module --- clippy_lints/src/methods/filter_flat_map.rs | 21 +++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 19 ++----------------- 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 clippy_lints/src/methods/filter_flat_map.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs new file mode 100644 index 00000000000..8da867fce51 --- /dev/null +++ b/clippy_lints/src/methods/filter_flat_map.rs @@ -0,0 +1,21 @@ +use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::FILTER_MAP; + +/// lint use of `filter().flat_map()` for `Iterators` +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter().flat_map()` is an Iterator + if match_trait_method(cx, expr, &paths::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); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fe1327564b2..3251a49da09 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod bytes_nth; mod clone_on_ref_ptr; mod expect_used; mod filetype_is_file; +mod filter_flat_map; mod filter_map; mod filter_map_identity; mod filter_map_map; @@ -1705,7 +1706,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => filter_map::check(cx, expr, true), - ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), + ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]), @@ -2753,22 +2754,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map ); } -/// lint use of `filter().flat_map()` for `Iterators` -fn lint_filter_flat_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter().flat_map()` is an Iterator - if match_trait_method(cx, expr, &paths::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); - } -} - /// lint use of `filter_map().flat_map()` for `Iterators` fn lint_filter_map_flat_map<'tcx>( cx: &LateContext<'tcx>, -- cgit 1.4.1-3-g733a5 From f430384f04ee3ee62156a9e3060ba4d9054278b5 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:22:15 +0900 Subject: move filter_map_flat_map to its own module --- clippy_lints/src/methods/filter_map_flat_map.rs | 21 +++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 19 ++----------------- 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 clippy_lints/src/methods/filter_map_flat_map.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs new file mode 100644 index 00000000000..a6db138623a --- /dev/null +++ b/clippy_lints/src/methods/filter_map_flat_map.rs @@ -0,0 +1,21 @@ +use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::FILTER_MAP; + +/// lint use of `filter_map().flat_map()` for `Iterators` +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter_map().flat_map()` is an Iterator + if match_trait_method(cx, expr, &paths::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); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3251a49da09..34e1463f630 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5,6 +5,7 @@ mod expect_used; mod filetype_is_file; mod filter_flat_map; mod filter_map; +mod filter_map_flat_map; mod filter_map_identity; mod filter_map_map; mod filter_map_next; @@ -1707,7 +1708,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => filter_map::check(cx, expr, true), ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), - ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), + ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]), ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), @@ -2754,22 +2755,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map ); } -/// lint use of `filter_map().flat_map()` for `Iterators` -fn lint_filter_map_flat_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter_map().flat_map()` is an Iterator - if match_trait_method(cx, expr, &paths::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); - } -} - /// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient fn lint_flat_map_identity<'tcx>( cx: &LateContext<'tcx>, -- cgit 1.4.1-3-g733a5 From 37ba779a532cd229a18e8a99342f351ba035aa30 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:30:40 +0900 Subject: move flat_map_identity to its own module --- clippy_lints/src/methods/flat_map_identity.rs | 57 +++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 52 +----------------------- 2 files changed, 59 insertions(+), 50 deletions(-) create mode 100644 clippy_lints/src/methods/flat_map_identity.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs new file mode 100644 index 00000000000..ce3194f8a23 --- /dev/null +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -0,0 +1,57 @@ +use crate::utils::{match_qpath, match_trait_method, paths, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +use super::FLAT_MAP_IDENTITY; + +/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + flat_map_args: &'tcx [hir::Expr<'_>], + flat_map_span: Span, +) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + let arg_node = &flat_map_args[1].kind; + + let apply_lint = |message: &str| { + span_lint_and_sugg( + cx, + FLAT_MAP_IDENTITY, + flat_map_span.with_hi(expr.span.hi()), + message, + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); + }; + + if_chain! { + if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; + let body = cx.tcx.hir().body(*body_id); + + if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; + + if path.segments.len() == 1; + if path.segments[0].ident.name == binding_ident.name; + + then { + apply_lint("called `flat_map(|x| x)` on an `Iterator`"); + } + } + + if_chain! { + if let hir::ExprKind::Path(ref qpath) = arg_node; + + if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); + + then { + apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); + } + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 34e1463f630..6a425e6e3ed 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -10,6 +10,7 @@ mod filter_map_identity; mod filter_map_map; mod filter_map_next; mod filter_next; +mod flat_map_identity; mod from_iter_instead_of_collect; mod get_unwrap; mod implicit_clone; @@ -1709,7 +1710,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["map", "find"] => filter_map::check(cx, expr, true), ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), - ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), + ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]), ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), ["is_some", "position"] => { @@ -2755,55 +2756,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map ); } -/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient -fn lint_flat_map_identity<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - flat_map_args: &'tcx [hir::Expr<'_>], - flat_map_span: Span, -) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - let arg_node = &flat_map_args[1].kind; - - let apply_lint = |message: &str| { - span_lint_and_sugg( - cx, - FLAT_MAP_IDENTITY, - flat_map_span.with_hi(expr.span.hi()), - message, - "try", - "flatten()".to_string(), - Applicability::MachineApplicable, - ); - }; - - if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); - - if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; - - if path.segments.len() == 1; - if path.segments[0].ident.name == binding_ident.name; - - then { - apply_lint("called `flat_map(|x| x)` on an `Iterator`"); - } - } - - if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } - } - } -} - /// lint searching an Iterator followed by `is_some()` /// or calling `find()` on a string followed by `is_some()` fn lint_search_is_some<'tcx>( -- cgit 1.4.1-3-g733a5 From 24909fabd27091a7b363dcbc549636e98ba4a898 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:41:43 +0900 Subject: move map_flatten and search_is_some to their own modules --- clippy_lints/src/methods/map_flatten.rs | 61 ++++++++++++ clippy_lints/src/methods/mod.rs | 150 ++--------------------------- clippy_lints/src/methods/search_is_some.rs | 101 +++++++++++++++++++ 3 files changed, 168 insertions(+), 144 deletions(-) create mode 100644 clippy_lints/src/methods/map_flatten.rs create mode 100644 clippy_lints/src/methods/search_is_some.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs new file mode 100644 index 00000000000..14a14e4f9ec --- /dev/null +++ b/clippy_lints/src/methods/map_flatten.rs @@ -0,0 +1,61 @@ +use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::MAP_FLATTEN; + +/// lint use of `map().flatten()` for `Iterators` and 'Options' +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { + // 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 = 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 { + // `(...).map(...)` has type `impl Iterator> + "filter_map" + } else { + // `(...).map(...)` has type `impl Iterator> + "flat_map" + }; + let func_snippet = snippet(cx, map_args[1].span, ".."); + let hint = format!(".{0}({1})", method_to_use, func_snippet); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + 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, + ); + } + + // 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 func_snippet = snippet(cx, map_args[1].span, ".."); + let hint = format!(".and_then({})", func_snippet); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + 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/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6a425e6e3ed..2b8bb0b4216 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -25,9 +25,11 @@ mod iter_nth_zero; mod iterator_step_by_zero; mod manual_saturating_arithmetic; mod map_collect_result_unit; +mod map_flatten; mod ok_expect; mod option_as_ref_deref; mod option_map_unwrap_or; +mod search_is_some; mod single_char_insert_string; mod single_char_pattern; mod single_char_push_string; @@ -1711,13 +1713,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), - ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]), - ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), + ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]), + ["is_some", "find"] => search_is_some::check(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), ["is_some", "position"] => { - lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1]) + search_is_some::check(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1]) }, ["is_some", "rposition"] => { - lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) + search_is_some::check(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) }, ["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]), ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), @@ -2566,59 +2568,6 @@ fn derefs_to_slice<'tcx>( } } -/// lint use of `map().flatten()` for `Iterators` and 'Options' -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 map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); - 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 { - // `(...).map(...)` has type `impl Iterator> - "filter_map" - } else { - // `(...).map(...)` has type `impl Iterator> - "flat_map" - }; - let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!(".{0}({1})", method_to_use, func_snippet); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - 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, - ); - } - - // 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 func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!(".and_then({})", func_snippet); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - expr.span.with_lo(map_args[0].span.hi()), - "called `map(..).flatten()` on an `Option`", - "try using `and_then` instead", - hint, - Applicability::MachineApplicable, - ); - } -} - const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s @@ -2756,93 +2705,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map ); } -/// lint searching an Iterator followed by `is_some()` -/// or calling `find()` on a string followed by `is_some()` -fn lint_search_is_some<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - search_method: &str, - search_args: &'tcx [hir::Expr<'_>], - is_some_args: &'tcx [hir::Expr<'_>], - method_span: Span, -) { - // lint if caller of search is an Iterator - if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { - let msg = format!( - "called `is_some()` after searching an `Iterator` with `{}`", - search_method - ); - let hint = "this is more succinctly expressed by calling `any()`"; - let search_snippet = snippet(cx, search_args[1].span, ".."); - if search_snippet.lines().count() <= 1 { - // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` - // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` - let any_search_snippet = if_chain! { - if search_method == "find"; - if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind; - let closure_body = cx.tcx.hir().body(body_id); - if let Some(closure_arg) = closure_body.params.get(0); - then { - if let hir::PatKind::Ref(..) = closure_arg.pat.kind { - Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind { - let name = &*ident.name.as_str(); - Some(search_snippet.replace(&format!("*{}", name), name)) - } else { - None - } - } else { - None - } - }; - // add note if not multi-line - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `any()` instead", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - Applicability::MachineApplicable, - ); - } else { - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); - } - } - // lint if `find()` is called by `String` or `&str` - else if search_method == "find" { - let is_string_or_str_slice = |e| { - let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym::string_type) { - true - } else { - *self_ty.kind() == ty::Str - } - }; - if_chain! { - if is_string_or_str_slice(&search_args[0]); - if is_string_or_str_slice(&search_args[1]); - then { - let msg = "called `is_some()` after calling `find()` on a string"; - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - msg, - "use `contains()` instead", - format!("contains({})", find_arg), - applicability, - ); - } - } - } -} - /// Used for `lint_binary_expr_with_method_call`. #[derive(Copy, Clone)] struct BinaryExprInfo<'a> { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs new file mode 100644 index 00000000000..e9e65443220 --- /dev/null +++ b/clippy_lints/src/methods/search_is_some.rs @@ -0,0 +1,101 @@ +use crate::utils::{ + is_type_diagnostic_item, match_trait_method, paths, snippet, snippet_with_applicability, span_lint_and_help, + span_lint_and_sugg, strip_pat_refs, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::PatKind; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; + +use super::SEARCH_IS_SOME; + +/// lint searching an Iterator followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + search_method: &str, + search_args: &'tcx [hir::Expr<'_>], + is_some_args: &'tcx [hir::Expr<'_>], + method_span: Span, +) { + // lint if caller of search is an Iterator + if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { + let msg = format!( + "called `is_some()` after searching an `Iterator` with `{}`", + search_method + ); + let hint = "this is more succinctly expressed by calling `any()`"; + let search_snippet = snippet(cx, search_args[1].span, ".."); + if search_snippet.lines().count() <= 1 { + // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` + // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` + let any_search_snippet = if_chain! { + if search_method == "find"; + if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind; + let closure_body = cx.tcx.hir().body(body_id); + if let Some(closure_arg) = closure_body.params.get(0); + then { + if let hir::PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind { + let name = &*ident.name.as_str(); + Some(search_snippet.replace(&format!("*{}", name), name)) + } else { + None + } + } else { + None + } + }; + // add note if not multi-line + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `any()` instead", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); + } + } + // lint if `find()` is called by `String` or `&str` + else if search_method == "find" { + let is_string_or_str_slice = |e| { + let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); + if is_type_diagnostic_item(cx, self_ty, sym::string_type) { + true + } else { + *self_ty.kind() == ty::Str + } + }; + if_chain! { + if is_string_or_str_slice(&search_args[0]); + if is_string_or_str_slice(&search_args[1]); + then { + let msg = "called `is_some()` after calling `find()` on a string"; + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + msg, + "use `contains()` instead", + format!("contains({})", find_arg), + applicability, + ); + } + } + } +} -- cgit 1.4.1-3-g733a5 From 171c4c148540846c8aab4e1f0f491e8bfb892fcf Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:45:25 +0900 Subject: move iter_skip_next to its own module --- clippy_lints/src/methods/iter_skip_next.rs | 24 ++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 21 ++------------------- 2 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 clippy_lints/src/methods/iter_skip_next.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs new file mode 100644 index 00000000000..5f5969134e4 --- /dev/null +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -0,0 +1,24 @@ +use crate::utils::{match_trait_method, paths, snippet, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::ITER_SKIP_NEXT; + +pub(super) fn check(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) { + 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(..).next()` on an iterator", + "use `nth` instead", + hint, + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2b8bb0b4216..927375f9e0b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -22,6 +22,7 @@ mod iter_count; mod iter_next_slice; mod iter_nth; mod iter_nth_zero; +mod iter_skip_next; mod iterator_step_by_zero; mod manual_saturating_arithmetic; mod map_collect_result_unit; @@ -1730,7 +1731,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["nth", "bytes"] => bytes_nth::check(cx, expr, &arg_lists[1]), ["nth", ..] => iter_nth_zero::check(cx, expr, arg_lists[0]), ["step_by", ..] => iterator_step_by_zero::check(cx, expr, arg_lists[0]), - ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), + ["next", "skip"] => iter_skip_next::check(cx, expr, arg_lists[1]), ["collect", "cloned"] => iter_cloned_collect::check(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]), @@ -2510,24 +2511,6 @@ fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: } } -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) { - 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(..).next()` on an iterator", - "use `nth` instead", - hint, - Applicability::MachineApplicable, - ); - } - } -} - fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, -- cgit 1.4.1-3-g733a5 From caaba8270c7de1bf3446f3345e7e794295db59fd Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:53:31 +0900 Subject: move clone_on_copy to its own module --- clippy_lints/src/methods/clone_on_copy.rs | 109 ++++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 106 +---------------------------- 2 files changed, 112 insertions(+), 103 deletions(-) create mode 100644 clippy_lints/src/methods/clone_on_copy.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs new file mode 100644 index 00000000000..4a130ed47db --- /dev/null +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -0,0 +1,109 @@ +use crate::utils::{is_copy, span_lint_and_then, sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use std::iter; + +use super::CLONE_DOUBLE_REF; +use super::CLONE_ON_COPY; + +/// Checks for the `CLONE_ON_COPY` lint. +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { + let ty = cx.typeck_results().expr_ty(expr); + if let ty::Ref(_, inner, _) = arg_ty.kind() { + if let ty::Ref(_, innermost, _) = inner.kind() { + span_lint_and_then( + cx, + CLONE_DOUBLE_REF, + expr.span, + &format!( + "using `clone` on a double-reference; \ + this will copy the reference of type `{}` instead of cloning the inner type", + ty + ), + |diag| { + if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { + let mut ty = innermost; + let mut n = 0; + while let ty::Ref(_, inner, _) = ty.kind() { + ty = inner; + n += 1; + } + let refs: String = iter::repeat('&').take(n + 1).collect(); + let derefs: String = iter::repeat('*').take(n).collect(); + let explicit = format!("<{}{}>::clone({})", refs, ty, snip); + diag.span_suggestion( + expr.span, + "try dereferencing it", + format!("{}({}{}).clone()", refs, derefs, snip.deref()), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + expr.span, + "or try being explicit if you are sure, that you want to clone a reference", + explicit, + Applicability::MaybeIncorrect, + ); + } + }, + ); + return; // don't report clone_on_copy + } + } + + if is_copy(cx, ty) { + let snip; + if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { + let parent = cx.tcx.hir().get_parent_node(expr.hir_id); + match &cx.tcx.hir().get(parent) { + hir::Node::Expr(parent) => match parent.kind { + // &*x is a nop, &x.clone() is not + hir::ExprKind::AddrOf(..) => return, + // (*x).func() is useless, x.clone().func() can work in case func borrows mutably + hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { + return; + }, + + _ => {}, + }, + hir::Node::Stmt(stmt) => { + if let hir::StmtKind::Local(ref loc) = stmt.kind { + if let hir::PatKind::Ref(..) = loc.pat.kind { + // let ref y = *x borrows x, let ref y = x.clone() does not + return; + } + } + }, + _ => {}, + } + + // x.clone() might have dereferenced x, possibly through Deref impls + if cx.typeck_results().expr_ty(arg) == ty { + snip = Some(("try removing the `clone` call", format!("{}", snippet))); + } else { + let deref_count = cx + .typeck_results() + .expr_adjustments(arg) + .iter() + .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))); + } + } else { + snip = None; + } + span_lint_and_then( + cx, + CLONE_ON_COPY, + expr.span, + &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + |diag| { + if let Some((text, snip)) = snip { + diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); + } + }, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 927375f9e0b..f248a09e18e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,5 +1,6 @@ mod bind_instead_of_map; mod bytes_nth; +mod clone_on_copy; mod clone_on_ref_ptr; mod expect_used; mod filetype_is_file; @@ -45,7 +46,6 @@ mod wrong_self_convention; mod zst_offset; use std::borrow::Cow; -use std::iter; use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; @@ -69,7 +69,7 @@ use crate::utils::{ is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth, SpanlessEq, + span_lint_and_help, span_lint_and_sugg, strip_pat_refs, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1781,7 +1781,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); if args.len() == 1 && method_call.ident.name == sym::clone { - lint_clone_on_copy(cx, expr, &args[0], self_ty); + clone_on_copy::check(cx, expr, &args[0], self_ty); clone_on_ref_ptr::check(cx, expr, &args[0]); } if args.len() == 1 && method_call.ident.name == sym!(to_string) { @@ -2323,106 +2323,6 @@ fn lint_expect_fun_call( ); } -/// Checks for the `CLONE_ON_COPY` lint. -fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind() { - if let ty::Ref(_, innermost, _) = inner.kind() { - span_lint_and_then( - cx, - CLONE_DOUBLE_REF, - expr.span, - &format!( - "using `clone` on a double-reference; \ - this will copy the reference of type `{}` instead of cloning the inner type", - ty - ), - |diag| { - if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { - let mut ty = innermost; - let mut n = 0; - while let ty::Ref(_, inner, _) = ty.kind() { - ty = inner; - n += 1; - } - let refs: String = iter::repeat('&').take(n + 1).collect(); - let derefs: String = iter::repeat('*').take(n).collect(); - let explicit = format!("<{}{}>::clone({})", refs, ty, snip); - diag.span_suggestion( - expr.span, - "try dereferencing it", - format!("{}({}{}).clone()", refs, derefs, snip.deref()), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - expr.span, - "or try being explicit if you are sure, that you want to clone a reference", - explicit, - Applicability::MaybeIncorrect, - ); - } - }, - ); - return; // don't report clone_on_copy - } - } - - if is_copy(cx, ty) { - let snip; - if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { - let parent = cx.tcx.hir().get_parent_node(expr.hir_id); - match &cx.tcx.hir().get(parent) { - hir::Node::Expr(parent) => match parent.kind { - // &*x is a nop, &x.clone() is not - hir::ExprKind::AddrOf(..) => return, - // (*x).func() is useless, x.clone().func() can work in case func borrows mutably - hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { - return; - }, - - _ => {}, - }, - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Local(ref loc) = stmt.kind { - if let hir::PatKind::Ref(..) = loc.pat.kind { - // let ref y = *x borrows x, let ref y = x.clone() does not - return; - } - } - }, - _ => {}, - } - - // x.clone() might have dereferenced x, possibly through Deref impls - if cx.typeck_results().expr_ty(arg) == ty { - snip = Some(("try removing the `clone` call", format!("{}", snippet))); - } else { - let deref_count = cx - .typeck_results() - .expr_adjustments(arg) - .iter() - .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))); - } - } else { - snip = None; - } - span_lint_and_then( - cx, - CLONE_ON_COPY, - expr.span, - &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), - |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }, - ); - } -} - fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { fn check_fold_with_op( cx: &LateContext<'_>, -- cgit 1.4.1-3-g733a5 From 78e572c62782b4bd027c23b9d56a14b0b42ba93a Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 18:59:35 +0900 Subject: move useless_asref to its own module --- clippy_lints/src/methods/mod.rs | 45 ++++--------------------------- clippy_lints/src/methods/useless_asref.rs | 45 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 40 deletions(-) create mode 100644 clippy_lints/src/methods/useless_asref.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f248a09e18e..eaf247536b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -42,6 +42,7 @@ mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_lazy_eval; mod unwrap_used; +mod useless_asref; mod wrong_self_convention; mod zst_offset; @@ -65,11 +66,11 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_return, contains_ty, get_parent_expr, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, + contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, strip_pat_refs, walk_ptrs_ty_depth, SpanlessEq, + span_lint_and_help, span_lint_and_sugg, strip_pat_refs, SpanlessEq, }; declare_clippy_lint! { @@ -1733,8 +1734,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["step_by", ..] => iterator_step_by_zero::check(cx, expr, arg_lists[0]), ["next", "skip"] => iter_skip_next::check(cx, expr, arg_lists[1]), ["collect", "cloned"] => iter_cloned_collect::check(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]), + ["as_ref"] => useless_asref::check(cx, expr, "as_ref", arg_lists[0]), + ["as_mut"] => useless_asref::check(cx, expr, "as_mut", arg_lists[0]), ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]), ["filter_map", ..] => { unnecessary_filter_map::check(cx, expr, arg_lists[0]); @@ -2751,42 +2752,6 @@ fn get_hint_if_single_char_arg( } } -/// 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" - // check if the call is to the actual `AsRef` or `AsMut` trait - if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { - // check if the type after `as_ref` or `as_mut` is the same as before - let recvr = &as_ref_args[0]; - let rcv_ty = cx.typeck_results().expr_ty(recvr); - let res_ty = cx.typeck_results().expr_ty(expr); - let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); - let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); - if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { - // allow the `as_ref` or `as_mut` if it is followed by another method call - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::MethodCall(_, ref span, _, _) = parent.kind; - if span != &expr.span; - then { - return; - } - } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - USELESS_ASREF, - expr.span, - &format!("this call to `{}` does nothing", call_name), - "try this", - snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), - applicability, - ); - } - } -} - const FN_HEADER: hir::FnHeader = hir::FnHeader { unsafety: hir::Unsafety::Normal, constness: hir::Constness::NotConst, diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs new file mode 100644 index 00000000000..e4554f8d489 --- /dev/null +++ b/clippy_lints/src/methods/useless_asref.rs @@ -0,0 +1,45 @@ +use crate::utils::{ + get_parent_expr, match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty_depth, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::USELESS_ASREF; + +/// Checks for the `USELESS_ASREF` lint. +pub(super) fn check(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" + // check if the call is to the actual `AsRef` or `AsMut` trait + if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { + // check if the type after `as_ref` or `as_mut` is the same as before + let recvr = &as_ref_args[0]; + let rcv_ty = cx.typeck_results().expr_ty(recvr); + let res_ty = cx.typeck_results().expr_ty(expr); + let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); + let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { + // allow the `as_ref` or `as_mut` if it is followed by another method call + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::MethodCall(_, ref span, _, _) = parent.kind; + if span != &expr.span; + then { + return; + } + } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + USELESS_ASREF, + expr.span, + &format!("this call to `{}` does nothing", call_name), + "try this", + snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), + applicability, + ); + } + } +} -- cgit 1.4.1-3-g733a5 From bbed852f6fb82aa85f72fc8b2aeffdb90f409495 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 19:05:51 +0900 Subject: unnecessary_fold to its own module --- clippy_lints/src/methods/mod.rs | 100 ++------------------------ clippy_lints/src/methods/unnecessary_fold.rs | 101 +++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 94 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_fold.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index eaf247536b6..702c8e76f49 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -40,6 +40,7 @@ mod string_extend_chars; mod suspicious_map; mod uninit_assumed_init; mod unnecessary_filter_map; +mod unnecessary_fold; mod unnecessary_lazy_eval; mod unwrap_used; mod useless_asref; @@ -53,7 +54,7 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{PatKind, TraitItem, TraitItemKind}; +use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; @@ -67,10 +68,9 @@ use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, - is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, - match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty, - single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, strip_pat_refs, SpanlessEq, + is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_type, meets_msrv, + method_calls, method_chain_args, paths, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; declare_clippy_lint! { @@ -1736,7 +1736,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["collect", "cloned"] => iter_cloned_collect::check(cx, expr, arg_lists[1]), ["as_ref"] => useless_asref::check(cx, expr, "as_ref", arg_lists[0]), ["as_mut"] => useless_asref::check(cx, expr, "as_mut", arg_lists[0]), - ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]), + ["fold", ..] => unnecessary_fold::check(cx, expr, arg_lists[0], method_spans[0]), ["filter_map", ..] => { unnecessary_filter_map::check(cx, expr, arg_lists[0]); filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); @@ -2324,94 +2324,6 @@ fn lint_expect_fun_call( ); } -fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { - fn check_fold_with_op( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - fold_args: &[hir::Expr<'_>], - fold_span: Span, - op: hir::BinOpKind, - replacement_method_name: &str, - replacement_has_args: bool, - ) { - if_chain! { - // Extract the body of the closure passed to fold - if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind; - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); - - // Check if the closure body is of the form `acc some_expr(x)` - if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind; - if bin_op.node == op; - - // Extract the names of the two arguments to the closure - if let [param_a, param_b] = closure_body.params; - if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind; - if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind; - - if path_to_local_id(left_expr, first_arg_id); - if replacement_has_args || path_to_local_id(right_expr, second_arg_id); - - then { - let mut applicability = Applicability::MachineApplicable; - let sugg = if replacement_has_args { - format!( - "{replacement}(|{s}| {r})", - replacement = replacement_method_name, - s = second_arg_ident, - r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), - ) - } else { - format!( - "{replacement}()", - replacement = replacement_method_name, - ) - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_FOLD, - fold_span.with_hi(expr.span.hi()), - // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) - "this `.fold` can be written more succinctly using another method", - "try", - sugg, - applicability, - ); - } - } - } - - // Check that this is a call to Iterator::fold rather than just some function called fold - if !match_trait_method(cx, expr, &paths::ITERATOR) { - return; - } - - assert!( - fold_args.len() == 3, - "Expected fold_args to have three entries - the receiver, the initial value and the closure" - ); - - // Check if the first argument to .fold is a suitable literal - if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind { - match lit.node { - ast::LitKind::Bool(false) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true) - }, - ast::LitKind::Bool(true) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true) - }, - ast::LitKind::Int(0, _) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false) - }, - ast::LitKind::Int(1, _) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false) - }, - _ => (), - } - } -} - fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs new file mode 100644 index 00000000000..a26443f4ee9 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -0,0 +1,101 @@ +use crate::utils::{ + match_trait_method, path_to_local_id, paths, remove_blocks, snippet_with_applicability, span_lint_and_sugg, + strip_pat_refs, +}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::PatKind; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +use super::UNNECESSARY_FOLD; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { + fn check_fold_with_op( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + fold_args: &[hir::Expr<'_>], + fold_span: Span, + op: hir::BinOpKind, + replacement_method_name: &str, + replacement_has_args: bool, + ) { + if_chain! { + // Extract the body of the closure passed to fold + if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind; + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + // Check if the closure body is of the form `acc some_expr(x)` + if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind; + if bin_op.node == op; + + // Extract the names of the two arguments to the closure + if let [param_a, param_b] = closure_body.params; + if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind; + if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind; + + if path_to_local_id(left_expr, first_arg_id); + if replacement_has_args || path_to_local_id(right_expr, second_arg_id); + + then { + let mut applicability = Applicability::MachineApplicable; + let sugg = if replacement_has_args { + format!( + "{replacement}(|{s}| {r})", + replacement = replacement_method_name, + s = second_arg_ident, + r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), + ) + } else { + format!( + "{replacement}()", + replacement = replacement_method_name, + ) + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_FOLD, + fold_span.with_hi(expr.span.hi()), + // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) + "this `.fold` can be written more succinctly using another method", + "try", + sugg, + applicability, + ); + } + } + } + + // Check that this is a call to Iterator::fold rather than just some function called fold + if !match_trait_method(cx, expr, &paths::ITERATOR) { + return; + } + + assert!( + fold_args.len() == 3, + "Expected fold_args to have three entries - the receiver, the initial value and the closure" + ); + + // Check if the first argument to .fold is a suitable literal + if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind { + match lit.node { + ast::LitKind::Bool(false) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true) + }, + ast::LitKind::Bool(true) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true) + }, + ast::LitKind::Int(0, _) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false) + }, + ast::LitKind::Int(1, _) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false) + }, + _ => (), + } + } +} -- cgit 1.4.1-3-g733a5 From 5557596926b493f1fdb85aef6bd78941f231f962 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 19:11:52 +0900 Subject: move option_map_or_none to its own module --- clippy_lints/src/methods/mod.rs | 73 +----------------------- clippy_lints/src/methods/option_map_or_none.rs | 78 ++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 clippy_lints/src/methods/option_map_or_none.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 702c8e76f49..0c67c3b892b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -30,6 +30,7 @@ mod map_collect_result_unit; mod map_flatten; mod ok_expect; mod option_as_ref_deref; +mod option_map_or_none; mod option_map_unwrap_or; mod search_is_some; mod single_char_insert_string; @@ -1692,7 +1693,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"); } }, - ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), + ["map_or", ..] => option_map_or_none::check(cx, expr, arg_lists[0]), ["and_then", ..] => { let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, arg_lists[0]); let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, arg_lists[0]); @@ -2431,76 +2432,6 @@ fn lint_map_unwrap_or_else<'tcx>( false } -/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s -fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type); - - // There are two variants of this `map_or` lint: - // (1) using `map_or` as an adapter from `Result` to `Option` - // (2) using `map_or` as a combinator instead of `and_then` - // - // (For this lint) we don't care if any other type calls `map_or` - if !is_option && !is_result { - return; - } - - let (lint_name, msg, instead, hint) = { - let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind { - match_qpath(qpath, &paths::OPTION_NONE) - } else { - return; - }; - - if !default_arg_is_none { - // nothing to lint! - return; - } - - let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind { - match_qpath(qpath, &paths::OPTION_SOME) - } else { - false - }; - - 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, ..)` on an `Option` value. This can be done more directly by calling \ - `and_then(..)` instead"; - ( - OPTION_MAP_OR_NONE, - msg, - "try using `and_then` instead", - format!("{0}.and_then({1})", self_snippet, func_snippet), - ) - } else if f_arg_is_some { - let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ - `ok()` instead"; - let self_snippet = snippet(cx, map_or_args[0].span, ".."); - ( - RESULT_MAP_OR_INTO_OPTION, - msg, - "try using `ok` instead", - format!("{0}.ok()", self_snippet), - ) - } else { - // nothing to lint! - return; - } - }; - - span_lint_and_sugg( - cx, - lint_name, - expr.span, - msg, - instead, - hint, - Applicability::MachineApplicable, - ); -} - /// Used for `lint_binary_expr_with_method_call`. #[derive(Copy, Clone)] struct BinaryExprInfo<'a> { diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs new file mode 100644 index 00000000000..64f6ebc5062 --- /dev/null +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -0,0 +1,78 @@ +use crate::utils::{is_type_diagnostic_item, match_qpath, paths, snippet, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::OPTION_MAP_OR_NONE; +use super::RESULT_MAP_OR_INTO_OPTION; + +/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type); + + // There are two variants of this `map_or` lint: + // (1) using `map_or` as an adapter from `Result` to `Option` + // (2) using `map_or` as a combinator instead of `and_then` + // + // (For this lint) we don't care if any other type calls `map_or` + if !is_option && !is_result { + return; + } + + let (lint_name, msg, instead, hint) = { + let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind { + match_qpath(qpath, &paths::OPTION_NONE) + } else { + return; + }; + + if !default_arg_is_none { + // nothing to lint! + return; + } + + let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind { + match_qpath(qpath, &paths::OPTION_SOME) + } else { + false + }; + + 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, ..)` on an `Option` value. This can be done more directly by calling \ + `and_then(..)` instead"; + ( + OPTION_MAP_OR_NONE, + msg, + "try using `and_then` instead", + format!("{0}.and_then({1})", self_snippet, func_snippet), + ) + } else if f_arg_is_some { + let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ + `ok()` instead"; + let self_snippet = snippet(cx, map_or_args[0].span, ".."); + ( + RESULT_MAP_OR_INTO_OPTION, + msg, + "try using `ok` instead", + format!("{0}.ok()", self_snippet), + ) + } else { + // nothing to lint! + return; + } + }; + + span_lint_and_sugg( + cx, + lint_name, + expr.span, + msg, + instead, + hint, + Applicability::MachineApplicable, + ); +} -- cgit 1.4.1-3-g733a5 From b0824bf75fba159f40e498dd88386174feea5cf5 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 19:31:46 +0900 Subject: move map_unwrap_or to its own module --- clippy_lints/src/methods/map_unwrap_or.rs | 76 +++++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 75 ++---------------------------- 2 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 clippy_lints/src/methods/map_unwrap_or.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs new file mode 100644 index 00000000000..63b2cf87f32 --- /dev/null +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -0,0 +1,76 @@ +use crate::utils::usage::mutated_variables; +use crate::utils::{is_type_diagnostic_item, meets_msrv, snippet, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; +use rustc_span::symbol::sym; + +use super::MAP_UNWRAP_OR; + +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + +/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + map_args: &'tcx [hir::Expr<'_>], + unwrap_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } + // lint if the caller of `map()` is an `Option` + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); + + if is_option || is_result { + // Don't make a suggestion that may fail to compile due to mutably borrowing + // the same variable twice. + let map_mutated_vars = mutated_variables(&map_args[0], cx); + 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 false; + } + } else { + return false; + } + + // lint message + let msg = if is_option { + "called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling \ + `map_or_else(, )` instead" + } else { + "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, ".."); + let unwrap_snippet = snippet(cx, unwrap_args[1].span, ".."); + // lint, with note if neither arg is > 1 line and both map() and + // unwrap_or_else() have the same span + 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 { + let var_snippet = snippet(cx, map_args[0].span, ".."); + span_lint_and_sugg( + cx, + MAP_UNWRAP_OR, + expr.span, + msg, + "try this", + format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + Applicability::MachineApplicable, + ); + return true; + } else if same_span && multiline { + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); + return true; + } + } + + false +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0c67c3b892b..5f62942530c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -28,6 +28,7 @@ mod iterator_step_by_zero; mod manual_saturating_arithmetic; mod map_collect_result_unit; mod map_flatten; +mod map_unwrap_or; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -66,11 +67,10 @@ use rustc_span::symbol::{sym, Symbol, SymbolStr}; use rustc_typeck::hir_ty_to_ty; use crate::utils::eager_or_lazy::is_lazyness_candidate; -use crate::utils::usage::mutated_variables; use crate::utils::{ contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, - is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_type, meets_msrv, - method_calls, method_chain_args, paths, return_ty, single_segment_path, snippet, snippet_with_applicability, + is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_type, method_calls, + method_chain_args, paths, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; @@ -1689,7 +1689,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => expect_used::check(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::check(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], self.msrv.as_ref()) { + if !map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"); } }, @@ -2365,73 +2365,6 @@ fn derefs_to_slice<'tcx>( } } -const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); - -/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s -/// Return true if lint triggered -fn lint_map_unwrap_or_else<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - map_args: &'tcx [hir::Expr<'_>], - unwrap_args: &'tcx [hir::Expr<'_>], - msrv: Option<&RustcVersion>, -) -> bool { - if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { - return false; - } - // lint if the caller of `map()` is an `Option` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); - - if is_option || is_result { - // Don't make a suggestion that may fail to compile due to mutably borrowing - // the same variable twice. - let map_mutated_vars = mutated_variables(&map_args[0], cx); - 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 false; - } - } else { - return false; - } - - // lint message - let msg = if is_option { - "called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling \ - `map_or_else(, )` instead" - } else { - "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, ".."); - let unwrap_snippet = snippet(cx, unwrap_args[1].span, ".."); - // lint, with note if neither arg is > 1 line and both map() and - // unwrap_or_else() have the same span - 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 { - let var_snippet = snippet(cx, map_args[0].span, ".."); - span_lint_and_sugg( - cx, - MAP_UNWRAP_OR, - expr.span, - msg, - "try this", - format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), - Applicability::MachineApplicable, - ); - return true; - } else if same_span && multiline { - span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - return true; - } - } - - false -} - /// Used for `lint_binary_expr_with_method_call`. #[derive(Copy, Clone)] struct BinaryExprInfo<'a> { -- cgit 1.4.1-3-g733a5 From f49349bf333001166e3a215b7b2eb5b5cb1c9989 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 19:40:34 +0900 Subject: move or_fun_call to its own module --- clippy_lints/src/methods/mod.rs | 168 +------------------------------ clippy_lints/src/methods/or_fun_call.rs | 173 ++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 163 deletions(-) create mode 100644 clippy_lints/src/methods/or_fun_call.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5f62942530c..857c1bf0ec0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -33,6 +33,7 @@ mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; mod option_map_unwrap_or; +mod or_fun_call; mod search_is_some; mod single_char_insert_string; mod single_char_pattern; @@ -66,12 +67,11 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol, SymbolStr}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::{ contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, - is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_type, method_calls, - method_chain_args, paths, return_ty, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, + is_type_diagnostic_item, iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, + return_ty, single_segment_path, snippet, snippet_with_applicability, span_lint, span_lint_and_help, + span_lint_and_sugg, SpanlessEq, }; declare_clippy_lint! { @@ -1778,7 +1778,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } }, hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { - lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); + or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); @@ -1973,164 +1973,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -/// Checks for the `OR_FUN_CALL` lint. -#[allow(clippy::too_many_lines)] -fn lint_or_fun_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - method_span: Span, - name: &str, - args: &'tcx [hir::Expr<'_>], -) { - /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. - fn check_unwrap_or_default( - cx: &LateContext<'_>, - name: &str, - fun: &hir::Expr<'_>, - self_expr: &hir::Expr<'_>, - arg: &hir::Expr<'_>, - or_has_args: bool, - span: Span, - ) -> bool { - if_chain! { - if !or_has_args; - if name == "unwrap_or"; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - let path = &*last_path_segment(qpath).ident.as_str(); - if ["default", "new"].contains(&path); - let arg_ty = cx.typeck_results().expr_ty(arg); - if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); - if implements_trait(cx, arg_ty, default_trait_id, &[]); - - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span, - &format!("use of `{}` followed by a call to `{}`", name, path), - "try this", - format!( - "{}.unwrap_or_default()", - snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) - ), - applicability, - ); - - true - } else { - false - } - } - } - - /// Checks for `*or(foo())`. - #[allow(clippy::too_many_arguments)] - fn check_general_case<'tcx>( - cx: &LateContext<'tcx>, - name: &str, - method_span: Span, - self_expr: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - span: Span, - // None if lambda is required - fun_span: Option, - ) { - // (path, fn_has_argument, methods, suffix) - static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), - ]; - - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { - if path.ident.as_str() == "len" { - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - - match ty.kind() { - ty::Slice(_) | ty::Array(_, _) => return, - _ => (), - } - - if is_type_diagnostic_item(cx, ty, sym::vec_type) { - return; - } - } - } - - if_chain! { - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); - - if is_lazyness_candidate(cx, arg); - if !contains_return(&arg); - - let self_ty = cx.typeck_results().expr_ty(self_expr); - - if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); - - if poss.contains(&name); - - then { - let macro_expanded_snipped; - let sugg: Cow<'_, str> = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - let snippet = { - let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); - if not_macro_argument_snippet == "vec![]" { - macro_expanded_snipped = snippet(cx, snippet_span, ".."); - match macro_expanded_snipped.strip_prefix("$crate::vec::") { - Some(stripped) => Cow::from(stripped), - None => macro_expanded_snipped - } - } - else { - not_macro_argument_snippet - } - }; - - if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{}| {}", l_arg, snippet).into() - } else { - snippet - } - }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - &format!("use of `{}` followed by a function call", name), - "try this", - format!("{}_{}({})", name, suffix, sugg), - Applicability::HasPlaceholders, - ); - } - } - } - - if args.len() == 2 { - match args[1].kind { - hir::ExprKind::Call(ref fun, ref or_args) => { - let or_has_args = !or_args.is_empty(); - if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { - let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); - } - }, - hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); - }, - _ => {}, - } - } -} - /// Checks for the `EXPECT_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_expect_fun_call( diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs new file mode 100644 index 00000000000..5f7fc431d22 --- /dev/null +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -0,0 +1,173 @@ +use crate::utils::eager_or_lazy::is_lazyness_candidate; +use crate::utils::{ + contains_return, get_trait_def_id, implements_trait, is_type_diagnostic_item, last_path_segment, match_type, paths, + snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint_and_sugg, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; +use std::borrow::Cow; + +use super::OR_FUN_CALL; + +/// Checks for the `OR_FUN_CALL` lint. +#[allow(clippy::too_many_lines)] +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'_>], +) { + /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. + fn check_unwrap_or_default( + cx: &LateContext<'_>, + name: &str, + fun: &hir::Expr<'_>, + self_expr: &hir::Expr<'_>, + arg: &hir::Expr<'_>, + or_has_args: bool, + span: Span, + ) -> bool { + if_chain! { + if !or_has_args; + if name == "unwrap_or"; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + let path = &*last_path_segment(qpath).ident.as_str(); + if ["default", "new"].contains(&path); + let arg_ty = cx.typeck_results().expr_ty(arg); + if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); + if implements_trait(cx, arg_ty, default_trait_id, &[]); + + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span, + &format!("use of `{}` followed by a call to `{}`", name, path), + "try this", + format!( + "{}.unwrap_or_default()", + snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) + ), + applicability, + ); + + true + } else { + false + } + } + } + + /// Checks for `*or(foo())`. + #[allow(clippy::too_many_arguments)] + fn check_general_case<'tcx>( + cx: &LateContext<'tcx>, + name: &str, + method_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + span: Span, + // None if lambda is required + fun_span: Option, + ) { + // (path, fn_has_argument, methods, suffix) + static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ + (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + ]; + + if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { + if path.ident.as_str() == "len" { + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + + match ty.kind() { + ty::Slice(_) | ty::Array(_, _) => return, + _ => (), + } + + if is_type_diagnostic_item(cx, ty, sym::vec_type) { + return; + } + } + } + + if_chain! { + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); + + if is_lazyness_candidate(cx, arg); + if !contains_return(&arg); + + let self_ty = cx.typeck_results().expr_ty(self_expr); + + if let Some(&(_, fn_has_arguments, poss, suffix)) = + KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); + + if poss.contains(&name); + + then { + let macro_expanded_snipped; + let sugg: Cow<'_, str> = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + let snippet = { + let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + if not_macro_argument_snippet == "vec![]" { + macro_expanded_snipped = snippet(cx, snippet_span, ".."); + match macro_expanded_snipped.strip_prefix("$crate::vec::") { + Some(stripped) => Cow::from(stripped), + None => macro_expanded_snipped + } + } + else { + not_macro_argument_snippet + } + }; + + if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{}| {}", l_arg, snippet).into() + } else { + snippet + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("{}_{}({})", name, suffix, sugg), + Applicability::HasPlaceholders, + ); + } + } + } + + if args.len() == 2 { + match args[1].kind { + hir::ExprKind::Call(ref fun, ref or_args) => { + let or_has_args = !or_args.is_empty(); + if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { + let fun_span = if or_has_args { None } else { Some(fun.span) }; + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); + } + }, + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + }, + _ => {}, + } + } +} -- cgit 1.4.1-3-g733a5 From c711de28ee903c78ad465f6af99afdb977ab8dfe Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 6 Mar 2021 22:26:29 +0900 Subject: move expect_fun_call to its own module --- clippy_lints/src/methods/expect_fun_call.rs | 199 +++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 206 +--------------------------- 2 files changed, 204 insertions(+), 201 deletions(-) create mode 100644 clippy_lints/src/methods/expect_fun_call.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs new file mode 100644 index 00000000000..6866e9c652a --- /dev/null +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -0,0 +1,199 @@ +use crate::utils::{is_expn_of, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; +use std::borrow::Cow; + +use super::EXPECT_FUN_CALL; + +/// Checks for the `EXPECT_FUN_CALL` lint. +#[allow(clippy::too_many_lines)] +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) { + // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or + // `&str` + fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + let mut arg_root = arg; + loop { + arg_root = match &arg_root.kind { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, + hir::ExprKind::MethodCall(method_name, _, call_args, _) => { + if call_args.len() == 1 + && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) + && { + let arg_type = cx.typeck_results().expr_ty(&call_args[0]); + let base_type = arg_type.peel_refs(); + *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type) + } + { + &call_args[0] + } else { + break; + } + }, + _ => break, + }; + } + arg_root + } + + // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be + // converted to string. + fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { + let arg_ty = cx.typeck_results().expr_ty(arg); + if is_type_diagnostic_item(cx, arg_ty, sym::string_type) { + return false; + } + if let ty::Ref(_, ty, ..) = arg_ty.kind() { + if *ty.kind() == ty::Str && can_be_static_str(cx, arg) { + return false; + } + }; + true + } + + // Check if an expression could have type `&'static str`, knowing that it + // has type `&str` for some lifetime. + fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { + match arg.kind { + hir::ExprKind::Lit(_) => true, + hir::ExprKind::Call(fun, _) => { + if let hir::ExprKind::Path(ref p) = fun.kind { + match cx.qpath_res(p, fun.hir_id) { + hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( + cx.tcx.fn_sig(def_id).output().skip_binder().kind(), + ty::Ref(ty::ReStatic, ..) + ), + _ => false, + } + } else { + false + } + }, + hir::ExprKind::MethodCall(..) => { + cx.typeck_results() + .type_dependent_def_id(arg.hir_id) + .map_or(false, |method_id| { + matches!( + cx.tcx.fn_sig(method_id).output().skip_binder().kind(), + ty::Ref(ty::ReStatic, ..) + ) + }) + }, + 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, + } + } + + fn generate_format_arg_snippet( + cx: &LateContext<'_>, + a: &hir::Expr<'_>, + applicability: &mut Applicability, + ) -> Vec { + if_chain! { + if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind; + if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind; + if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind; + + then { + format_arg_expr_tup + .iter() + .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned()) + .collect() + } else { + unreachable!() + } + } + } + + fn is_call(node: &hir::ExprKind<'_>) -> bool { + match node { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => { + is_call(&expr.kind) + }, + hir::ExprKind::Call(..) + | hir::ExprKind::MethodCall(..) + // These variants are debatable or require further examination + | hir::ExprKind::If(..) + | hir::ExprKind::Match(..) + | hir::ExprKind::Block{ .. } => true, + _ => false, + } + } + + if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) { + return; + } + + let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]); + let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) { + "||" + } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) { + "|_|" + } else { + return; + }; + + let arg_root = get_arg_root(cx, &args[1]); + + let span_replace_word = method_span.with_hi(expr.span.hi()); + + let mut applicability = Applicability::MachineApplicable; + + //Special handling for `format!` as arg_root + if_chain! { + if let hir::ExprKind::Block(block, None) = &arg_root.kind; + if block.stmts.len() == 1; + if let hir::StmtKind::Local(local) = &block.stmts[0].kind; + if let Some(arg_root) = &local.init; + if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind; + if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; + if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; + then { + let fmt_spec = &format_args[0]; + let fmt_args = &format_args[1]; + + let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()]; + + args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability)); + + let sugg = args.join(", "); + + span_lint_and_sugg( + cx, + EXPECT_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("unwrap_or_else({} panic!({}))", closure_args, sugg), + applicability, + ); + + return; + } + } + + let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability); + if requires_to_string(cx, arg_root) { + arg_root_snippet.to_mut().push_str(".to_string()"); + } + + span_lint_and_sugg( + cx, + EXPECT_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!( + "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", + closure_args, arg_root_snippet + ), + applicability, + ); +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 857c1bf0ec0..180620e860a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2,6 +2,7 @@ mod bind_instead_of_map; mod bytes_nth; mod clone_on_copy; mod clone_on_ref_ptr; +mod expect_fun_call; mod expect_used; mod filetype_is_file; mod filter_flat_map; @@ -50,8 +51,6 @@ mod useless_asref; mod wrong_self_convention; mod zst_offset; -use std::borrow::Cow; - use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; @@ -68,10 +67,9 @@ use rustc_span::symbol::{sym, Symbol, SymbolStr}; use rustc_typeck::hir_ty_to_ty; use crate::utils::{ - contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, - is_type_diagnostic_item, iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, - return_ty, single_segment_path, snippet, snippet_with_applicability, span_lint, span_lint_and_help, - span_lint_and_sugg, SpanlessEq, + contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_type_diagnostic_item, + iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, return_ty, + single_segment_path, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; declare_clippy_lint! { @@ -1779,7 +1777,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); - lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); + expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); if args.len() == 1 && method_call.ident.name == sym::clone { @@ -1973,200 +1971,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -/// Checks for the `EXPECT_FUN_CALL` lint. -#[allow(clippy::too_many_lines)] -fn lint_expect_fun_call( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_span: Span, - name: &str, - args: &[hir::Expr<'_>], -) { - // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or - // `&str` - fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - let mut arg_root = arg; - loop { - arg_root = match &arg_root.kind { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, - hir::ExprKind::MethodCall(method_name, _, call_args, _) => { - if call_args.len() == 1 - && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) - && { - let arg_type = cx.typeck_results().expr_ty(&call_args[0]); - let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type) - } - { - &call_args[0] - } else { - break; - } - }, - _ => break, - }; - } - arg_root - } - - // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be - // converted to string. - fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { - let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym::string_type) { - return false; - } - if let ty::Ref(_, ty, ..) = arg_ty.kind() { - if *ty.kind() == ty::Str && can_be_static_str(cx, arg) { - return false; - } - }; - true - } - - // Check if an expression could have type `&'static str`, knowing that it - // has type `&str` for some lifetime. - fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { - match arg.kind { - hir::ExprKind::Lit(_) => true, - hir::ExprKind::Call(fun, _) => { - if let hir::ExprKind::Path(ref p) = fun.kind { - match cx.qpath_res(p, fun.hir_id) { - hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( - cx.tcx.fn_sig(def_id).output().skip_binder().kind(), - ty::Ref(ty::ReStatic, ..) - ), - _ => false, - } - } else { - false - } - }, - hir::ExprKind::MethodCall(..) => { - cx.typeck_results() - .type_dependent_def_id(arg.hir_id) - .map_or(false, |method_id| { - matches!( - cx.tcx.fn_sig(method_id).output().skip_binder().kind(), - ty::Ref(ty::ReStatic, ..) - ) - }) - }, - 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, - } - } - - fn generate_format_arg_snippet( - cx: &LateContext<'_>, - a: &hir::Expr<'_>, - applicability: &mut Applicability, - ) -> Vec { - if_chain! { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind; - if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind; - if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind; - - then { - format_arg_expr_tup - .iter() - .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned()) - .collect() - } else { - unreachable!() - } - } - } - - fn is_call(node: &hir::ExprKind<'_>) -> bool { - match node { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => { - is_call(&expr.kind) - }, - hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) - // These variants are debatable or require further examination - | hir::ExprKind::If(..) - | hir::ExprKind::Match(..) - | hir::ExprKind::Block{ .. } => true, - _ => false, - } - } - - if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) { - return; - } - - let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) { - "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) { - "|_|" - } else { - return; - }; - - let arg_root = get_arg_root(cx, &args[1]); - - let span_replace_word = method_span.with_hi(expr.span.hi()); - - let mut applicability = Applicability::MachineApplicable; - - //Special handling for `format!` as arg_root - if_chain! { - if let hir::ExprKind::Block(block, None) = &arg_root.kind; - if block.stmts.len() == 1; - if let hir::StmtKind::Local(local) = &block.stmts[0].kind; - if let Some(arg_root) = &local.init; - if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind; - if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; - if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; - then { - let fmt_spec = &format_args[0]; - let fmt_args = &format_args[1]; - - let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()]; - - args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability)); - - let sugg = args.join(", "); - - span_lint_and_sugg( - cx, - EXPECT_FUN_CALL, - span_replace_word, - &format!("use of `{}` followed by a function call", name), - "try this", - format!("unwrap_or_else({} panic!({}))", closure_args, sugg), - applicability, - ); - - return; - } - } - - let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability); - if requires_to_string(cx, arg_root) { - arg_root_snippet.to_mut().push_str(".to_string()"); - } - - span_lint_and_sugg( - cx, - EXPECT_FUN_CALL, - span_replace_word, - &format!("use of `{}` followed by a function call", name), - "try this", - format!( - "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", - closure_args, arg_root_snippet - ), - applicability, - ); -} - fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, -- cgit 1.4.1-3-g733a5 From 99f860768cdd71c371cdaf7827c377dbaa4fe396 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 11 Mar 2021 20:02:29 +0900 Subject: remove unused imports --- clippy_lints/src/methods/into_iter_on_ref.rs | 3 ++- clippy_lints/src/methods/mod.rs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index d94b243404c..1e8315dbee2 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; use super::INTO_ITER_ON_REF; @@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty< } } -fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> { +fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { has_iter_method(cx, self_ref_ty).map(|ty_name| { let mutbl = match self_ref_ty.kind() { ty::Ref(_, _, mutbl) => mutbl, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 180620e860a..7fd14c4f9b1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -62,8 +62,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; -use rustc_span::symbol::{sym, Symbol, SymbolStr}; +use rustc_span::symbol::{sym, SymbolStr}; use rustc_typeck::hir_ty_to_ty; use crate::utils::{ -- cgit 1.4.1-3-g733a5 From 83a955335f986c503ac114cf5009ff965bb21741 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 11 Mar 2021 20:18:33 +0900 Subject: fix interning-defined-symbol error --- clippy_lints/src/methods/get_unwrap.rs | 4 ++-- clippy_lints/src/methods/iter_nth.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 2684c50e01b..e157db2712a 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -27,10 +27,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) { needs_ref = get_args_str.parse::().is_ok(); "Vec" - } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) { + } else if is_type_diagnostic_item(cx, expr_ty, sym::vecdeque_type) { needs_ref = get_args_str.parse::().is_ok(); "VecDeque" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym!(hashmap_type)) { + } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::hashmap_type) { needs_ref = true; "HashMap" } else if !is_mut && match_type(cx, expr_ty, &paths::BTREEMAP) { diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index c8adea9536b..cc3e56ea872 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( "slice" } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { "Vec" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vecdeque_type) { "VecDeque" } else { let nth_args = nth_and_iter_args[0]; -- cgit 1.4.1-3-g733a5 From 2547edb84272aeb3d01e2b61640a9107c01b281b Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 11 Mar 2021 23:47:15 -0800 Subject: wrong_self_convention: fix lint in case of `to_*_mut` method When a method starts with `to_` and ends with `_mut`, it should expect a `&mut self` parameter, otherwise `&self`. --- clippy_lints/src/methods/mod.rs | 15 ++-- clippy_lints/src/methods/wrong_self_convention.rs | 72 +++++++++++++---- tests/ui/def_id_nocore.stderr | 3 +- tests/ui/wrong_self_convention.stderr | 95 +++++++++++++++++------ tests/ui/wrong_self_conventions_mut.rs | 30 +++++++ tests/ui/wrong_self_conventions_mut.stderr | 19 +++++ 6 files changed, 189 insertions(+), 45 deletions(-) create mode 100644 tests/ui/wrong_self_conventions_mut.rs create mode 100644 tests/ui/wrong_self_conventions_mut.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7fd14c4f9b1..16d5e3bb43f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -191,13 +191,14 @@ declare_clippy_lint! { /// **What it does:** Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |`self` taken | - /// |-------|----------------------| - /// |`as_` |`&self` or `&mut self`| - /// |`from_`| none | - /// |`into_`|`self` | - /// |`is_` |`&self` or none | - /// |`to_` |`&self` | + /// |Prefix |Postfix |`self` taken | + /// |-------|------------|----------------------| + /// |`as_` | none |`&self` or `&mut self`| + /// |`from_`| none | none | + /// |`into_`| none |`self` | + /// |`is_` | none |`&self` or none | + /// |`to_` | `_mut` |`&mut &self` | + /// |`to_` | not `_mut` |`&self` | /// /// **Why is this bad?** Consistency breeds readability. If you follow the /// conventions, your users won't be surprised that they, e.g., need to supply a diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 90fab577436..c8bcad7be3e 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,5 +1,5 @@ use crate::methods::SelfKind; -use crate::utils::span_lint; +use crate::utils::span_lint_and_help; use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::source_map::Span; @@ -9,18 +9,22 @@ use super::WRONG_PUB_SELF_CONVENTION; use super::WRONG_SELF_CONVENTION; #[rustfmt::skip] -const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [ - (Convention::Eq("new"), &[SelfKind::No]), - (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]), - (Convention::StartsWith("from_"), &[SelfKind::No]), - (Convention::StartsWith("into_"), &[SelfKind::Value]), - (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]), - (Convention::Eq("to_mut"), &[SelfKind::RefMut]), - (Convention::StartsWith("to_"), &[SelfKind::Ref]), +const CONVENTIONS: [(&[Convention], &[SelfKind]); 8] = [ + (&[Convention::Eq("new")], &[SelfKind::No]), + (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), + (&[Convention::StartsWith("from_")], &[SelfKind::No]), + (&[Convention::StartsWith("into_")], &[SelfKind::Value]), + (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), + (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut")], &[SelfKind::Ref]), ]; + enum Convention { Eq(&'static str), StartsWith(&'static str), + EndsWith(&'static str), + NotEndsWith(&'static str), } impl Convention { @@ -29,6 +33,8 @@ impl Convention { match *self { Self::Eq(this) => this == other, Self::StartsWith(this) => other.starts_with(this) && this != other, + Self::EndsWith(this) => other.ends_with(this) && this != other, + Self::NotEndsWith(this) => !Self::EndsWith(this).check(other), } } } @@ -38,6 +44,8 @@ impl fmt::Display for Convention { match *self { Self::Eq(this) => this.fmt(f), Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), + Self::EndsWith(this) => '*'.fmt(f).and_then(|_| this.fmt(f)), + Self::NotEndsWith(this) => '~'.fmt(f).and_then(|_| this.fmt(f)), } } } @@ -55,21 +63,59 @@ pub(super) fn check<'tcx>( } else { WRONG_SELF_CONVENTION }; - if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if let Some((conventions, self_kinds)) = &CONVENTIONS + .iter() + .find(|(convs, _)| convs.iter().all(|conv| conv.check(item_name))) + { if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( + let suggestion = { + if conventions.len() > 1 { + let special_case = { + // Don't mention `NotEndsWith` when there is also `StartsWith` convention present + if conventions.len() == 2 { + match conventions { + [Convention::StartsWith(starts_with), Convention::NotEndsWith(_)] + | [Convention::NotEndsWith(_), Convention::StartsWith(starts_with)] => { + Some(format!("methods called `{}`", Convention::StartsWith(starts_with))) + }, + _ => None, + } + } else { + None + } + }; + + if let Some(suggestion) = special_case { + suggestion + } else { + let s = conventions + .iter() + .map(|c| format!("`{}`", &c.to_string())) + .collect::>() + .join(" and "); + + format!("methods called like this: ({})", &s) + } + } else { + format!("methods called `{}`", &conventions[0]) + } + }; + + span_lint_and_help( cx, lint, first_arg_span, &format!( - "methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, + "{} usually take {}", + suggestion, &self_kinds .iter() .map(|k| k.description()) .collect::>() .join(" or ") ), + None, + "consider choosing a less ambiguous name", ); } } diff --git a/tests/ui/def_id_nocore.stderr b/tests/ui/def_id_nocore.stderr index ed87a50547d..a3e9cc75b08 100644 --- a/tests/ui/def_id_nocore.stderr +++ b/tests/ui/def_id_nocore.stderr @@ -1,10 +1,11 @@ -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/def_id_nocore.rs:26:19 | LL | pub fn as_ref(self) -> &'static str { | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name error: aborting due to previous error diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 32bd9075bd5..f43fea0d513 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,148 +1,195 @@ -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:95:19 | LL | fn as_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:98:25 | LL | fn into_i32_ref(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:102:19 | LL | fn to_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:122:25 | LL | fn into_i32_ref(&self); | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:126:19 | LL | fn to_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:128:21 | LL | fn from_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:146:25 | LL | fn into_i32_ref(&self); | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_conventions_mut.rs b/tests/ui/wrong_self_conventions_mut.rs new file mode 100644 index 00000000000..486a0d77235 --- /dev/null +++ b/tests/ui/wrong_self_conventions_mut.rs @@ -0,0 +1,30 @@ +// edition:2018 +#![warn(clippy::wrong_self_convention)] +#![allow(dead_code)] + +fn main() {} + +mod issue6758 { + pub enum Test { + One(T), + Many(Vec), + } + + impl Test { + // If a method starts with `to_` and not ends with `_mut` it should expect `&self` + pub fn to_many(&mut self) -> Option<&mut [T]> { + match self { + Self::Many(data) => Some(data), + _ => None, + } + } + + // If a method starts with `to_` and ends with `_mut` it should expect `&mut self` + pub fn to_many_mut(&self) -> Option<&[T]> { + match self { + Self::Many(data) => Some(data), + _ => None, + } + } + } +} diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr new file mode 100644 index 00000000000..7662b38e67d --- /dev/null +++ b/tests/ui/wrong_self_conventions_mut.stderr @@ -0,0 +1,19 @@ +error: methods called `to_*` usually take self by reference + --> $DIR/wrong_self_conventions_mut.rs:15:24 + | +LL | pub fn to_many(&mut self) -> Option<&mut [T]> { + | ^^^^^^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: methods called like this: (`to_*` and `*_mut`) usually take self by mutable reference + --> $DIR/wrong_self_conventions_mut.rs:23:28 + | +LL | pub fn to_many_mut(&self) -> Option<&[T]> { + | ^^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 11d2af7e9694e8e63000e33f77d4926e936e3822 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Fri, 12 Mar 2021 20:46:46 +0900 Subject: Improve suggestion and make it work for macros --- clippy_lints/src/if_then_some_else_none.rs | 18 ++++++++++++---- tests/ui/if_then_some_else_none.rs | 16 ++++++++++++++ tests/ui/if_then_some_else_none.stderr | 34 ++++++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 569a7f06f95..a527f51b1fd 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -82,12 +82,22 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if utils::match_qpath(els_call_qpath, &utils::paths::OPTION_NONE); then { - let cond_snip = utils::snippet(cx, cond.span, "[condition]"); - let arg_snip = utils::snippet(cx, then_arg.span, ""); + let cond_snip = utils::snippet_with_macro_callsite(cx, cond.span, "[condition]"); + let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { + format!("({})", cond_snip) + } else { + cond_snip.into_owned() + }; + let arg_snip = utils::snippet_with_macro_callsite(cx, then_arg.span, ""); + let closure_body = if then_block.stmts.is_empty() { + arg_snip.into_owned() + } else { + format!("{{ /* snippet */ {} }}", arg_snip) + }; let help = format!( - "consider using `bool::then` like: `{}.then(|| {{ /* snippet */ {} }})`", + "consider using `bool::then` like: `{}.then(|| {})`", cond_snip, - arg_snip, + closure_body, ); utils::span_lint_and_help( cx, diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 14a5fe76245..54789bf9320 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -10,6 +10,22 @@ fn main() { None }; + // Should issue an error when macros are used. + let _ = if matches!(true, true) { + println!("true!"); + Some(matches!(true, false)) + } else { + None + }; + + // Should issue an error. Binary expression `o < 32` should be parenthesized. + let x = Some(5); + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Should issue an error. Unary expression `!x` should be parenthesized. + let x = true; + let _ = if !x { Some(0) } else { None }; + // Should not issue an error since the `else` block has a statement besides `None`. let _ = if foo() { println!("true!"); diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 722c52b1cb4..8cb22d569f4 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -14,7 +14,37 @@ LL | | }; = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:66:13 + --> $DIR/if_then_some_else_none.rs:14:13 + | +LL | let _ = if matches!(true, true) { + | _____________^ +LL | | println!("true!"); +LL | | Some(matches!(true, false)) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | + = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:23:28 + | +LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `bool::then` like: `(o < 32).then(|| o)` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:27:13 + | +LL | let _ = if !x { Some(0) } else { None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `bool::then` like: `(!x).then(|| 0)` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:82:13 | LL | let _ = if foo() { | _____________^ @@ -27,5 +57,5 @@ LL | | }; | = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })` -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 93ee80ac3e9ecc3eee4246c56fce5582a6e1a15f Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Mar 2021 02:09:19 +0900 Subject: Use sym::Iterator instead of paths::ITERATOR --- clippy_lints/src/blocks_in_if_conditions.rs | 7 ++++--- clippy_lints/src/copy_iterator.rs | 21 +++++++++++++-------- clippy_lints/src/loops/iter_next_loop.rs | 6 ++++-- clippy_lints/src/loops/needless_collect.rs | 6 +++--- clippy_lints/src/loops/utils.rs | 7 +++---- clippy_lints/src/loops/while_let_on_iterator.rs | 8 ++++---- clippy_lints/src/map_clone.rs | 5 ++--- clippy_lints/src/map_identity.rs | 4 ++-- clippy_lints/src/matches.rs | 4 ++-- clippy_lints/src/methods/filter_flat_map.rs | 5 +++-- clippy_lints/src/methods/filter_map.rs | 4 ++-- clippy_lints/src/methods/filter_map_flat_map.rs | 5 +++-- clippy_lints/src/methods/filter_map_identity.rs | 6 +++--- clippy_lints/src/methods/filter_map_map.rs | 5 +++-- clippy_lints/src/methods/filter_map_next.rs | 5 +++-- clippy_lints/src/methods/filter_next.rs | 5 +++-- clippy_lints/src/methods/flat_map_identity.rs | 6 +++--- .../src/methods/from_iter_instead_of_collect.rs | 3 ++- clippy_lints/src/methods/inspect_for_each.rs | 6 +++--- clippy_lints/src/methods/iter_nth_zero.rs | 5 +++-- clippy_lints/src/methods/iter_skip_next.rs | 5 +++-- clippy_lints/src/methods/iterator_step_by_zero.rs | 5 +++-- clippy_lints/src/methods/map_collect_result_unit.rs | 4 ++-- clippy_lints/src/methods/map_flatten.rs | 4 ++-- clippy_lints/src/methods/search_is_some.rs | 4 ++-- clippy_lints/src/methods/skip_while_next.rs | 5 +++-- clippy_lints/src/methods/unnecessary_filter_map.rs | 5 +++-- clippy_lints/src/methods/unnecessary_fold.rs | 7 +++---- clippy_utils/src/lib.rs | 8 ++++++++ clippy_utils/src/paths.rs | 1 - 30 files changed, 97 insertions(+), 74 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index b53f80fd8bc..59bddb8473b 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,6 +1,6 @@ use crate::utils::{ - differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths, - snippet_block_with_applicability, span_lint, span_lint_and_sugg, + differing_macro_contexts, get_parent_expr, implements_trait, snippet_block_with_applicability, span_lint, + span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -10,6 +10,7 @@ use rustc_lint::{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::sym; declare_clippy_lint! { /// **What it does:** Checks for `if` conditions that use blocks containing an @@ -61,7 +62,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { if let Some(parent) = get_parent_expr(self.cx, expr); if let ExprKind::MethodCall(_, _, args, _) = parent.kind; let caller = self.cx.typeck_results().expr_ty(&args[0]); - if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR); + if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); if implements_trait(self.cx, caller, iter_id, &[]); then { return; diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 004bce5f62a..0502f725efb 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -1,7 +1,10 @@ -use crate::utils::{is_copy, match_path, paths, span_lint_and_note}; +use crate::utils::{is_copy, span_lint_and_note}; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +use if_chain::if_chain; declare_clippy_lint! { /// **What it does:** Checks for types that implement `Copy` as well as @@ -33,14 +36,16 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - .. - }) = item.kind - { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + .. + }) = item.kind; let ty = cx.tcx.type_of(item.def_id); - - if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) { + if is_copy(cx, ty); + if let Some(trait_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); + then { span_lint_and_note( cx, COPY_ITERATOR, diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index cf78bbc49a3..d6b40fa9fa8 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -1,10 +1,12 @@ use super::ITER_NEXT_LOOP; -use crate::utils::{match_trait_method, paths, span_lint}; use rustc_hir::Expr; use rustc_lint::LateContext; +use rustc_span::sym; + +use crate::utils::{is_trait_method, span_lint}; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool { - if match_trait_method(cx, arg, &paths::ITERATOR) { + if is_trait_method(cx, arg, sym::Iterator) { span_lint( cx, ITER_NEXT_LOOP, diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 92560c80629..f8432abfa8a 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,7 +1,7 @@ use super::NEEDLESS_COLLECT; use crate::utils::sugg::Sugg; use crate::utils::{ - is_type_diagnostic_item, match_trait_method, match_type, path_to_local_id, paths, snippet, span_lint_and_sugg, + is_trait_method, is_type_diagnostic_item, match_type, path_to_local_id, paths, snippet, span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; @@ -23,7 +23,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; - if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR); + if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); if let Some(ref generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); then { @@ -94,7 +94,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo 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 method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::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); diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 9e38e17719a..e62b2ab16d1 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,6 +1,5 @@ use crate::utils::{ - get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, is_integer_const, path_to_local, - path_to_local_id, paths, sugg, + get_parent_expr, has_iter_method, implements_trait, is_integer_const, path_to_local, path_to_local_id, sugg, }; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; @@ -10,7 +9,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, P use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{sym, Symbol}; use std::iter::Iterator; #[derive(Debug, PartialEq)] @@ -316,7 +315,7 @@ pub(super) fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the /// actual `Iterator` that the loop uses. pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { - let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |id| { + let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) }); if impls_iterator { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index e5a47694faa..f3b8d0da9cd 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,8 +2,8 @@ use super::utils::{LoopNestVisitor, Nesting}; use super::WHILE_LET_ON_ITERATOR; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_enclosing_block, get_trait_def_id, implements_trait, is_refutable, last_path_segment, match_trait_method, - path_to_local, path_to_local_id, paths, snippet_with_applicability, span_lint_and_sugg, + get_enclosing_block, implements_trait, is_refutable, is_trait_method, last_path_segment, path_to_local, + path_to_local_id, snippet_with_applicability, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Don't lint when the iterator is recreated on every iteration if_chain! { if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; - if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR); + if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator); if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]); then { return; @@ -36,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let lhs_constructor = last_path_segment(qpath); if method_path.ident.name == sym::next - && match_trait_method(cx, match_expr, &paths::ITERATOR) + && is_trait_method(cx, match_expr, sym::Iterator) && lhs_constructor.ident.name == sym::Some && (pat_args.is_empty() || !is_refutable(cx, &pat_args[0]) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 4b685c09a05..61cf768bcab 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,6 +1,5 @@ -use crate::utils::paths; use crate::utils::{ - is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg, + is_copy, is_trait_method, is_type_diagnostic_item, remove_blocks, snippet_with_applicability, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -55,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if args.len() == 2; if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); + if is_type_diagnostic_item(cx, ty, sym::option_type) || is_trait_method(cx, e, sym::Iterator); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 9f9c108a85a..7202a29edd9 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks, + is_adjusted, is_trait_method, is_type_diagnostic_item, match_path, match_var, paths, remove_blocks, span_lint_and_sugg, }; use if_chain::if_chain; @@ -65,7 +65,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); - if match_trait_method(cx, expr, &paths::ITERATOR) + if is_trait_method(cx, expr, sym::Iterator) || is_type_diagnostic_item(cx, caller_ty, sym::result_type) || is_type_diagnostic_item(cx, caller_ty, sym::option_type); then { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 1d5a6e7fcc5..e6c2885eb4d 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1614,7 +1614,7 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; - use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; + use crate::utils::{is_trait_method, match_qpath, paths, snippet, span_lint_and_then}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -1679,7 +1679,7 @@ mod redundant_pattern_match { 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); + if is_trait_method(cx, op, sym::Iterator); then { return; } diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs index 8da867fce51..42f05aa0b3a 100644 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ b/clippy_lints/src/methods/filter_flat_map.rs @@ -1,6 +1,7 @@ -use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use crate::utils::{is_trait_method, span_lint_and_help}; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::FILTER_MAP; @@ -12,7 +13,7 @@ pub(super) fn check<'tcx>( _map_args: &'tcx [hir::Expr<'_>], ) { // lint if caller of `.filter().flat_map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::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()`"; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index f559160004c..91c11afcaaf 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_trait_method, path_to_local_id, paths, snippet, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{is_trait_method, path_to_local_id, snippet, span_lint_and_sugg, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_ if_chain! { if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if match_trait_method(cx, map_recv, &paths::ITERATOR); + if is_trait_method(cx, map_recv, sym::Iterator); // filter(|x| ...is_some())... if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs index a6db138623a..e113f3f71b1 100644 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ b/clippy_lints/src/methods/filter_map_flat_map.rs @@ -1,6 +1,7 @@ -use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use crate::utils::{is_trait_method, span_lint_and_help}; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::FILTER_MAP; @@ -12,7 +13,7 @@ pub(super) fn check<'tcx>( _map_args: &'tcx [hir::Expr<'_>], ) { // lint if caller of `.filter_map().flat_map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::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()`"; diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 9e646360a40..5f627b42abc 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,9 +1,9 @@ -use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint_and_sugg}; +use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::source_map::Span; +use rustc_span::{source_map::Span, sym}; use super::FILTER_MAP_IDENTITY; @@ -13,7 +13,7 @@ pub(super) fn check( filter_map_args: &[hir::Expr<'_>], filter_map_span: Span, ) { - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { let arg_node = &filter_map_args[1].kind; let apply_lint = |message: &str| { diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs index d015b4c7b38..2e704c4c555 100644 --- a/clippy_lints/src/methods/filter_map_map.rs +++ b/clippy_lints/src/methods/filter_map_map.rs @@ -1,6 +1,7 @@ -use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use crate::utils::{is_trait_method, span_lint_and_help}; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::FILTER_MAP; @@ -12,7 +13,7 @@ pub(super) fn check<'tcx>( _map_args: &'tcx [hir::Expr<'_>], ) { // lint if caller of `.filter_map().map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::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); diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index a789df922ff..9019202df0c 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,8 +1,9 @@ -use crate::utils::{match_trait_method, meets_msrv, paths, snippet, span_lint, span_lint_and_sugg}; +use crate::utils::{is_trait_method, meets_msrv, snippet, span_lint, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_semver::RustcVersion; +use rustc_span::sym; use super::FILTER_MAP_NEXT; @@ -14,7 +15,7 @@ pub(super) fn check<'tcx>( filter_args: &'tcx [hir::Expr<'_>], msrv: Option<&RustcVersion>, ) { - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { return; } diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 81619e73017..34787b9a828 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,14 +1,15 @@ -use crate::utils::{match_trait_method, paths, snippet, span_lint, span_lint_and_sugg}; +use crate::utils::{is_trait_method, snippet, span_lint, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::FILTER_NEXT; /// lint use of `filter().next()` for `Iterators` pub(super) fn check<'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) { + if is_trait_method(cx, expr, sym::Iterator) { 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, ".."); diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index ce3194f8a23..19ddceeccce 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,9 +1,9 @@ -use crate::utils::{match_qpath, match_trait_method, paths, span_lint_and_sugg}; +use crate::utils::{is_trait_method, match_qpath, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::source_map::Span; +use rustc_span::{source_map::Span, sym}; use super::FLAT_MAP_IDENTITY; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( flat_map_args: &'tcx [hir::Expr<'_>], flat_map_span: Span, ) { - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { let arg_node = &flat_map_args[1].kind; let apply_lint = |message: &str| { diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index e50d0a33400..a5c5bc0e4e2 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -4,6 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; +use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; @@ -13,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp if_chain! { if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); - if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR); + if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); then { diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index 959457a5bfc..e7c3a433fe1 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -1,14 +1,14 @@ use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::source_map::Span; +use rustc_span::{source_map::Span, sym}; -use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use crate::utils::{is_trait_method, span_lint_and_help}; use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; span_lint_and_help( diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 247192d81f3..ca2e5db4382 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,15 +1,16 @@ use crate::consts::{constant, Constant}; -use crate::utils::{match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{is_trait_method, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::ITER_NTH_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) { if_chain! { - if match_trait_method(cx, expr, &paths::ITERATOR); + if is_trait_method(cx, expr, sym::Iterator); if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index 5f5969134e4..bdfa133b9e2 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,13 +1,14 @@ -use crate::utils::{match_trait_method, paths, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::ITER_SKIP_NEXT; pub(super) fn check(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) { + if is_trait_method(cx, expr, sym::Iterator) { if let [caller, n] = skip_args { let hint = format!(".nth({})", snippet(cx, n.span, "..")); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 3e05d7f76b7..019a08f746e 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,12 +1,13 @@ use crate::consts::{constant, Constant}; -use crate::utils::{match_trait_method, paths, span_lint}; +use crate::utils::{is_trait_method, span_lint}; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) { span_lint( cx, diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index 5b20e268d9f..8f4de00a2b7 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, is_type_diagnostic_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -17,7 +17,7 @@ pub(super) fn check( if_chain! { // called on Iterator if let [map_expr] = collect_args; - if match_trait_method(cx, map_expr, &paths::ITERATOR); + if is_trait_method(cx, map_expr, sym::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); diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 14a14e4f9ec..afd76e8b959 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::MAP_FLATTEN; /// lint use of `map().flatten()` for `Iterators` and 'Options' pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.map().flatten()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); let is_map_to_option = match map_closure_ty.kind() { ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index e9e65443220..eb3dc308722 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_trait_method, paths, snippet, snippet_with_applicability, span_lint_and_help, + is_trait_method, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, strip_pat_refs, }; use if_chain::if_chain; @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( method_span: Span, ) { // lint if caller of search is an Iterator - if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { + if is_trait_method(cx, &is_some_args[0], sym::Iterator) { let msg = format!( "called `is_some()` after searching an `Iterator` with `{}`", search_method diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index 8ba6ae95200..a9ff78c3260 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -1,13 +1,14 @@ -use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use crate::utils::{is_trait_method, span_lint_and_help}; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::SKIP_WHILE_NEXT; /// lint use of `skip_while().next()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, _skip_while_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.skip_while().next()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { + if is_trait_method(cx, expr, sym::Iterator) { span_lint_and_help( cx, SKIP_WHILE_NEXT, diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 12b2cf0a165..686874c0a24 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,16 +1,17 @@ use crate::utils::usage::mutated_variables; -use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint}; +use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths, span_lint}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use rustc_span::sym; use if_chain::if_chain; use super::UNNECESSARY_FILTER_MAP; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - if !match_trait_method(cx, expr, &paths::ITERATOR) { + if !is_trait_method(cx, expr, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index a26443f4ee9..9aa53f02ef0 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,5 @@ use crate::utils::{ - match_trait_method, path_to_local_id, paths, remove_blocks, snippet_with_applicability, span_lint_and_sugg, - strip_pat_refs, + is_trait_method, path_to_local_id, remove_blocks, snippet_with_applicability, span_lint_and_sugg, strip_pat_refs, }; use if_chain::if_chain; use rustc_ast::ast; @@ -8,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::source_map::Span; +use rustc_span::{source_map::Span, sym}; use super::UNNECESSARY_FOLD; @@ -71,7 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir } // Check that this is a call to Iterator::fold rather than just some function called fold - if !match_trait_method(cx, expr, &paths::ITERATOR) { + if !is_trait_method(cx, expr, sym::Iterator) { return; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e17af49f618..3dad21d2028 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -273,6 +273,7 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { } /// Checks if the method call given in `expr` belongs to the given trait. +/// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); let trt_id = cx.tcx.trait_of_item(def_id); @@ -295,6 +296,13 @@ pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id)) } +/// Checks if the method call given in `expr` belongs to the given trait. +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { + cx.typeck_results() + .type_dependent_def_id(expr.hir_id) + .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item)) +} + /// Checks if an expression references a variable of the given name. pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 560614efc74..11a446e42a4 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -61,7 +61,6 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; -pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] -- cgit 1.4.1-3-g733a5 From 6bc5fe4a88d1c6dce50c7be169f8be5d77d4f7e7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 12 Mar 2021 20:40:23 +0100 Subject: inconsistent_struct_constructor: try to make message and lint description a bit clearer --- clippy_lints/src/inconsistent_struct_constructor.rs | 12 +++++++----- tests/ui/inconsistent_struct_constructor.stderr | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 4f35e13c85a..49c17a12102 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -10,8 +10,9 @@ use if_chain::if_chain; use crate::utils::{snippet, span_lint_and_sugg}; declare_clippy_lint! { - /// **What it does:** Checks for struct constructors where the order of the field init - /// shorthand in the constructor is inconsistent with the order in the struct definition. + /// **What it does:** Checks for struct constructors where all fields are shorthand and + /// the order of the field init shorthand in the constructor is inconsistent + /// with the order in the struct definition. /// /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the /// resulted instance as the below example indicates, @@ -25,11 +26,11 @@ declare_clippy_lint! { /// let x = 1; /// let y = 2; /// - /// // This assertion never fails. + /// // This assertion never fails: /// assert_eq!(Foo { x, y }, Foo { y, x }); /// ``` /// - /// inconsistent order means nothing and just decreases readability and consistency. + /// inconsistent order can be confusing and decreases readability and consistency. /// /// **Known problems:** None. /// @@ -42,6 +43,7 @@ declare_clippy_lint! { /// } /// let x = 1; /// let y = 2; + /// /// Foo { y, x }; /// ``` /// @@ -107,7 +109,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { cx, INCONSISTENT_STRUCT_CONSTRUCTOR, expr.span, - "inconsistent struct constructor", + "struct constructor field order is inconsistent with struct definition field order", "try", sugg, Applicability::MachineApplicable, diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index d7abe44f254..d021bb19579 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,4 +1,4 @@ -error: inconsistent struct constructor +error: struct constructor field order is inconsistent with struct definition field order --> $DIR/inconsistent_struct_constructor.rs:25:9 | LL | Foo { y, x, z }; @@ -6,7 +6,7 @@ LL | Foo { y, x, z }; | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` -error: inconsistent struct constructor +error: struct constructor field order is inconsistent with struct definition field order --> $DIR/inconsistent_struct_constructor.rs:43:9 | LL | / Foo { -- cgit 1.4.1-3-g733a5 From c86ba7f92d9627d2170827c07449542c7c0f7d77 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 1 Mar 2021 16:49:52 +0100 Subject: mem_replace_with_default: recognize some std library ctors --- clippy_lints/src/mem_replace.rs | 17 ++++++++- clippy_utils/src/paths.rs | 8 +++++ tests/ui/mem_replace.fixed | 29 +++++++++++++++ tests/ui/mem_replace.rs | 29 +++++++++++++++ tests/ui/mem_replace.stderr | 78 +++++++++++++++++++++++++++++++++++++---- 5 files changed, 154 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 19087b02077..2f71e22b4fc 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -200,7 +200,22 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< if !in_external_macro(cx.tcx.sess, expr_span); if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD); + + let defaults = &[ + paths::DEFAULT_TRAIT_METHOD.as_ref(), + paths::STRING_NEW.as_ref(), + paths::VEC_NEW.as_ref(), + paths::VEC_DEQUE_NEW.as_ref(), + paths::LINKED_LIST_NEW.as_ref(), + paths::HASHMAP_NEW.as_ref(), + paths::BTREEMAP_NEW.as_ref(), + paths::HASHSET_NEW.as_ref(), + paths::BTREESET_NEW.as_ref(), + paths::BINARY_HEAP_NEW.as_ref(), + ]; + + if defaults.iter().any(|x| match_def_path(cx, repl_def_id, &x)); + then { span_lint_and_then( cx, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 560614efc74..8234ab7282c 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -11,10 +11,13 @@ pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; +pub const BINARY_HEAP_NEW: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "new"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; +pub const BTREEMAP_NEW: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "new"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; +pub const BTREESET_NEW: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "new"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; @@ -46,8 +49,10 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const HASHMAP_NEW: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "new"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; +pub const HASHSET_NEW: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "new"]; #[cfg(feature = "internal-lints")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal-lints")] @@ -67,6 +72,7 @@ pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; +pub const LINKED_LIST_NEW: [&str; 5] = ["alloc", "collections", "linked_list", "LinkedList", "new"]; #[cfg(feature = "internal-lints")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; @@ -134,6 +140,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_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; 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"]; @@ -161,6 +168,7 @@ 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"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; +pub const VEC_DEQUE_NEW: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "new"]; 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"]; diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 54e962e7116..3b6224254a0 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -7,6 +7,7 @@ clippy::mem_replace_with_default )] +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; fn replace_option_with_none() { @@ -19,9 +20,37 @@ fn replace_option_with_none() { fn replace_with_default() { let mut s = String::from("foo"); let _ = std::mem::take(&mut s); + let s = &mut String::from("foo"); let _ = std::mem::take(s); let _ = std::mem::take(s); + + let mut v = vec![123]; + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + + let mut hash_map: HashMap = HashMap::new(); + let _ = std::mem::take(&mut hash_map); + + let mut btree_map: BTreeMap = BTreeMap::new(); + let _ = std::mem::take(&mut btree_map); + + let mut vd: VecDeque = VecDeque::new(); + let _ = std::mem::take(&mut vd); + + let mut hash_set: HashSet<&str> = HashSet::new(); + let _ = std::mem::take(&mut hash_set); + + let mut btree_set: BTreeSet<&str> = BTreeSet::new(); + let _ = std::mem::take(&mut btree_set); + + let mut list: LinkedList = LinkedList::new(); + let _ = std::mem::take(&mut list); + + let mut binary_heap: BinaryHeap = BinaryHeap::new(); + let _ = std::mem::take(&mut binary_heap); } fn main() { diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 60f52781071..0a36db9e921 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -7,6 +7,7 @@ clippy::mem_replace_with_default )] +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; fn replace_option_with_none() { @@ -19,9 +20,37 @@ fn replace_option_with_none() { fn replace_with_default() { let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); + let s = &mut String::from("foo"); let _ = std::mem::replace(s, String::default()); let _ = std::mem::replace(s, Default::default()); + + let mut v = vec![123]; + let _ = std::mem::replace(&mut v, Vec::default()); + let _ = std::mem::replace(&mut v, Default::default()); + let _ = std::mem::replace(&mut v, Vec::new()); + let _ = std::mem::replace(&mut v, vec![]); + + let mut hash_map: HashMap = HashMap::new(); + let _ = std::mem::replace(&mut hash_map, HashMap::new()); + + let mut btree_map: BTreeMap = BTreeMap::new(); + let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + + let mut vd: VecDeque = VecDeque::new(); + let _ = std::mem::replace(&mut vd, VecDeque::new()); + + let mut hash_set: HashSet<&str> = HashSet::new(); + let _ = std::mem::replace(&mut hash_set, HashSet::new()); + + let mut btree_set: BTreeSet<&str> = BTreeSet::new(); + let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + + let mut list: LinkedList = LinkedList::new(); + let _ = std::mem::replace(&mut list, LinkedList::new()); + + let mut binary_heap: BinaryHeap = BinaryHeap::new(); + let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); } fn main() { diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 245d33aa4f2..f8aa1538bff 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:14:13 + --> $DIR/mem_replace.rs:15:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:16:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:21:13 + --> $DIR/mem_replace.rs:22:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,16 +21,82 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:23:13 + --> $DIR/mem_replace.rs:25:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:24:13 + --> $DIR/mem_replace.rs:26:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` -error: aborting due to 5 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:29:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:30:13 + | +LL | let _ = std::mem::replace(&mut v, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:31:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:32:13 + | +LL | let _ = std::mem::replace(&mut v, vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:35:13 + | +LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:38:13 + | +LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:41:13 + | +LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:44:13 + | +LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:47:13 + | +LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:50:13 + | +LL | let _ = std::mem::replace(&mut list, LinkedList::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:53:13 + | +LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` + +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 41be515062ff709d080cd8a8858f31778a775bcc Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Fri, 12 Mar 2021 12:00:08 -0800 Subject: mem_replace_with_default: use diagnostic items intead of paths --- clippy_lints/src/mem_replace.rs | 49 ++++++++++++++++++++++++++++------------- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/paths.rs | 8 ------- 3 files changed, 35 insertions(+), 24 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 2f71e22b4fc..13f9c9b71f3 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -4,6 +4,7 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -12,6 +13,8 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; +use clippy_utils::is_diagnostic_assoc_item; + declare_clippy_lint! { /// **What it does:** Checks for `mem::replace()` on an `Option` with /// `None`. @@ -194,27 +197,43 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } } +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" +/// constructor from the std library +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { + let std_types_symbols = &[ + sym::string_type, + sym::vec_type, + sym::vecdeque_type, + sym::LinkedList, + sym::hashmap_type, + sym::BTreeMap, + sym::hashset_type, + sym::BTreeSet, + sym::BinaryHeap, + ]; + + if std_types_symbols + .iter() + .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol)) + { + if let QPath::TypeRelative(_, ref method) = path { + if method.ident.name == sym::new { + return true; + } + } + } + + false +} + fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { if let ExprKind::Call(ref repl_func, _) = src.kind { if_chain! { if !in_external_macro(cx.tcx.sess, expr_span); if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - - let defaults = &[ - paths::DEFAULT_TRAIT_METHOD.as_ref(), - paths::STRING_NEW.as_ref(), - paths::VEC_NEW.as_ref(), - paths::VEC_DEQUE_NEW.as_ref(), - paths::LINKED_LIST_NEW.as_ref(), - paths::HASHMAP_NEW.as_ref(), - paths::BTREEMAP_NEW.as_ref(), - paths::HASHSET_NEW.as_ref(), - paths::BTREESET_NEW.as_ref(), - paths::BINARY_HEAP_NEW.as_ref(), - ]; - - if defaults.iter().any(|x| match_def_path(cx, repl_def_id, &x)); + if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); then { span_lint_and_then( diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e17af49f618..045123ab71e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -279,7 +279,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } -/// Checks if the method call given in `expr` belongs to a trait or other container with a given +/// Checks if the method call given in `def_id` belongs to a trait or other container with a given /// diagnostic item pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { cx.tcx diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 8234ab7282c..560614efc74 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -11,13 +11,10 @@ pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; -pub const BINARY_HEAP_NEW: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "new"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; -pub const BTREEMAP_NEW: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "new"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; -pub const BTREESET_NEW: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "new"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; @@ -49,10 +46,8 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; -pub const HASHMAP_NEW: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "new"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; -pub const HASHSET_NEW: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "new"]; #[cfg(feature = "internal-lints")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal-lints")] @@ -72,7 +67,6 @@ pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; -pub const LINKED_LIST_NEW: [&str; 5] = ["alloc", "collections", "linked_list", "LinkedList", "new"]; #[cfg(feature = "internal-lints")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; @@ -140,7 +134,6 @@ 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_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; 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"]; @@ -168,7 +161,6 @@ 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"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; -pub const VEC_DEQUE_NEW: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "new"]; 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"]; -- cgit 1.4.1-3-g733a5 From 6d2236f5036e89b69bdba4a364d73d159ac74187 Mon Sep 17 00:00:00 2001 From: iobtl Date: Sat, 13 Mar 2021 16:54:59 +0800 Subject: replace span_lint with span_lint_and_sugg along with error message --- clippy_lints/src/casts/unnecessary_cast.rs | 7 +++++-- tests/ui/unnecessary_cast.stderr | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index fa2a07ef1da..171cac091c2 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; use if_chain::if_chain; -use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint, span_lint_and_sugg}; +use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint_and_sugg}; use super::UNNECESSARY_CAST; @@ -46,7 +46,7 @@ pub(super) fn check( LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { - span_lint( + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, @@ -54,6 +54,9 @@ pub(super) fn check( "casting to the same type is unnecessary (`{}` -> `{}`)", cast_from, cast_to ), + "try", + literal_str, + Applicability::MachineApplicable, ); return true; } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 8981d13e8ea..87261cd8333 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -2,7 +2,7 @@ error: casting to the same type is unnecessary (`i32` -> `i32`) --> $DIR/unnecessary_cast.rs:6:5 | LL | 1i32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: try: `1i32` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` @@ -10,13 +10,13 @@ error: casting to the same type is unnecessary (`f32` -> `f32`) --> $DIR/unnecessary_cast.rs:7:5 | LL | 1f32 as f32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: try: `1f32` error: casting to the same type is unnecessary (`bool` -> `bool`) --> $DIR/unnecessary_cast.rs:8:5 | LL | false as bool; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `false` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From a261bc5fadc3c72fd8828d64c4281097b2e7c0b0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 2 Mar 2021 13:32:56 -0500 Subject: Make `explicit_deref_methods` check for multiple deref calls Fix suggestion for `explicit_deref_methods`. Sometimes `&**` is needed, sometimes nothing is needed. Allow `explicit_deref_methods` to trigger in a few new contexts. `explicit_deref_methods` will now consider ufcs calls --- clippy_lints/src/dereference.rs | 369 +++++++++++++++++++++++++++------ clippy_lints/src/lib.rs | 2 +- clippy_lints/src/manual_map.rs | 8 +- clippy_lints/src/redundant_deref.rs | 63 ++++++ clippy_utils/src/lib.rs | 82 ++++++-- tests/ui/dereference.fixed | 93 --------- tests/ui/dereference.rs | 93 --------- tests/ui/dereference.stderr | 70 ------- tests/ui/explicit_deref_methods.fixed | 93 +++++++++ tests/ui/explicit_deref_methods.rs | 93 +++++++++ tests/ui/explicit_deref_methods.stderr | 70 +++++++ 11 files changed, 687 insertions(+), 349 deletions(-) create mode 100644 clippy_lints/src/redundant_deref.rs delete mode 100644 tests/ui/dereference.fixed delete mode 100644 tests/ui/dereference.rs delete mode 100644 tests/ui/dereference.stderr create mode 100644 tests/ui/explicit_deref_methods.fixed create mode 100644 tests/ui/explicit_deref_methods.rs create mode 100644 tests/ui/explicit_deref_methods.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b5fb51af1c7..a33634ca34e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,11 +1,13 @@ -use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX}; +use crate::utils::{ + get_node_span, get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg, +}; +use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_middle::ty::{self, adjustment::Adjustment, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. @@ -34,76 +36,313 @@ declare_clippy_lint! { "Explicit use of deref or deref_mut method while not in a method chain." } -declare_lint_pass!(Dereferencing => [ - EXPLICIT_DEREF_METHODS +impl_lint_pass!(Dereferencing => [ + EXPLICIT_DEREF_METHODS, ]); +#[derive(Default)] +pub struct Dereferencing { + state: Option<(State, StateData)>, + + // While parsing a `deref` method call in ufcs form, the path to the function is itself an + // expression. This is to store the id of that expression so it can be skipped when + // `check_expr` is called for it. + skip_expr: Option, +} + +struct StateData { + /// Span of the top level expression + span: Span, + /// The required mutability + target_mut: Mutability, +} + +enum State { + // Any number of deref method calls. + DerefMethod { + // The number of calls in a sequence which changed the referenced type + ty_changed_count: usize, + is_final_ufcs: bool, + }, +} + +// A reference operation considered by this lint pass +enum RefOp { + Method, + Deref, + AddrOf, +} + impl<'tcx> LateLintPass<'tcx> for Dereferencing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind; - if args.len() == 1; - - then { - if let Some(parent_expr) = get_parent_expr(cx, expr) { - // Check if we have the whole call chain here - if let ExprKind::MethodCall(..) = parent_expr.kind { - return; - } - // Check for Expr that we don't want to be linted - let precedence = parent_expr.precedence(); - match precedence { - // Lint a Call is ok though - ExprPrecedence::Call | ExprPrecedence::AddrOf => (), - _ => { - if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX { - return; - } - } + // Skip path expressions from deref calls. e.g. `Deref::deref(e)` + if Some(expr.hir_id) == self.skip_expr.take() { + return; + } + + // Stop processing sub expressions when a macro call is seen + if in_macro(expr.span) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + } + + let typeck = cx.typeck_results(); + let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { + x + } else { + // The whole chain of reference operations has been seen + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + }; + + match (self.state.take(), kind) { + (None, kind) => { + let parent = get_parent_node(cx.tcx, expr.hir_id); + // This is an odd case. The expression is a macro argument, but the top level + // address of expression is inserted by the compiler. + if matches!(kind, RefOp::AddrOf) && parent.and_then(get_node_span).map_or(false, in_macro) { + return; + } + + let expr_adjustments = find_adjustments(cx.tcx, typeck, expr); + let expr_ty = typeck.expr_ty(expr); + let target_mut = + if let ty::Ref(_, _, mutability) = *expr_adjustments.last().map_or(expr_ty, |a| a.target).kind() { + mutability + } else { + Mutability::Not + }; + + match kind { + RefOp::Method + if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) + && is_linted_explicit_deref_position(parent, expr.hir_id) => + { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) { + 0 + } else { + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + StateData { + span: expr.span, + target_mut, + }, + )); } + _ => (), } - let name = method_name.ident.as_str(); - lint_deref(cx, &*name, &args[0], args[0].span, expr.span); - } + }, + (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method) => { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { + ty_changed_count + } else { + ty_changed_count + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + data, + )); + }, + + (Some((state, data)), _) => report(cx, expr, state, data), } } } -fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { - match method_name { - "deref" => { - let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref method call", - "try this", - format!("&*{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } +fn try_parse_ref_op( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> Option<(RefOp, &'tcx Expr<'tcx>)> { + let (def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg), + ExprKind::Call( + Expr { + kind: ExprKind::Path(path), + hir_id, + .. + }, + [arg], + ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => { + return Some((RefOp::Deref, sub_expr)); }, - "deref_mut" => { - let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_mut_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref_mut method call", - "try this", - format!("&mut *{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } + ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)), + _ => return None, + }; + (tcx.is_diagnostic_item(sym::deref_method, def_id) + || tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()?) + .then(|| (RefOp::Method, arg)) +} + +// Checks whether the type for a deref call actually changed the type, not just the mutability of +// the reference. +fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { + match (result_ty.kind(), arg_ty.kind()) { + (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), + + // The result type for a deref method is always a reference + // Not matching the previous pattern means the argument type is not a reference + // This means that the type did change + _ => false, + } +} + +// Adjustments are sometimes made in the parent block rather than the expression itself. +fn find_adjustments( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> &'tcx [Adjustment<'tcx>] { + let map = tcx.hir(); + let mut iter = map.parent_iter(expr.hir_id); + let mut prev = expr; + + loop { + match typeck.expr_adjustments(prev) { + [] => (), + a => break a, + }; + + match iter.next().map(|(_, x)| x) { + Some(Node::Block(_)) => { + if let Some((_, Node::Expr(e))) = iter.next() { + prev = e; + } else { + // This shouldn't happen. Blocks are always contained in an expression. + break &[]; + } + }, + Some(Node::Expr(&Expr { + kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _), + .. + })) => { + if let Some(Node::Expr(e)) = map.find(id) { + prev = e; + iter = map.parent_iter(id); + continue; + } + // This shouldn't happen. The destination should definitely exist at this point. + break &[]; + }, + _ => break &[], + } + } +} + +// Checks whether the parent node is a suitable context for switching from a deref method to the +// deref operator. +fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { + let parent = match parent { + Some(Node::Expr(e)) => e, + _ => return true, + }; + match parent.kind { + // Leave deref calls in the middle of a method chain. + // e.g. x.deref().foo() + ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false, + + // Leave deref calls resulting in a called function + // e.g. (x.deref())() + ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false, + + // Makes an ugly suggestion + // e.g. *x.deref() => *&*x + ExprKind::Unary(UnOp::Deref, _) + // Postfix expressions would require parens + | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Err => false, + + ExprKind::Box(..) + | ExprKind::ConstBlock(..) + | ExprKind::Array(_) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Tup(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Lit(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::If(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Path(..) + | ExprKind::AddrOf(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Struct(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) => true, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { + match state { + State::DerefMethod { + ty_changed_count, + is_final_ufcs, + } => { + let mut app = Applicability::MachineApplicable; + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let ty = cx.typeck_results().expr_ty(expr); + let (_, ref_count) = peel_mid_ty_refs(ty); + let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { + // a deref call changing &T -> &U requires two deref operators the first time + // this occurs. One to remove the reference, a second to call the deref impl. + "*".repeat(ty_changed_count + 1) + } else { + "*".repeat(ty_changed_count) + }; + let addr_of_str = if ty_changed_count < ref_count { + // Check if a reborrow from &mut T -> &T is required. + if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + "&*" + } else { + "" + } + } else if data.target_mut == Mutability::Mut { + "&mut " + } else { + "&" + }; + + let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { + format!("({})", expr_str) + } else { + expr_str.into_owned() + }; + + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + data.span, + "explicit `deref` method call", + "try this", + format!("{}{}{}", addr_of_str, deref_str, expr_str), + app, + ); }, - _ => (), } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 04e151df8e8..bf5688aa95c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1241,7 +1241,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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); - store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box dereference::Dereferencing::default()); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box if_let_mutex::IfLetMutex); diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index ac1d51e1993..ea4cedc6754 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -129,7 +129,7 @@ impl LateLintPass<'_> for ManualMap { // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; - let scrutinee_str = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { format!("({})", scrutinee_str) @@ -160,7 +160,7 @@ impl LateLintPass<'_> for ManualMap { "|{}{}| {}", annotation, some_binding, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) }, } @@ -168,8 +168,8 @@ impl LateLintPass<'_> for ManualMap { // TODO: handle explicit reference annotations. format!( "|{}| {}", - snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app), - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. diff --git a/clippy_lints/src/redundant_deref.rs b/clippy_lints/src/redundant_deref.rs new file mode 100644 index 00000000000..029583720d2 --- /dev/null +++ b/clippy_lints/src/redundant_deref.rs @@ -0,0 +1,63 @@ +// use crate::utils::{get_parent_expr, snippet_with_applicability, span_lint_and_sugg}; +// use if_chain::if_chain; +// use rustc_errors::Applicability; +// use rustc_hir::{Expr, ExprKind, UnOp}; +// use rustc_lint::{LateContext, LateLintPass, LintContext}; +// use rustc_middle::lint::in_external_macro; +// use rustc_session::{declare_lint_pass, declare_tool_lint}; + +// declare_clippy_lint! { +// /// **What it does:** Checks for uses of the dereference operator which would be covered by +// /// auto-dereferencing. +// /// +// /// **Why is this bad?** This unnecessarily complicates the code. +// /// +// /// **Known problems:** None. +// /// +// /// **Example:** +// /// +// /// ```rust +// /// fn foo(_: &str) {} +// /// foo(&*String::new()) +// /// ``` +// /// Use instead: +// /// ```rust +// /// fn foo(_: &str) {} +// /// foo(&String::new()) +// /// ``` +// pub REDUNDANT_DEREF, +// style, +// "default lint description" +// } + +// declare_lint_pass!(RedundantDeref => [REDUNDANT_DEREF]); + +// impl LateLintPass<'_> for RedundantDeref { +// fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +// if_chain! { +// if let ExprKind::AddrOf(_, _, addr_expr) = expr.kind; +// if let ExprKind::Unary(UnOp::UnDeref, deref_expr) = addr_expr.kind; +// if !in_external_macro(cx.sess(), expr.span); +// if let Some(parent_expr) = get_parent_expr(cx, expr); +// if match parent_expr.kind { +// ExprKind::Call(func, _) => func.hir_id != expr.hir_id, +// ExprKind::MethodCall(..) => true, +// _ => false, +// }; +// if !cx.typeck_results().expr_ty(deref_expr).is_unsafe_ptr(); +// then { +// let mut app = Applicability::MachineApplicable; +// let sugg = format!("&{}", snippet_with_applicability(cx, deref_expr.span, "_", &mut app)); +// span_lint_and_sugg( +// cx, +// REDUNDANT_DEREF, +// expr.span, +// "redundant dereference", +// "remove the dereference", +// sugg, +// app, +// ); +// } +// } +// } +// } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3dad21d2028..2ac2298ff74 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -61,11 +61,11 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::Node; use rustc_hir::{ - def, Arm, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, - ItemKind, LangItem, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, - TyKind, Unsafety, + def, Arm, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, GenericParam, HirId, + Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, MatchSource, Node, Param, Pat, + PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, + Variant, Visibility, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -78,7 +78,7 @@ use rustc_session::Session; use rustc_span::hygiene::{self, ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -852,26 +852,31 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node /// would result in `box []`. If given the context of the address of expression, this function will /// correctly get a snippet of `vec![]`. +/// +/// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context( cx: &LateContext<'_>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, -) -> Cow<'a, str> { +) -> (Cow<'a, str>, bool) { let outer_span = hygiene::walk_chain(span, outer); - let span = if outer_span.ctxt() == outer { - outer_span + let (span, is_macro_call) = if outer_span.ctxt() == outer { + (outer_span, span.ctxt() != outer) } else { // The span is from a macro argument, and the outer context is the macro using the argument if *applicability != Applicability::Unspecified { *applicability = Applicability::MaybeIncorrect; } // TODO: get the argument span. - span + (span, false) }; - snippet_with_applicability(cx, span, default, applicability) + ( + snippet_with_applicability(cx, span, default, applicability), + is_macro_call, + ) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -1013,21 +1018,52 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, .join("\n") } +/// Gets the span of the node, if there is one. +pub fn get_node_span(node: Node<'_>) -> Option { + match node { + Node::Param(Param { span, .. }) + | Node::Item(Item { span, .. }) + | Node::ForeignItem(ForeignItem { span, .. }) + | Node::TraitItem(TraitItem { span, .. }) + | Node::ImplItem(ImplItem { span, .. }) + | Node::Variant(Variant { span, .. }) + | Node::Field(StructField { span, .. }) + | Node::Expr(Expr { span, .. }) + | Node::Stmt(Stmt { span, .. }) + | Node::PathSegment(PathSegment { + ident: Ident { span, .. }, + .. + }) + | Node::Ty(hir::Ty { span, .. }) + | Node::TraitRef(TraitRef { + path: Path { span, .. }, + .. + }) + | Node::Binding(Pat { span, .. }) + | Node::Pat(Pat { span, .. }) + | Node::Arm(Arm { span, .. }) + | Node::Block(Block { span, .. }) + | Node::Local(Local { span, .. }) + | Node::MacroDef(MacroDef { span, .. }) + | Node::Lifetime(Lifetime { span, .. }) + | Node::GenericParam(GenericParam { span, .. }) + | Node::Visibility(Visibility { span, .. }) + | Node::Crate(CrateItem { span, .. }) => Some(*span), + Node::Ctor(_) | Node::AnonConst(_) => None, + } +} + +/// Gets the parent node, if any. +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { + tcx.hir().parent_iter(id).next().map(|(_, node)| node) +} + /// Gets the parent expression, if any –- this is useful to constrain a lint. 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); - if hir_id == parent_id { - return None; - } - map.find(parent_id).and_then(|node| { - if let Node::Expr(parent) = node { - Some(parent) - } else { - None - } - }) + match get_parent_node(cx.tcx, e.hir_id) { + Some(Node::Expr(parent)) => Some(parent), + _ => None, + } } pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { diff --git a/tests/ui/dereference.fixed b/tests/ui/dereference.fixed deleted file mode 100644 index 459ca91b93b..00000000000 --- a/tests/ui/dereference.fixed +++ /dev/null @@ -1,93 +0,0 @@ -// run-rustfix - -#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] -#![warn(clippy::explicit_deref_methods)] - -use std::ops::{Deref, DerefMut}; - -fn concat(deref_str: &str) -> String { - format!("{}bar", deref_str) -} - -fn just_return(deref_str: &str) -> &str { - deref_str -} - -struct CustomVec(Vec); -impl Deref for CustomVec { - type Target = Vec; - - fn deref(&self) -> &Vec { - &self.0 - } -} - -fn main() { - let a: &mut String = &mut String::from("foo"); - - // these should require linting - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - // both derefs should get linted here - let b: String = format!("{}, {}", &*a, &*a); - - println!("{}", &*a); - - #[allow(clippy::match_single_binding)] - match &*a { - _ => (), - } - - let b: String = concat(&*a); - - let b = &*just_return(a); - - let b: String = concat(&*just_return(a)); - - let b: &str = &*a.deref(); - - let opt_a = Some(a.clone()); - let b = &*opt_a.unwrap(); - - // following should not require linting - - let cv = CustomVec(vec![0, 42]); - let c = cv.deref()[0]; - - let b: &str = &*a.deref(); - - let b: String = a.deref().clone(); - - let b: usize = a.deref_mut().len(); - - let b: &usize = &a.deref().len(); - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - macro_rules! expr_deref { - ($body:expr) => { - $body.deref() - }; - } - let b: &str = expr_deref!(a); - - // The struct does not implement Deref trait - #[derive(Copy, Clone)] - struct NoLint(u32); - impl NoLint { - pub fn deref(self) -> u32 { - self.0 - } - pub fn deref_mut(self) -> u32 { - self.0 - } - } - let no_lint = NoLint(42); - let b = no_lint.deref(); - let b = no_lint.deref_mut(); -} diff --git a/tests/ui/dereference.rs b/tests/ui/dereference.rs deleted file mode 100644 index 8dc5272e67f..00000000000 --- a/tests/ui/dereference.rs +++ /dev/null @@ -1,93 +0,0 @@ -// run-rustfix - -#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] -#![warn(clippy::explicit_deref_methods)] - -use std::ops::{Deref, DerefMut}; - -fn concat(deref_str: &str) -> String { - format!("{}bar", deref_str) -} - -fn just_return(deref_str: &str) -> &str { - deref_str -} - -struct CustomVec(Vec); -impl Deref for CustomVec { - type Target = Vec; - - fn deref(&self) -> &Vec { - &self.0 - } -} - -fn main() { - let a: &mut String = &mut String::from("foo"); - - // these should require linting - - let b: &str = a.deref(); - - let b: &mut str = a.deref_mut(); - - // both derefs should get linted here - let b: String = format!("{}, {}", a.deref(), a.deref()); - - println!("{}", a.deref()); - - #[allow(clippy::match_single_binding)] - match a.deref() { - _ => (), - } - - let b: String = concat(a.deref()); - - let b = just_return(a).deref(); - - let b: String = concat(just_return(a).deref()); - - let b: &str = a.deref().deref(); - - let opt_a = Some(a.clone()); - let b = opt_a.unwrap().deref(); - - // following should not require linting - - let cv = CustomVec(vec![0, 42]); - let c = cv.deref()[0]; - - let b: &str = &*a.deref(); - - let b: String = a.deref().clone(); - - let b: usize = a.deref_mut().len(); - - let b: &usize = &a.deref().len(); - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - macro_rules! expr_deref { - ($body:expr) => { - $body.deref() - }; - } - let b: &str = expr_deref!(a); - - // The struct does not implement Deref trait - #[derive(Copy, Clone)] - struct NoLint(u32); - impl NoLint { - pub fn deref(self) -> u32 { - self.0 - } - pub fn deref_mut(self) -> u32 { - self.0 - } - } - let no_lint = NoLint(42); - let b = no_lint.deref(); - let b = no_lint.deref_mut(); -} diff --git a/tests/ui/dereference.stderr b/tests/ui/dereference.stderr deleted file mode 100644 index d26b462a433..00000000000 --- a/tests/ui/dereference.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error: explicit deref method call - --> $DIR/dereference.rs:30:19 - | -LL | let b: &str = a.deref(); - | ^^^^^^^^^ help: try this: `&*a` - | - = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` - -error: explicit deref_mut method call - --> $DIR/dereference.rs:32:23 - | -LL | let b: &mut str = a.deref_mut(); - | ^^^^^^^^^^^^^ help: try this: `&mut *a` - -error: explicit deref method call - --> $DIR/dereference.rs:35:39 - | -LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:35:50 - | -LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:37:20 - | -LL | println!("{}", a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:40:11 - | -LL | match a.deref() { - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:44:28 - | -LL | let b: String = concat(a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:46:13 - | -LL | let b = just_return(a).deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` - -error: explicit deref method call - --> $DIR/dereference.rs:48:28 - | -LL | let b: String = concat(just_return(a).deref()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` - -error: explicit deref method call - --> $DIR/dereference.rs:50:19 - | -LL | let b: &str = a.deref().deref(); - | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()` - -error: explicit deref method call - --> $DIR/dereference.rs:53:13 - | -LL | let b = opt_a.unwrap().deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` - -error: aborting due to 11 previous errors - diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed new file mode 100644 index 00000000000..8155fdf9950 --- /dev/null +++ b/tests/ui/explicit_deref_methods.fixed @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec); +impl Deref for CustomVec { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = &*a; + + let b: &mut str = &mut **a; + + // both derefs should get linted here + let b: String = format!("{}, {}", &*a, &*a); + + println!("{}", &*a); + + #[allow(clippy::match_single_binding)] + match &*a { + _ => (), + } + + let b: String = concat(&*a); + + let b = just_return(a); + + let b: String = concat(just_return(a)); + + let b: &str = &**a; + + let opt_a = Some(a.clone()); + let b = &*opt_a.unwrap(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs new file mode 100644 index 00000000000..8dc5272e67f --- /dev/null +++ b/tests/ui/explicit_deref_methods.rs @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec); +impl Deref for CustomVec { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = a.deref(); + + let b: &mut str = a.deref_mut(); + + // both derefs should get linted here + let b: String = format!("{}, {}", a.deref(), a.deref()); + + println!("{}", a.deref()); + + #[allow(clippy::match_single_binding)] + match a.deref() { + _ => (), + } + + let b: String = concat(a.deref()); + + let b = just_return(a).deref(); + + let b: String = concat(just_return(a).deref()); + + let b: &str = a.deref().deref(); + + let opt_a = Some(a.clone()); + let b = opt_a.unwrap().deref(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr new file mode 100644 index 00000000000..35db6ef0905 --- /dev/null +++ b/tests/ui/explicit_deref_methods.stderr @@ -0,0 +1,70 @@ +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:30:19 + | +LL | let b: &str = a.deref(); + | ^^^^^^^^^ help: try this: `&*a` + | + = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:32:23 + | +LL | let b: &mut str = a.deref_mut(); + | ^^^^^^^^^^^^^ help: try this: `&mut **a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:39 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:50 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:37:20 + | +LL | println!("{}", a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:40:11 + | +LL | match a.deref() { + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:44:28 + | +LL | let b: String = concat(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:46:13 + | +LL | let b = just_return(a).deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:48:28 + | +LL | let b: String = concat(just_return(a).deref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:50:19 + | +LL | let b: &str = a.deref().deref(); + | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:53:13 + | +LL | let b = opt_a.unwrap().deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From 1666e43cc0b1f2df9b8b4a3d8bb3ffb5ecdcf27e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 9 Mar 2021 10:37:15 -0500 Subject: Remove unneeded code. --- clippy_lints/src/dereference.rs | 9 +----- clippy_lints/src/redundant_deref.rs | 63 ------------------------------------- 2 files changed, 1 insertion(+), 71 deletions(-) delete mode 100644 clippy_lints/src/redundant_deref.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a33634ca34e..6185cf50b35 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,6 +1,4 @@ -use crate::utils::{ - get_node_span, get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg, -}; +use crate::utils::{get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg}; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; @@ -102,11 +100,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let parent = get_parent_node(cx.tcx, expr.hir_id); - // This is an odd case. The expression is a macro argument, but the top level - // address of expression is inserted by the compiler. - if matches!(kind, RefOp::AddrOf) && parent.and_then(get_node_span).map_or(false, in_macro) { - return; - } let expr_adjustments = find_adjustments(cx.tcx, typeck, expr); let expr_ty = typeck.expr_ty(expr); diff --git a/clippy_lints/src/redundant_deref.rs b/clippy_lints/src/redundant_deref.rs deleted file mode 100644 index 029583720d2..00000000000 --- a/clippy_lints/src/redundant_deref.rs +++ /dev/null @@ -1,63 +0,0 @@ -// use crate::utils::{get_parent_expr, snippet_with_applicability, span_lint_and_sugg}; -// use if_chain::if_chain; -// use rustc_errors::Applicability; -// use rustc_hir::{Expr, ExprKind, UnOp}; -// use rustc_lint::{LateContext, LateLintPass, LintContext}; -// use rustc_middle::lint::in_external_macro; -// use rustc_session::{declare_lint_pass, declare_tool_lint}; - -// declare_clippy_lint! { -// /// **What it does:** Checks for uses of the dereference operator which would be covered by -// /// auto-dereferencing. -// /// -// /// **Why is this bad?** This unnecessarily complicates the code. -// /// -// /// **Known problems:** None. -// /// -// /// **Example:** -// /// -// /// ```rust -// /// fn foo(_: &str) {} -// /// foo(&*String::new()) -// /// ``` -// /// Use instead: -// /// ```rust -// /// fn foo(_: &str) {} -// /// foo(&String::new()) -// /// ``` -// pub REDUNDANT_DEREF, -// style, -// "default lint description" -// } - -// declare_lint_pass!(RedundantDeref => [REDUNDANT_DEREF]); - -// impl LateLintPass<'_> for RedundantDeref { -// fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { -// if_chain! { -// if let ExprKind::AddrOf(_, _, addr_expr) = expr.kind; -// if let ExprKind::Unary(UnOp::UnDeref, deref_expr) = addr_expr.kind; -// if !in_external_macro(cx.sess(), expr.span); -// if let Some(parent_expr) = get_parent_expr(cx, expr); -// if match parent_expr.kind { -// ExprKind::Call(func, _) => func.hir_id != expr.hir_id, -// ExprKind::MethodCall(..) => true, -// _ => false, -// }; -// if !cx.typeck_results().expr_ty(deref_expr).is_unsafe_ptr(); -// then { -// let mut app = Applicability::MachineApplicable; -// let sugg = format!("&{}", snippet_with_applicability(cx, deref_expr.span, "_", &mut app)); -// span_lint_and_sugg( -// cx, -// REDUNDANT_DEREF, -// expr.span, -// "redundant dereference", -// "remove the dereference", -// sugg, -// app, -// ); -// } -// } -// } -// } -- cgit 1.4.1-3-g733a5 From 704f7a8e5025cdf5765493b4369290b65230c5d1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 12 Mar 2021 20:13:45 -0500 Subject: Keep track of whether `deref` or `deref_mut` was called Remove more unnecessary code --- clippy_lints/src/dereference.rs | 75 ++++++++-------------------------- tests/ui/explicit_deref_methods.stderr | 2 +- 2 files changed, 17 insertions(+), 60 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 6185cf50b35..a1d0110929c 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,9 +1,9 @@ use crate::utils::{get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg}; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, adjustment::Adjustment, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; @@ -66,7 +66,7 @@ enum State { // A reference operation considered by this lint pass enum RefOp { - Method, + Method(Mutability), Deref, AddrOf, } @@ -100,18 +100,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let parent = get_parent_node(cx.tcx, expr.hir_id); - - let expr_adjustments = find_adjustments(cx.tcx, typeck, expr); let expr_ty = typeck.expr_ty(expr); - let target_mut = - if let ty::Ref(_, _, mutability) = *expr_adjustments.last().map_or(expr_ty, |a| a.target).kind() { - mutability - } else { - Mutability::Not - }; match kind { - RefOp::Method + RefOp::Method(target_mut) if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && is_linted_explicit_deref_position(parent, expr.hir_id) => { @@ -133,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { _ => (), } }, - (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method) => { + (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => { self.state = Some(( State::DerefMethod { ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { @@ -173,9 +165,13 @@ fn try_parse_ref_op( ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)), _ => return None, }; - (tcx.is_diagnostic_item(sym::deref_method, def_id) - || tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()?) - .then(|| (RefOp::Method, arg)) + if tcx.is_diagnostic_item(sym::deref_method, def_id) { + Some((RefOp::Method(Mutability::Not), arg)) + } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { + Some((RefOp::Method(Mutability::Mut), arg)) + } else { + None + } } // Checks whether the type for a deref call actually changed the type, not just the mutability of @@ -191,48 +187,6 @@ fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -// Adjustments are sometimes made in the parent block rather than the expression itself. -fn find_adjustments( - tcx: TyCtxt<'tcx>, - typeck: &'tcx TypeckResults<'_>, - expr: &'tcx Expr<'_>, -) -> &'tcx [Adjustment<'tcx>] { - let map = tcx.hir(); - let mut iter = map.parent_iter(expr.hir_id); - let mut prev = expr; - - loop { - match typeck.expr_adjustments(prev) { - [] => (), - a => break a, - }; - - match iter.next().map(|(_, x)| x) { - Some(Node::Block(_)) => { - if let Some((_, Node::Expr(e))) = iter.next() { - prev = e; - } else { - // This shouldn't happen. Blocks are always contained in an expression. - break &[]; - } - }, - Some(Node::Expr(&Expr { - kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _), - .. - })) => { - if let Some(Node::Expr(e)) = map.find(id) { - prev = e; - iter = map.parent_iter(id); - continue; - } - // This shouldn't happen. The destination should definitely exist at this point. - break &[]; - }, - _ => break &[], - } - } -} - // Checks whether the parent node is a suitable context for switching from a deref method to the // deref operator. fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { @@ -331,7 +285,10 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat cx, EXPLICIT_DEREF_METHODS, data.span, - "explicit `deref` method call", + match data.target_mut { + Mutability::Not => "explicit `deref` method call", + Mutability::Mut => "explicit `deref_mut` method call", + }, "try this", format!("{}{}{}", addr_of_str, deref_str, expr_str), app, diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 35db6ef0905..335c0e4bb9d 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -6,7 +6,7 @@ LL | let b: &str = a.deref(); | = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` -error: explicit `deref` method call +error: explicit `deref_mut` method call --> $DIR/explicit_deref_methods.rs:32:23 | LL | let b: &mut str = a.deref_mut(); -- cgit 1.4.1-3-g733a5 From 2713ad43425bef040e3c7d55618d18035fe68c1a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 13 Mar 2021 08:27:42 -0500 Subject: Properly lint macro arguments for `explicit_deref_methods` --- clippy_lints/src/dereference.rs | 6 +++--- tests/ui/explicit_deref_methods.fixed | 2 ++ tests/ui/explicit_deref_methods.rs | 2 ++ tests/ui/explicit_deref_methods.stderr | 8 +++++++- 4 files changed, 14 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a1d0110929c..40ed6d8d154 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match kind { RefOp::Method(target_mut) if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) - && is_linted_explicit_deref_position(parent, expr.hir_id) => + && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) => { self.state = Some(( State::DerefMethod { @@ -189,9 +189,9 @@ fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { // Checks whether the parent node is a suitable context for switching from a deref method to the // deref operator. -fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { +fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId, child_span: Span) -> bool { let parent = match parent { - Some(Node::Expr(e)) => e, + Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e, _ => return true, }; match parent.kind { diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 8155fdf9950..51d0468e47c 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -76,6 +76,8 @@ fn main() { } let b: &str = expr_deref!(a); + let b: &str = expr_deref!(&*a); + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 8dc5272e67f..680664bd4f6 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -76,6 +76,8 @@ fn main() { } let b: &str = expr_deref!(a); + let b: &str = expr_deref!(a.deref()); + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 335c0e4bb9d..8035d77d18d 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -66,5 +66,11 @@ error: explicit `deref` method call LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` -error: aborting due to 11 previous errors +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:79:31 + | +LL | let b: &str = expr_deref!(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 1054eb0c853aefec9787b57e103d3a1432169263 Mon Sep 17 00:00:00 2001 From: iobtl Date: Sun, 14 Mar 2021 08:09:08 +0800 Subject: use lint_unnecessary_cast for literals, suggest `_` if not present --- clippy_lints/src/casts/unnecessary_cast.rs | 9 +++++++++ tests/ui/unnecessary_cast.stderr | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 171cac091c2..c43bbf32949 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -44,6 +44,15 @@ pub(super) fn check( lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) + | LitKind::Float(_, LitFloatType::Suffixed(_)) + if cast_from.kind() == cast_to.kind() => + { + if let Some(src) = snippet_opt(cx, lit.span) { + let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap(); + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + } + }, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint_and_sugg( diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 87261cd8333..70aa448af68 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,16 +1,16 @@ -error: casting to the same type is unnecessary (`i32` -> `i32`) +error: casting integer literal to `i32` is unnecessary --> $DIR/unnecessary_cast.rs:6:5 | LL | 1i32 as i32; - | ^^^^^^^^^^^ help: try: `1i32` + | ^^^^^^^^^^^ help: try: `1_i32` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` -error: casting to the same type is unnecessary (`f32` -> `f32`) +error: casting float literal to `f32` is unnecessary --> $DIR/unnecessary_cast.rs:7:5 | LL | 1f32 as f32; - | ^^^^^^^^^^^ help: try: `1f32` + | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) --> $DIR/unnecessary_cast.rs:8:5 -- cgit 1.4.1-3-g733a5 From ecf0c76c369a80c1a2de61e25c94b7df5dc7d164 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 25 Mar 2020 21:13:24 -0400 Subject: Fix suspicious_map false positives --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/suspicious_map.rs | 45 ++++++++++++++++----- clippy_utils/src/lib.rs | 64 ++++++++++++++++++++++++++++-- tests/ui/suspicious_map.rs | 27 +++++++++++++ tests/ui/suspicious_map.stderr | 10 ++++- 5 files changed, 132 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7fd14c4f9b1..45e906cf468 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1739,7 +1739,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { unnecessary_filter_map::check(cx, expr, arg_lists[0]); filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); }, - ["count", "map"] => suspicious_map::check(cx, expr), + ["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr), ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..]) diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index e135a826dc4..0ffa71de30c 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,16 +1,41 @@ -use crate::utils::span_lint_and_help; +use crate::utils::usage::mutated_variables; +use crate::utils::{expr_or_init, is_trait_method, span_lint_and_help}; +use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::SUSPICIOUS_MAP; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - span_lint_and_help( - cx, - SUSPICIOUS_MAP, - expr.span, - "this call to `map()` won't have an effect on the call to `count()`", - None, - "make sure you did not confuse `map` with `filter` or `for_each`", - ); +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + map_args: &[hir::Expr<'_>], + count_args: &[hir::Expr<'_>], +) { + if_chain! { + if let [count_recv] = count_args; + if let [_, map_arg] = map_args; + if is_trait_method(cx, count_recv, sym::Iterator); + let closure = expr_or_init(cx, map_arg); + if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id); + let closure_body = cx.tcx.hir().body(body_id); + if !cx.typeck_results().expr_ty(&closure_body.value).is_unit(); + then { + if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) { + // A variable is used mutably inside of the closure. Suppress the lint. + if !map_mutated_vars.is_empty() { + return; + } + } + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + expr.span, + "this call to `map()` won't have an effect on the call to `count()`", + None, + "make sure you did not confuse `map` with `filter` or `for_each`", + ); + } + } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0ddc915862c..98cdeae739f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -62,10 +62,10 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{ - def, Arm, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, GenericParam, HirId, - Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, MatchSource, Node, Param, Pat, - PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, - Variant, Visibility, + def, Arm, BindingAnnotation, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, + GenericParam, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, + MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, + TraitRef, TyKind, Unsafety, Variant, Visibility, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -138,6 +138,62 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool { rhs.ctxt() != lhs.ctxt() } +/// If the given expression is a local binding, find the initializer expression. +/// If that initializer expression is another local binding, find its initializer again. +/// This process repeats as long as possible (but usually no more than once). Initializer +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`] +/// instead. +/// +/// Examples: +/// ```ignore +/// let abc = 1; +/// // ^ output +/// let def = abc; +/// dbg!(def) +/// // ^^^ input +/// +/// // or... +/// let abc = 1; +/// let def = abc + 2; +/// // ^^^^^^^ output +/// dbg!(def) +/// // ^^^ input +/// ``` +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { + while let Some(init) = path_to_local(expr) + .and_then(|id| find_binding_init(cx, id)) + .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) + { + expr = init; + } + expr +} + +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable. +/// By only considering immutable bindings, we guarantee that the returned expression represents the +/// value of the binding wherever it is referenced. +/// +/// Example: +/// ```ignore +/// let abc = 1; +/// // ^ output +/// dbg!(abc) +/// // ^^^ input +/// ``` +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { + let hir = cx.tcx.hir(); + if_chain! { + if let Some(Node::Binding(pat)) = hir.find(hir_id); + if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..)); + let parent = hir.get_parent_node(hir_id); + if let Some(Node::Local(local)) = hir.find(parent); + then { + return local.init; + } + } + None +} + /// Returns `true` if the given `NodeId` is inside a constant context /// /// # Example diff --git a/tests/ui/suspicious_map.rs b/tests/ui/suspicious_map.rs index d838d8fde21..3a2a10cf09e 100644 --- a/tests/ui/suspicious_map.rs +++ b/tests/ui/suspicious_map.rs @@ -2,4 +2,31 @@ fn main() { let _ = (0..3).map(|x| x + 2).count(); + + let f = |x| x + 1; + let _ = (0..3).map(f).count(); +} + +fn negative() { + // closure with side effects + let mut sum = 0; + let _ = (0..3).map(|x| sum += x).count(); + + // closure variable with side effects + let ext_closure = |x| sum += x; + let _ = (0..3).map(ext_closure).count(); + + // closure that returns unit + let _ = (0..3) + .map(|x| { + // do nothing + }) + .count(); + + // external function + let _ = (0..3).map(do_something).count(); +} + +fn do_something(t: T) -> String { + unimplemented!() } diff --git a/tests/ui/suspicious_map.stderr b/tests/ui/suspicious_map.stderr index e1b4ba40376..8c3f36584a5 100644 --- a/tests/ui/suspicious_map.stderr +++ b/tests/ui/suspicious_map.stderr @@ -7,5 +7,13 @@ LL | let _ = (0..3).map(|x| x + 2).count(); = note: `-D clippy::suspicious-map` implied by `-D warnings` = help: make sure you did not confuse `map` with `filter` or `for_each` -error: aborting due to previous error +error: this call to `map()` won't have an effect on the call to `count()` + --> $DIR/suspicious_map.rs:7:13 + | +LL | let _ = (0..3).map(f).count(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: make sure you did not confuse `map` with `filter` or `for_each` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From eb7f8d6089e2dba4006f0452ab25262735bbbf61 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 13 Mar 2021 17:01:03 -0600 Subject: Move some utils to ty_utils --- clippy_lints/src/assign_ops.rs | 5 +- clippy_lints/src/async_yields_async.rs | 3 +- clippy_lints/src/blocks_in_if_conditions.rs | 4 +- clippy_lints/src/booleans.rs | 4 +- clippy_lints/src/bytecount.rs | 3 +- clippy_lints/src/casts/cast_lossless.rs | 3 +- clippy_lints/src/casts/cast_possible_truncation.rs | 3 +- clippy_lints/src/casts/cast_possible_wrap.rs | 3 +- clippy_lints/src/casts/cast_precision_loss.rs | 3 +- clippy_lints/src/cognitive_complexity.rs | 3 +- clippy_lints/src/comparison_chain.rs | 5 +- clippy_lints/src/copy_iterator.rs | 3 +- clippy_lints/src/dereference.rs | 3 +- clippy_lints/src/derive.rs | 5 +- clippy_lints/src/doc.rs | 4 +- clippy_lints/src/drop_forget_ref.rs | 3 +- clippy_lints/src/duration_subsec.rs | 3 +- clippy_lints/src/entry.rs | 3 +- clippy_lints/src/eq_op.rs | 5 +- clippy_lints/src/escape.rs | 3 +- clippy_lints/src/eta_reduction.rs | 10 +- clippy_lints/src/fallible_impl_from.rs | 3 +- clippy_lints/src/format.rs | 4 +- clippy_lints/src/from_str_radix_10.rs | 2 +- clippy_lints/src/functions.rs | 7 +- clippy_lints/src/get_last_with_len.rs | 3 +- clippy_lints/src/if_let_mutex.rs | 3 +- clippy_lints/src/if_let_some_result.rs | 3 +- clippy_lints/src/infinite_iter.rs | 3 +- clippy_lints/src/inherent_to_string.rs | 6 +- clippy_lints/src/let_underscore.rs | 3 +- clippy_lints/src/loops/explicit_iter_loop.rs | 4 +- clippy_lints/src/loops/for_kv_map.rs | 3 +- clippy_lints/src/loops/for_loops_over_fallibles.rs | 3 +- clippy_lints/src/loops/manual_memcpy.rs | 5 +- clippy_lints/src/loops/needless_collect.rs | 6 +- clippy_lints/src/loops/needless_range_loop.rs | 5 +- clippy_lints/src/loops/same_item_push.rs | 3 +- clippy_lints/src/loops/utils.rs | 5 +- clippy_lints/src/loops/while_let_on_iterator.rs | 6 +- clippy_lints/src/manual_map.rs | 6 +- clippy_lints/src/manual_ok_or.rs | 4 +- clippy_lints/src/manual_unwrap_or.rs | 5 +- clippy_lints/src/map_clone.rs | 6 +- clippy_lints/src/map_identity.rs | 6 +- clippy_lints/src/map_unit_fn.rs | 3 +- clippy_lints/src/match_on_vec_items.rs | 3 +- clippy_lints/src/matches.rs | 10 +- clippy_lints/src/mem_discriminant.rs | 4 +- clippy_lints/src/methods/bind_instead_of_map.rs | 3 +- clippy_lints/src/methods/bytes_nth.rs | 3 +- clippy_lints/src/methods/clone_on_copy.rs | 3 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 3 +- clippy_lints/src/methods/expect_fun_call.rs | 3 +- clippy_lints/src/methods/expect_used.rs | 3 +- clippy_lints/src/methods/filetype_is_file.rs | 3 +- .../src/methods/from_iter_instead_of_collect.rs | 3 +- clippy_lints/src/methods/get_unwrap.rs | 5 +- clippy_lints/src/methods/inefficient_to_string.rs | 5 +- clippy_lints/src/methods/into_iter_on_ref.rs | 3 +- clippy_lints/src/methods/iter_cloned_collect.rs | 3 +- clippy_lints/src/methods/iter_count.rs | 4 +- clippy_lints/src/methods/iter_next_slice.rs | 3 +- clippy_lints/src/methods/iter_nth.rs | 3 +- .../src/methods/map_collect_result_unit.rs | 3 +- clippy_lints/src/methods/map_flatten.rs | 3 +- clippy_lints/src/methods/map_unwrap_or.rs | 3 +- clippy_lints/src/methods/mod.rs | 7 +- clippy_lints/src/methods/ok_expect.rs | 3 +- clippy_lints/src/methods/option_as_ref_deref.rs | 6 +- clippy_lints/src/methods/option_map_or_none.rs | 3 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 3 +- clippy_lints/src/methods/or_fun_call.rs | 5 +- clippy_lints/src/methods/search_is_some.rs | 4 +- clippy_lints/src/methods/string_extend_chars.rs | 3 +- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 3 +- clippy_lints/src/methods/unwrap_used.rs | 3 +- clippy_lints/src/methods/useless_asref.rs | 5 +- clippy_lints/src/misc.rs | 7 +- clippy_lints/src/missing_const_for_fn.rs | 5 +- clippy_lints/src/mut_mutex_lock.rs | 3 +- clippy_lints/src/mutex_atomic.rs | 3 +- clippy_lints/src/needless_pass_by_value.rs | 6 +- clippy_lints/src/needless_question_mark.rs | 9 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 5 +- clippy_lints/src/no_effect.rs | 3 +- clippy_lints/src/open_options.rs | 3 +- clippy_lints/src/option_if_let_else.rs | 3 +- clippy_lints/src/panic_in_result_fn.rs | 3 +- clippy_lints/src/pass_by_ref_or_value.rs | 3 +- clippy_lints/src/path_buf_push_overwrite.rs | 3 +- clippy_lints/src/ptr.rs | 6 +- clippy_lints/src/question_mark.rs | 6 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 3 +- clippy_lints/src/repeat_once.rs | 3 +- clippy_lints/src/strings.rs | 15 +- clippy_lints/src/swap.rs | 5 +- clippy_lints/src/transmute/utils.rs | 3 +- clippy_lints/src/try_err.rs | 5 +- clippy_lints/src/types/mod.rs | 7 +- clippy_lints/src/undropped_manually_drops.rs | 3 +- clippy_lints/src/unnecessary_sort_by.rs | 3 +- clippy_lints/src/unwrap.rs | 5 +- clippy_lints/src/unwrap_in_result.rs | 8 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/internal_lints.rs | 5 +- clippy_lints/src/vec.rs | 3 +- clippy_lints/src/vec_init_then_push.rs | 5 +- clippy_lints/src/verbose_file_reads.rs | 3 +- clippy_lints/src/zero_sized_map_values.rs | 3 +- clippy_utils/src/eager_or_lazy.rs | 3 +- clippy_utils/src/lib.rs | 323 ++------------------- clippy_utils/src/ty.rs | 296 +++++++++++++++++++ 114 files changed, 578 insertions(+), 509 deletions(-) create mode 100644 clippy_utils/src/ty.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index e13f62d0428..ae300324158 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,7 +1,6 @@ -use crate::utils::{ - eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, -}; +use crate::utils::{eq_expr_value, get_trait_def_id, snippet_opt, span_lint_and_then, trait_ref_of_method}; use crate::utils::{higher, sugg}; +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 869a5c28d05..1e185e97df6 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,4 +1,5 @@ -use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use crate::utils::{snippet, span_lint_and_then}; +use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 59bddb8473b..222bb39f89c 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,7 +1,7 @@ use crate::utils::{ - differing_macro_contexts, get_parent_expr, implements_trait, snippet_block_with_applicability, span_lint, - span_lint_and_sugg, + differing_macro_contexts, get_parent_expr, snippet_block_with_applicability, span_lint, span_lint_and_sugg, }; +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 0713303ec4b..affc7d2c93f 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ use crate::utils::{ - 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, + eq_expr_value, get_trait_def_id, in_macro, paths, snippet_opt, span_lint_and_sugg, span_lint_and_then, }; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index eb5dc7ceecd..67cca8dcadb 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,6 +1,7 @@ use crate::utils::{ - contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg, + contains_name, get_pat_name, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg, }; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 478832a5164..902fb423ff1 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,9 +1,10 @@ +use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg}; +use crate::utils::{in_constant, snippet_opt, span_lint_and_sugg}; use super::{utils, CAST_LOSSLESS}; diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 33b06b8fe7c..522ef5348be 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,8 +1,9 @@ +use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::{is_isize_or_usize, span_lint}; +use crate::utils::span_lint; use super::{utils, CAST_POSSIBLE_TRUNCATION}; diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 56d301ed3e1..931252415ad 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,8 +1,9 @@ +use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use crate::utils::{is_isize_or_usize, span_lint}; +use crate::utils::span_lint; use super::{utils, CAST_POSSIBLE_WRAP}; diff --git a/clippy_lints/src/casts/cast_precision_loss.rs b/clippy_lints/src/casts/cast_precision_loss.rs index a1c3900ce1f..b6905c21c78 100644 --- a/clippy_lints/src/casts/cast_precision_loss.rs +++ b/clippy_lints/src/casts/cast_precision_loss.rs @@ -1,8 +1,9 @@ +use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::{is_isize_or_usize, span_lint}; +use crate::utils::span_lint; use super::{utils, CAST_PRECISION_LOSS}; diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 658d445dfec..3d842a46813 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,6 @@ //! calculate cognitive complexity and warn about overly complex functions +use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; @@ -9,7 +10,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::{sym, BytePos}; -use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack}; +use crate::utils::{snippet_opt, span_lint_and_help, LimitStack}; declare_clippy_lint! { /// **What it does:** Checks for methods with high cognitive complexity. diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index e309db25995..9843033a2e0 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq, -}; +use crate::utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq}; +use clippy_utils::ty::implements_trait; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 0502f725efb..434d8958da5 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_copy, span_lint_and_note}; +use crate::utils::span_lint_and_note; +use clippy_utils::ty::is_copy; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 40ed6d8d154..8842ea9628d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg}; +use crate::utils::{get_parent_node, in_macro, is_allowed, snippet_with_context, span_lint_and_sugg}; +use clippy_utils::ty::peel_mid_ty_refs; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 6d3094ed6bf..460fe385272 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,8 +1,9 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, span_lint_and_help, - span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_allowed, is_automatically_derived, match_def_path, span_lint_and_help, span_lint_and_note, + span_lint_and_then, }; +use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 90b02d52f8a..4f3c573691e 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,7 +1,7 @@ use crate::utils::{ - implements_trait, is_entrypoint_fn, is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, - return_ty, span_lint, span_lint_and_note, + is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty, span_lint, span_lint_and_note, }; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind}; diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 2aea00d883c..dd145ba6867 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_copy, match_def_path, paths, span_lint_and_note}; +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index c0529a34cc4..c97392e3385 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -7,7 +8,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}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 55575969927..68363e53f0e 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,6 +1,7 @@ use crate::utils::SpanlessEq; -use crate::utils::{get_item_name, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt}; +use crate::utils::{get_item_name, paths, snippet, snippet_opt}; use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 6308f6e2e7e..fa19f16074d 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,7 +1,8 @@ use crate::utils::{ - ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, - multispan_sugg, snippet, span_lint, span_lint_and_then, + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, multispan_sugg, snippet, + span_lint, span_lint_and_then, }; +use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index f8ef2a464d5..a7258eea0ad 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::contains_ty; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node}; use rustc_infer::infer::TyCtxtInferExt; @@ -10,7 +11,7 @@ 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::{contains_ty, span_lint}; +use crate::utils::span_lint; #[derive(Copy, Clone)] pub struct BoxedLocal { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index c461732fd36..d93c32c179f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,3 +1,6 @@ +use clippy_utils::higher; +use clippy_utils::higher::VecArgs; +use clippy_utils::ty::{implements_trait, type_is_unsafe_function}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath}; @@ -6,12 +9,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{ - implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then, - type_is_unsafe_function, -}; -use clippy_utils::higher; -use clippy_utils::higher::VecArgs; +use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for closures which just call another function where diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index f466dddc13c..cc4e570956b 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then}; +use crate::utils::{is_expn_of, match_panic_def_id, method_chain_args, span_lint_and_then}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index fd6bf19db94..b6209f815bc 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,8 +1,8 @@ use crate::utils::paths; use crate::utils::{ - is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, - span_lint_and_then, + is_expn_of, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, span_lint_and_then, }; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 0933f983014..b92c8ccfb1b 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; @@ -6,7 +7,6 @@ use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; -use crate::utils::is_type_diagnostic_item; use crate::utils::span_lint_and_sugg; use crate::utils::sugg::Sugg; diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index c474db06fe3..349cd39ffb6 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, is_type_diagnostic_item, iter_input_pats, - match_def_path, must_use_attr, path_to_local, 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_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, + path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, + trait_ref_of_method, }; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index cdd8a42e7cd..478008ea48c 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -1,6 +1,7 @@ //! lint on using `x.get(x.len() - 1)` instead of `x.last()` -use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 58511c6d57c..f53d1b1cf3b 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help, SpanlessEq}; +use crate::utils::{span_lint_and_help, SpanlessEq}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, ExprKind, MatchSource}; diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 1194bd7e55e..0cd44a1c3a0 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 7040ac3191f..6bce205ec3a 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,8 +1,9 @@ +use clippy_utils::ty::{implements_trait, match_type}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint}; +use crate::utils::{get_trait_def_id, higher, match_qpath, paths, span_lint}; declare_clippy_lint! { /// **What it does:** Checks for iteration that is guaranteed to be infinite. diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index c1f3e1d9d68..9207413cd69 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,13 +1,11 @@ +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::{ - get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, - trait_ref_of_method, -}; +use crate::utils::{get_trait_def_id, paths, return_ty, span_lint_and_help, trait_ref_of_method}; declare_clippy_lint! { /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 7e96dfcc7da..2da7137b48b 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::{is_must_use_ty, match_type}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -5,7 +6,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; +use crate::utils::{is_must_use_func_call, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for `let _ = ` diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 9683e59a396..b70585015ca 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,12 +1,12 @@ use super::EXPLICIT_ITER_LOOP; -use crate::utils::{match_trait_method, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TyS}; use rustc_span::sym; -use crate::utils::{is_type_diagnostic_item, match_type, paths}; +use crate::utils::{match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg}; pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { let should_lint = match method_name { diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 6ee9b95a3b6..aef21886803 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,6 +1,7 @@ use super::FOR_KV_MAP; use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{is_type_diagnostic_item, match_type, multispan_sugg, paths, snippet, span_lint_and_then, sugg}; +use crate::utils::{multispan_sugg, paths, snippet, span_lint_and_then, sugg}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs index db22d90a304..063078adcd1 100644 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ b/clippy_lints/src/loops/for_loops_over_fallibles.rs @@ -1,5 +1,6 @@ use super::FOR_LOOPS_OVER_FALLIBLES; -use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_help}; +use crate::utils::{snippet, span_lint_and_help}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{Expr, Pat}; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index fad96c2d5c0..362124c082a 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,8 +1,7 @@ use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use crate::utils::sugg::Sugg; -use crate::utils::{ - get_enclosing_block, higher, is_type_diagnostic_item, path_to_local, snippet, span_lint_and_sugg, sugg, -}; +use crate::utils::{get_enclosing_block, higher, path_to_local, snippet, span_lint_and_sugg, sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index f8432abfa8a..f4d3b9eb272 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,9 +1,7 @@ use super::NEEDLESS_COLLECT; use crate::utils::sugg::Sugg; -use crate::utils::{ - is_trait_method, is_type_diagnostic_item, match_type, path_to_local_id, paths, snippet, span_lint_and_sugg, - span_lint_and_then, -}; +use crate::utils::{is_trait_method, path_to_local_id, paths, snippet, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 5f02e4b9d87..6d498149166 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -1,9 +1,10 @@ use super::NEEDLESS_RANGE_LOOP; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ - contains_name, has_iter_method, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id, - paths, snippet, span_lint_and_then, sugg, SpanlessEq, + contains_name, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id, paths, snippet, + span_lint_and_then, sugg, SpanlessEq, }; +use clippy_utils::ty::has_iter_method; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index f3585830e4a..f891f7b8a1f 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,5 +1,6 @@ use super::SAME_ITEM_PUSH; -use crate::utils::{implements_trait, is_type_diagnostic_item, snippet_with_macro_callsite, span_lint_and_help}; +use crate::utils::{snippet_with_macro_callsite, span_lint_and_help}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index e62b2ab16d1..b85676570b4 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - get_parent_expr, has_iter_method, implements_trait, is_integer_const, path_to_local, path_to_local_id, sugg, -}; +use crate::utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; +use clippy_utils::ty::{has_iter_method, implements_trait}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index f3b8d0da9cd..619fd1554f0 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,16 +2,16 @@ use super::utils::{LoopNestVisitor, Nesting}; use super::WHILE_LET_ON_ITERATOR; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_enclosing_block, implements_trait, is_refutable, is_trait_method, last_path_segment, path_to_local, - path_to_local_id, snippet_with_applicability, span_lint_and_sugg, + get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, + snippet_with_applicability, span_lint_and_sugg, }; +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; - use rustc_span::symbol::sym; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index ea4cedc6754..7c89359f2d9 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,11 +2,11 @@ use crate::{ map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF, utils::{ - can_partially_move_ty, is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, - peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, snippet_with_context, - span_lint_and_sugg, + is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs, snippet_with_applicability, + snippet_with_context, span_lint_and_sugg, }, }; +use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index efb05b8ffdf..b08829a8bbf 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,7 +1,7 @@ use crate::utils::{ - indent_of, is_type_diagnostic_item, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt, - span_lint_and_sugg, + indent_of, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt, span_lint_and_sugg, }; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind}; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index b452225b5db..3a1bde9540d 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,6 +1,7 @@ use crate::consts::constant_simple; use crate::utils; use crate::utils::{path_to_local_id, sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; @@ -96,9 +97,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) { + if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) { Some(Case::Option) - } else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) { + } else if is_type_diagnostic_item(cx, ty, sym::result_type) { Some(Case::Result) } else { None diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 61cf768bcab..73d924cd51e 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,6 +1,6 @@ -use crate::utils::{ - is_copy, is_trait_method, is_type_diagnostic_item, remove_blocks, snippet_with_applicability, span_lint_and_sugg, -}; +use crate::utils::is_trait_method; +use crate::utils::{remove_blocks, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 7202a29edd9..79570367ed9 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,7 +1,5 @@ -use crate::utils::{ - is_adjusted, is_trait_method, is_type_diagnostic_item, match_path, match_var, paths, remove_blocks, - span_lint_and_sugg, -}; +use crate::utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 01126e86199..6f4ce87ea5c 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then}; +use crate::utils::{iter_input_pats, method_chain_args, snippet, span_lint_and_then}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 086dae9422f..65afd691d62 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; +use crate::utils::{snippet, span_lint_and_sugg}; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e6c2885eb4d..4689974e850 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,13 +2,13 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ - expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, - snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, strip_pat_refs, + expr_block, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, + meets_msrv, multispan_sugg, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, + remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, + span_lint_and_note, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index c71c2ee7d70..d9d86ac9912 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -1,10 +1,10 @@ -use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth}; +use crate::utils::{match_def_path, paths, snippet, span_lint_and_then}; +use clippy_utils::ty::walk_ptrs_ty_depth; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; - use std::iter; declare_clippy_lint! { diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 5decb81d9f2..073d684dbe3 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,8 +1,9 @@ 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, + in_macro, match_qpath, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions, }; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 71a7e195e41..a695c27f11b 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 4a130ed47db..954d589a47e 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_copy, span_lint_and_then, sugg}; +use crate::utils::{span_lint_and_then, sugg}; +use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 3d5a68d69d7..7bc4ed622dc 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_macro_callsite, span_lint_and_sugg}; +use crate::utils::{paths, snippet_with_macro_callsite, span_lint_and_sugg}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 6866e9c652a..3de35c930ea 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{is_expn_of, snippet, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 90b781bd9d1..37c41194b4a 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index b03835f97e6..791fe3647cb 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_expr, match_type, paths, span_lint_and_help}; +use crate::utils::{get_parent_expr, paths, span_lint_and_help}; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index a5c5bc0e4e2..9b46adaac58 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, implements_trait, paths, span_lint_and_sugg, sugg}; +use crate::utils::{get_trait_def_id, paths, span_lint_and_sugg, sugg}; +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index e157db2712a..35071a25cec 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -1,7 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{ - get_parent_expr, is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg, -}; +use crate::utils::{get_parent_expr, paths, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 3045b09c238..42ba62cd4e7 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,7 +1,6 @@ use super::INEFFICIENT_TO_STRING; -use crate::utils::{ - is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth, -}; +use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_then}; +use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 1e8315dbee2..f28f082e6fc 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,4 +1,5 @@ -use crate::utils::{has_iter_method, match_trait_method, paths, span_lint_and_sugg}; +use crate::utils::{match_trait_method, paths, span_lint_and_sugg}; +use clippy_utils::ty::has_iter_method; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index c3e48ffa5fa..1e1d3fdcf70 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 869440e0165..320fd15d6f8 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,6 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg}; - +use crate::utils::{paths, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index 3c03a949cfe..736148d4fb8 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, higher, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, higher, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index cc3e56ea872..17600bb8153 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,6 +1,7 @@ use crate::methods::derefs_to_slice; use crate::methods::iter_nth_zero; -use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index 8f4de00a2b7..0a8837be752 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, is_type_diagnostic_item, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index afd76e8b959..3d1c6408dea 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, is_type_diagnostic_item, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 63b2cf87f32..4346869ae0d 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,5 +1,6 @@ use crate::utils::usage::mutated_variables; -use crate::utils::{is_type_diagnostic_item, meets_msrv, snippet, span_lint, span_lint_and_sugg}; +use crate::utils::{meets_msrv, snippet, span_lint, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 45e906cf468..3e78b662b89 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -52,6 +52,7 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -66,9 +67,9 @@ use rustc_span::symbol::{sym, SymbolStr}; use rustc_typeck::hir_ty_to_ty; use crate::utils::{ - contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_type_diagnostic_item, - iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, return_ty, - single_segment_path, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, + contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls, + method_chain_args, paths, return_ty, single_segment_path, snippet_with_applicability, span_lint, + span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; declare_clippy_lint! { diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index c1706cc7cc7..2618d04b242 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,4 +1,5 @@ -use crate::utils::{implements_trait, is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::span_lint_and_help; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 89067dbfe0e..17444336a5e 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,5 @@ -use crate::utils::{ - is_type_diagnostic_item, match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, snippet, - span_lint_and_sugg, -}; +use crate::utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 64f6ebc5062..5bd4a5bfe21 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 7cdd49bbf03..d1c78b3a833 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,5 +1,6 @@ use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then}; -use crate::utils::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 5f7fc431d22..9f24fe79089 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,8 +1,9 @@ use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::{ - contains_return, get_trait_def_id, implements_trait, is_type_diagnostic_item, last_path_segment, match_type, paths, - snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint_and_sugg, + contains_return, get_trait_def_id, last_path_segment, paths, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint_and_sugg, }; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index eb3dc308722..d903f1e74d3 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ use crate::utils::{ - is_trait_method, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_help, - span_lint_and_sugg, strip_pat_refs, + is_trait_method, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, strip_pat_refs, }; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 0a08ea26175..5b8cca34931 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a17259d697f..d260a771c01 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,5 +1,6 @@ use crate::utils::{eager_or_lazy, usage}; -use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; +use crate::utils::{snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index 094c3fc45c4..efe43a6a952 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index e4554f8d489..e6b1789a4a8 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - get_parent_expr, match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty_depth, -}; +use crate::utils::{get_parent_expr, match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::walk_ptrs_ty_depth; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index acdc245456b..5c4d4401cd9 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -17,9 +18,9 @@ use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; use crate::utils::sugg::Sugg; use crate::utils::{ - get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_diagnostic_assoc_item, is_integer_const, - iter_input_pats, last_path_segment, match_qpath, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq, + get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, + last_path_segment, match_qpath, snippet, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then, + span_lint_hir_and_then, unsext, SpanlessEq, }; declare_clippy_lint! { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index b0998a80128..4cc20b8d38c 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,6 @@ use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{ - fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method, -}; +use crate::utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method}; +use clippy_utils::ty::has_drop; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index df1cecb328c..e4a57661f97 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability}; diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 40b236493a3..9f746ce2e1a 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -2,7 +2,8 @@ //! //! This lint is **warn** by default -use crate::utils::{is_type_diagnostic_item, span_lint}; +use crate::utils::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index cac4b207511..14989283681 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,8 +1,6 @@ use crate::utils::ptr::get_spans; -use crate::utils::{ - get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet, - snippet_opt, span_lint_and_then, -}; +use crate::utils::{get_trait_def_id, is_self, multispan_sugg, paths, snippet, snippet_opt, span_lint_and_then}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index a3293f1b361..d616290ee9c 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -175,8 +176,8 @@ fn is_some_or_ok_call<'a>( let outer_ty = cx.typeck_results().expr_ty(expr); // Check if outer and inner type are Option - let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type); - let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type); + let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type); + let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); // Check for Option MSRV let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); @@ -185,8 +186,8 @@ fn is_some_or_ok_call<'a>( } // Check if outer and inner type are Result - let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type); - let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type); + let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type); + let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type); // Additional check: if the error type of the Result can be converted // via the From trait, then don't match 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 ec0ad58ca9c..67d49f06ad9 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -60,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let implements_ord = { if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) { - utils::implements_trait(cx, ty, id, &[]) + implements_trait(cx, ty, id, &[]) } else { return; } @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let implements_partial_ord = { if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { - utils::implements_trait(cx, ty, id, &[]) + implements_trait(cx, ty, id, &[]) } else { return; } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 69302d695ce..c377d373ad6 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,4 +1,5 @@ -use crate::utils::{has_drop, snippet_opt, span_lint, span_lint_and_sugg}; +use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg}; +use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 07ca196990d..71e77932f7a 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_type, paths, span_lint}; +use crate::utils::{paths, span_lint}; +use clippy_utils::ty::match_type; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9ef0d267b0b..8a6d257848f 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,7 +1,8 @@ use crate::utils; use crate::utils::eager_or_lazy; use crate::utils::sugg::Sugg; -use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; +use crate::utils::{paths, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 207423a1861..1821e2611ff 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,4 +1,5 @@ -use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use crate::utils::{find_macro_calls, return_ty, span_lint_and_then}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index ff700aa5146..c9b59b89306 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,6 +1,7 @@ use std::cmp; -use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; +use crate::utils::{is_self_ty, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_ast::attr; use rustc_errors::Applicability; diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 4a7b0ad07aa..46533682d42 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 6ea2d8b06d8..c2649a135eb 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,10 +1,8 @@ //! Checks for usage of `&Vec[_]` and `&String`. use crate::utils::ptr::get_spans; -use crate::utils::{ - 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 crate::utils::{is_allowed, match_qpath, paths, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 6c480d48c75..dc00aaffa58 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -7,10 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; use crate::utils::sugg::Sugg; -use crate::utils::{ - eq_expr_value, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, - span_lint_and_sugg, -}; +use crate::utils::{eq_expr_value, match_def_path, match_qpath, paths, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for expressions that could be replaced by the question mark operator. diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index c57e1d0ba77..d2e5a5db372 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ use crate::utils::{ - fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, paths, snippet_opt, - span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth, + fn_has_unsatisfiable_preds, match_def_path, paths, snippet_opt, span_lint_hir, span_lint_hir_and_then, }; +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use if_chain::if_chain; use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index e5ced13514f..992d1901584 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -5,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{lint::in_external_macro, ty::TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for redundant slicing expressions which use the full range, and diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index d34e744eb94..7d668739c5a 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg}; +use crate::utils::{in_macro, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 31dd5965473..62b4b40df45 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,3 +1,10 @@ +use crate::utils::SpanlessEq; +use crate::utils::{ + get_parent_expr, is_allowed, match_function_call, method_calls, paths, span_lint, span_lint_and_help, + span_lint_and_sugg, +}; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -7,14 +14,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use rustc_span::sym; -use if_chain::if_chain; - -use crate::utils::SpanlessEq; -use crate::utils::{ - get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, - span_lint_and_help, span_lint_and_sugg, -}; - declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without /// `let`!). diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 9d8a0c24833..ba2d73a8228 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, -}; +use crate::utils::{differing_macro_contexts, eq_expr_value, snippet_with_applicability, span_lint_and_then}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 55008d8ec3f..f4b0f205993 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_normalizable, last_path_segment, snippet}; +use crate::utils::{last_path_segment, snippet}; +use clippy_utils::ty::is_normalizable; use if_chain::if_chain; use rustc_hir::{Expr, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 73e3a04aec9..bdb81ea32d7 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,7 +1,8 @@ use crate::utils::{ - differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, + differing_macro_contexts, in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + span_lint_and_sugg, }; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 13da768b0ca..fcd38718b9b 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -13,6 +13,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; +use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -37,9 +38,9 @@ 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, indent_of, int_bits, is_isize_or_usize, - is_type_diagnostic_item, match_path, multispan_sugg, reindent_multiline, sext, snippet, snippet_opt, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_then, unsext, + clip, comparisons, differing_macro_contexts, higher, indent_of, int_bits, match_path, multispan_sugg, + reindent_multiline, sext, snippet, snippet_opt, snippet_with_macro_callsite, span_lint, span_lint_and_help, + span_lint_and_then, unsext, }; declare_clippy_lint! { diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 5443f1601fc..8c34ca16e6f 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help}; +use crate::utils::{match_function_call, paths, span_lint_and_help}; +use clippy_utils::ty::is_type_lang_item; use rustc_hir::{lang_items, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 00a707107bc..7e385b00646 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,6 @@ use crate::utils; use crate::utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; @@ -176,7 +177,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 2fb0463c5a6..43b5f138a42 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated, -}; +use crate::utils::{differing_macro_contexts, span_lint_and_then, usage::is_potentially_mutated}; +use clippy_utils::ty::is_type_diagnostic_item; 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}; diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 8cb7429849d..6a8f8211566 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,6 +1,9 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then}; +use crate::utils::{method_chain_args, return_ty, span_lint_and_then}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; @@ -66,9 +69,6 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { } } -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>, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index c5334853986..23032c74f9a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,8 +1,9 @@ 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, + get_parent_expr, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, + span_lint_and_help, span_lint_and_sugg, }; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0a347516c3a..2f4bc74ef64 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,8 +1,9 @@ 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, run_lints, snippet, - span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, + is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, snippet, span_lint, + span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; use rustc_ast::visit::FnKind; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index c132e4de4f6..682cc35a7d7 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,6 +1,7 @@ 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 crate::utils::{higher, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 8d111f98add..b3262a253e7 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg, -}; +use crate::utils::{match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs index 32574d9d6c9..079a4279c60 100644 --- a/clippy_lints/src/verbose_file_reads.rs +++ b/clippy_lints/src/verbose_file_reads.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_type, paths, span_lint_and_help}; +use crate::utils::{paths, span_lint_and_help}; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index adf7077e650..ab27b60cfa4 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,3 +1,4 @@ +use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -7,7 +8,7 @@ use rustc_span::sym; use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help}; +use crate::utils::{paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 8013c4e4fcb..88b115a63d7 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -9,7 +9,8 @@ //! - or-fun-call //! - option-if-let-else -use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item}; +use crate::is_ctor_or_promotable_const_function; +use crate::ty::is_type_diagnostic_item; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5b2529b4930..366962d8b77 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -42,6 +42,7 @@ pub mod paths; pub mod ptr; pub mod qualify_min_const_fn; pub mod sugg; +pub mod ty; pub mod usage; pub mod visitors; @@ -54,7 +55,7 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability}; +use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; @@ -65,14 +66,13 @@ use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, GenericParam, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, - TraitRef, TyKind, Unsafety, Variant, Visibility, + TraitRef, TyKind, Variant, Visibility, }; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, IntTy, Ty, TyCtxt, TypeFoldable, UintTy}; +use rustc_middle::ty as rustc_ty; +use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{self, ExpnKind, MacroKind}; @@ -81,11 +81,10 @@ use rustc_span::sym; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP}; use rustc_target::abi::Integer; -use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; use crate::consts::{constant, Constant}; -use std::collections::HashMap; +use crate::ty::is_recursively_primitive_type; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -256,35 +255,6 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { matches!(pat.kind, PatKind::Wild) } -/// 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), - _ => false, - } -} - -/// 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), - _ => false, - } -} - -/// Checks if the type is equal to a lang item -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did, - _ => false, - } -} - /// Checks if the first type parameter is a lang item. pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> { let ty = get_qpath_generic_tys(qpath).next()?; @@ -319,11 +289,6 @@ pub fn is_ty_param_diagnostic_item( } } -/// Return `true` if the passed `typ` is `isize` or `usize`. -pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { - matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) -} - /// Checks if the method call given in `expr` belongs to the given trait. /// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { @@ -338,10 +303,10 @@ pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: cx.tcx .opt_associated_item(def_id) .and_then(|associated_item| match associated_item.container { - ty::TraitContainer(assoc_def_id) => Some(assoc_def_id), - ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() { - ty::Adt(adt, _) => Some(adt.did), - ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works + rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id), + rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() { + rustc_ty::Adt(adt, _) => Some(adt.did), + rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works _ => None, }, }) @@ -540,26 +505,6 @@ pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { } } -/// Checks whether a type implements a trait. -/// See also `get_trait_def_id`. -pub fn implements_trait<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - trait_id: DefId, - ty_params: &[GenericArg<'tcx>], -) -> bool { - // Do not check on infer_types to avoid panic in evaluate_obligation. - if ty.has_infer_types() { - return false; - } - let ty = cx.tcx.erase_regions(ty); - if ty.has_escaping_bound_vars() { - return false; - } - let ty_params = cx.tcx.mk_substs(ty_params.iter()); - cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env)) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: @@ -587,26 +532,6 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio None } -/// Checks whether this type implements `Drop`. -pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.ty_adt_def() { - Some(def) => def.has_dtor(cx.tcx), - None => false, - } -} - -/// Checks whether a type can be partially moved. -pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if has_drop(cx, ty) || is_copy(cx, ty) { - return false; - } - match ty.kind() { - ty::Param(_) => false, - ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))), - _ => true, - } -} - /// Returns the method names and argument list of nested method call expressions that make up /// `expr`. method/span lists are sorted with the most recent call first. pub fn method_calls<'tcx>( @@ -1155,26 +1080,6 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { } } -/// Returns the base type for HIR references and pointers. -pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_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) { - fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind() { - ty::Ref(_, ty, _) => inner(ty, depth + 1), - _ => (ty, depth), - } - } - inner(ty, 0) -} - /// Checks whether the given expression is a constant integer of the given value. /// unlike `is_integer_literal`, this version does const folding pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool { @@ -1270,26 +1175,6 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(ret_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), - 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() { - ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, - _ => false, - } -} - -pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) -} - /// 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 { if let ExprKind::Call(ref fun, _) = expr.kind { @@ -1338,11 +1223,13 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, PatKind::Slice(ref head, ref middle, ref tail) => { match &cx.typeck_results().node_type(pat.hir_id).kind() { - ty::Slice(..) => { + rustc_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)), + rustc_ty::Array(..) => { + are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)) + }, _ => { // unreachable!() true @@ -1454,26 +1341,26 @@ pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { pat } -pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 { +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 { Integer::from_int_ty(&tcx, ity).size().bits() } #[allow(clippy::cast_possible_wrap)] /// Turn a constant int byte representation into an i128 -pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ty::IntTy) -> i128 { +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 { let amt = 128 - int_bits(tcx, ity); ((u as i128) << amt) >> amt } #[allow(clippy::cast_sign_loss)] /// clip unused bytes -pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ty::IntTy) -> u128 { +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 { let amt = 128 - int_bits(tcx, ity); ((u as u128) << amt) >> amt } /// clip unused bytes -pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ty::UintTy) -> u128 { +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 { let bits = Integer::from_uint_ty(&tcx, ity).size().bits(); let amt = 128 - bits; (u << amt) >> amt @@ -1526,47 +1413,6 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool false } -/// Returns true if ty has `iter` or `iter_mut` methods -pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { - // FIXME: instead of this hard-coded list, we should check if `::iter` - // exists and has the desired signature. Unfortunately FnCtxt is not exported - // so we can't use its `lookup_method` method. - let into_iter_collections: &[Symbol] = &[ - sym::vec_type, - sym::option_type, - sym::result_type, - sym::BTreeMap, - sym::BTreeSet, - sym::vecdeque_type, - sym::LinkedList, - sym::BinaryHeap, - sym::hashset_type, - sym::hashmap_type, - sym::PathBuf, - sym::Path, - sym::Receiver, - ]; - - let ty_to_check = match probably_ref_ty.kind() { - ty::Ref(_, ty_to_check, _) => ty_to_check, - _ => probably_ref_ty, - }; - - let def_id = match ty_to_check.kind() { - ty::Array(..) => return Some(sym::array), - ty::Slice(..) => return Some(sym::slice), - ty::Adt(adt, _) => adt.did, - _ => return None, - }; - - for &name in into_iter_collections { - if cx.tcx.is_diagnostic_item(name, def_id) { - return Some(cx.tcx.item_name(def_id)); - } - } - None -} - /// Matches a function call with the given path and returns the arguments. /// /// Usage: @@ -1591,51 +1437,6 @@ pub fn match_function_call<'tcx>( None } -// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize -// this function can be removed once the `normalizie` method does not panic when normalization does -// not succeed -/// Checks if `Ty` is normalizable. This function is useful -/// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - is_normalizable_helper(cx, param_env, ty, &mut HashMap::new()) -} - -fn is_normalizable_helper<'tcx>( - cx: &LateContext<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - cache: &mut HashMap, bool>, -) -> bool { - if let Some(&cached_result) = cache.get(ty) { - return cached_result; - } - // prevent recursive loops, false-negative is better than endless loop leading to stack overflow - cache.insert(ty, false); - let result = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = rustc_middle::traits::ObligationCause::dummy(); - if infcx.at(&cause, param_env).normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, substs) => def.variants.iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false - } - }); - cache.insert(ty, result); - result -} - pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path` // accepts only that. We should probably move to Symbols in Clippy as well. @@ -1721,44 +1522,6 @@ pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { attr_by_name(attrs, "must_use") } -// Returns whether the type has #[must_use] attribute -pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind() { - ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), - ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), - ty::Slice(ref ty) - | ty::Array(ref ty, _) - | ty::RawPtr(ty::TypeAndMut { ref ty, .. }) - | ty::Ref(_, ref ty, _) => { - // for the Array case we don't need to care for the len == 0 case - // because we don't want to lint functions returning empty arrays - is_must_use_ty(cx, *ty) - }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(ref def_id, _) => { - for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { - if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { - if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { - return true; - } - } - } - false - }, - ty::Dynamic(binder, _) => { - for predicate in binder.iter() { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { - return true; - } - } - } - false - }, - _ => false, - } -} - // 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 { @@ -1853,18 +1616,6 @@ 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 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. @@ -1872,9 +1623,9 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option is_recursively_primitive_type(element_type), - ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => { - if let ty::Slice(element_type) = inner_ty.kind() { + rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type), + rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => { + if let rustc_ty::Slice(element_type) = inner_ty.kind() { is_recursively_primitive_type(element_type) } else { unreachable!() @@ -1887,9 +1638,9 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option return Some("slice".into()), - ty::Array(..) => return Some("array".into()), - ty::Tuple(..) => return Some("tuple".into()), + rustc_ty::Slice(..) => return Some("slice".into()), + rustc_ty::Array(..) => return Some("array".into()), + rustc_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 @@ -1973,32 +1724,6 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { f(expr, 0) } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(ty, count + 1) - } else { - (ty, count) - } - } - peel(ty, 0) -} - -/// Peels off all references on the type.Returns the underlying type, the number of references -/// removed, and whether the pointer is ultimately mutable or not. -pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { - fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { - match ty.kind() { - ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability), - ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not), - _ => (ty, count, mutability), - } - } - f(ty, 0, Mutability::Mut) -} - #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs new file mode 100644 index 00000000000..f4a1ae67da3 --- /dev/null +++ b/clippy_utils/src/ty.rs @@ -0,0 +1,296 @@ +//! Util methods for [`rustc_middle::ty`] + +#![allow(clippy::module_name_repetitions)] + +use std::collections::HashMap; + +use rustc_ast::ast::Mutability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{TyKind, Unsafety}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::LateContext; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, IntTy, Ty, TypeFoldable, UintTy}; +use rustc_span::sym; +use rustc_span::symbol::Symbol; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::normalize::AtExt; + +use crate::{match_def_path, must_use_attr}; + +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) +} + +/// Checks whether a type can be partially moved. +pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if has_drop(cx, ty) || is_copy(cx, ty) { + return false; + } + match ty.kind() { + ty::Param(_) => false, + ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))), + _ => true, + } +} + +/// 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), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + +/// Returns true if ty has `iter` or `iter_mut` methods +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { + // FIXME: instead of this hard-coded list, we should check if `::iter` + // exists and has the desired signature. Unfortunately FnCtxt is not exported + // so we can't use its `lookup_method` method. + let into_iter_collections: &[Symbol] = &[ + sym::vec_type, + sym::option_type, + sym::result_type, + sym::BTreeMap, + sym::BTreeSet, + sym::vecdeque_type, + sym::LinkedList, + sym::BinaryHeap, + sym::hashset_type, + sym::hashmap_type, + sym::PathBuf, + sym::Path, + sym::Receiver, + ]; + + let ty_to_check = match probably_ref_ty.kind() { + ty::Ref(_, ty_to_check, _) => ty_to_check, + _ => probably_ref_ty, + }; + + let def_id = match ty_to_check.kind() { + ty::Array(..) => return Some(sym::array), + ty::Slice(..) => return Some(sym::slice), + ty::Adt(adt, _) => adt.did, + _ => return None, + }; + + for &name in into_iter_collections { + if cx.tcx.is_diagnostic_item(name, def_id) { + return Some(cx.tcx.item_name(def_id)); + } + } + None +} + +/// Checks whether a type implements a trait. +/// See also `get_trait_def_id`. +pub fn implements_trait<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + ty_params: &[GenericArg<'tcx>], +) -> bool { + // Do not check on infer_types to avoid panic in evaluate_obligation. + if ty.has_infer_types() { + return false; + } + let ty = cx.tcx.erase_regions(ty); + if ty.has_escaping_bound_vars() { + return false; + } + let ty_params = cx.tcx.mk_substs(ty_params.iter()); + cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env)) +} + +/// Checks whether this type implements `Drop`. +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.ty_adt_def() { + Some(def) => def.has_dtor(cx.tcx), + None => false, + } +} + +// Returns whether the type has #[must_use] attribute +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), + ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), + ty::Slice(ref ty) + | ty::Array(ref ty, _) + | ty::RawPtr(ty::TypeAndMut { ref ty, .. }) + | ty::Ref(_, ref ty, _) => { + // for the Array case we don't need to care for the len == 0 case + // because we don't want to lint functions returning empty arrays + is_must_use_ty(cx, *ty) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), + ty::Opaque(ref def_id, _) => { + for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { + if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { + if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { + return true; + } + } + } + false + }, + ty::Dynamic(binder, _) => { + for predicate in binder.iter() { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { + if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { + return true; + } + } + } + false + }, + _ => false, + } +} + +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize +// this function can be removed once the `normalizie` method does not panic when normalization does +// not succeed +/// Checks if `Ty` is normalizable. This function is useful +/// to avoid crashes on `layout_of`. +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + is_normalizable_helper(cx, param_env, ty, &mut HashMap::new()) +} + +fn is_normalizable_helper<'tcx>( + cx: &LateContext<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + cache: &mut HashMap, bool>, +) -> bool { + if let Some(&cached_result) = cache.get(ty) { + return cached_result; + } + // prevent recursive loops, false-negative is better than endless loop leading to stack overflow + cache.insert(ty, false); + let result = cx.tcx.infer_ctxt().enter(|infcx| { + let cause = rustc_middle::traits::ObligationCause::dummy(); + if infcx.at(&cause, param_env).normalize(ty).is_ok() { + match ty.kind() { + ty::Adt(def, substs) => def.variants.iter().all(|variant| { + variant + .fields + .iter() + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it + }), + } + } else { + false + } + }); + cache.insert(ty, result); + result +} + +/// 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, + } +} + +/// 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), + _ => false, + } +} + +/// Checks if the type is equal to a lang item +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did, + _ => false, + } +} + +/// Return `true` if the passed `typ` is `isize` or `usize`. +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { + matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) +} + +/// 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), + _ => false, + } +} + +/// Peels off all references on the type. Returns the underlying type and the number of references +/// removed. +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { + if let ty::Ref(_, ty, _) = ty.kind() { + peel(ty, count + 1) + } else { + (ty, count) + } + } + peel(ty, 0) +} + +/// Peels off all references on the type.Returns the underlying type, the number of references +/// removed, and whether the pointer is ultimately mutable or not. +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { + fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { + match ty.kind() { + ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability), + ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not), + _ => (ty, count, mutability), + } + } + f(ty, 0, Mutability::Mut) +} + +/// 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() { + ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, + _ => false, + } +} + +/// Returns the base type for HIR references and pointers. +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match ty.kind { + TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_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) { + fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { + match ty.kind() { + ty::Ref(_, ty, _) => inner(ty, depth + 1), + _ => (ty, depth), + } + } + inner(ty, 0) +} -- cgit 1.4.1-3-g733a5 From 6fc52a63d19c85e952fc81298e7dd2289a774ac6 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 14 Mar 2021 18:17:44 -0500 Subject: Move some utils to clippy_utils::source module --- clippy_lints/src/assertions_on_constants.rs | 3 +- clippy_lints/src/assign_ops.rs | 3 +- clippy_lints/src/async_yields_async.rs | 3 +- clippy_lints/src/attrs.rs | 6 +- clippy_lints/src/blocks_in_if_conditions.rs | 5 +- clippy_lints/src/booleans.rs | 5 +- clippy_lints/src/bytecount.rs | 5 +- clippy_lints/src/casts/cast_lossless.rs | 3 +- clippy_lints/src/casts/char_lit_as_u8.rs | 6 +- clippy_lints/src/casts/fn_to_numeric_cast.rs | 4 +- .../casts/fn_to_numeric_cast_with_truncation.rs | 4 +- clippy_lints/src/casts/unnecessary_cast.rs | 6 +- clippy_lints/src/checked_conversions.rs | 3 +- clippy_lints/src/cognitive_complexity.rs | 3 +- clippy_lints/src/collapsible_if.rs | 5 +- clippy_lints/src/create_dir.rs | 3 +- clippy_lints/src/dbg_macro.rs | 3 +- clippy_lints/src/default.rs | 5 +- clippy_lints/src/default_numeric_fallback.rs | 6 +- clippy_lints/src/dereference.rs | 3 +- clippy_lints/src/double_comparison.rs | 3 +- clippy_lints/src/duration_subsec.rs | 3 +- clippy_lints/src/entry.rs | 5 +- clippy_lints/src/enum_variants.rs | 3 +- clippy_lints/src/eq_op.rs | 5 +- clippy_lints/src/eta_reduction.rs | 3 +- clippy_lints/src/exhaustive_items.rs | 5 +- clippy_lints/src/format.rs | 5 +- clippy_lints/src/formatting.rs | 3 +- clippy_lints/src/functions.rs | 4 +- clippy_lints/src/get_last_with_len.rs | 3 +- clippy_lints/src/identity_op.rs | 3 +- clippy_lints/src/if_let_some_result.rs | 3 +- clippy_lints/src/if_then_some_else_none.rs | 5 +- clippy_lints/src/implicit_return.rs | 3 +- .../src/inconsistent_struct_constructor.rs | 6 +- clippy_lints/src/int_plus_one.rs | 3 +- clippy_lints/src/large_enum_variant.rs | 3 +- clippy_lints/src/large_stack_arrays.rs | 6 +- clippy_lints/src/len_zero.rs | 6 +- clippy_lints/src/let_if_seq.rs | 3 +- clippy_lints/src/literal_representation.rs | 3 +- clippy_lints/src/loops/explicit_counter_loop.rs | 3 +- clippy_lints/src/loops/explicit_into_iter_loop.rs | 3 +- clippy_lints/src/loops/explicit_iter_loop.rs | 4 +- clippy_lints/src/loops/for_kv_map.rs | 3 +- clippy_lints/src/loops/for_loops_over_fallibles.rs | 6 +- clippy_lints/src/loops/manual_memcpy.rs | 3 +- clippy_lints/src/loops/needless_collect.rs | 3 +- clippy_lints/src/loops/needless_range_loop.rs | 3 +- clippy_lints/src/loops/same_item_push.rs | 3 +- clippy_lints/src/loops/single_element_loop.rs | 3 +- clippy_lints/src/loops/while_let_loop.rs | 3 +- clippy_lints/src/loops/while_let_on_iterator.rs | 3 +- clippy_lints/src/macro_use.rs | 3 +- clippy_lints/src/main_recursion.rs | 5 +- clippy_lints/src/manual_async_fn.rs | 3 +- clippy_lints/src/manual_map.rs | 6 +- clippy_lints/src/manual_non_exhaustive.rs | 3 +- clippy_lints/src/manual_ok_or.rs | 5 +- clippy_lints/src/manual_strip.rs | 6 +- clippy_lints/src/manual_unwrap_or.rs | 7 +- clippy_lints/src/map_clone.rs | 3 +- clippy_lints/src/map_unit_fn.rs | 3 +- clippy_lints/src/match_on_vec_items.rs | 3 +- clippy_lints/src/matches.rs | 9 +- clippy_lints/src/mem_discriminant.rs | 3 +- clippy_lints/src/mem_replace.rs | 8 +- clippy_lints/src/methods/bind_instead_of_map.rs | 5 +- clippy_lints/src/methods/bytes_nth.rs | 3 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 3 +- clippy_lints/src/methods/expect_fun_call.rs | 3 +- clippy_lints/src/methods/filter_map.rs | 3 +- clippy_lints/src/methods/filter_map_next.rs | 3 +- clippy_lints/src/methods/filter_next.rs | 3 +- clippy_lints/src/methods/get_unwrap.rs | 3 +- clippy_lints/src/methods/inefficient_to_string.rs | 3 +- clippy_lints/src/methods/iter_count.rs | 3 +- clippy_lints/src/methods/iter_next_slice.rs | 3 +- clippy_lints/src/methods/iter_nth_zero.rs | 3 +- clippy_lints/src/methods/iter_skip_next.rs | 3 +- .../src/methods/manual_saturating_arithmetic.rs | 3 +- .../src/methods/map_collect_result_unit.rs | 3 +- clippy_lints/src/methods/map_flatten.rs | 3 +- clippy_lints/src/methods/map_unwrap_or.rs | 3 +- clippy_lints/src/methods/mod.rs | 5 +- clippy_lints/src/methods/option_as_ref_deref.rs | 3 +- clippy_lints/src/methods/option_map_or_none.rs | 3 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 3 +- clippy_lints/src/methods/or_fun_call.rs | 6 +- clippy_lints/src/methods/search_is_some.rs | 5 +- .../src/methods/single_char_insert_string.rs | 3 +- .../src/methods/single_char_push_string.rs | 3 +- clippy_lints/src/methods/string_extend_chars.rs | 3 +- clippy_lints/src/methods/unnecessary_fold.rs | 5 +- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 3 +- clippy_lints/src/methods/useless_asref.rs | 3 +- clippy_lints/src/misc.rs | 5 +- clippy_lints/src/misc_early.rs | 3 +- clippy_lints/src/needless_bool.rs | 3 +- clippy_lints/src/needless_borrow.rs | 3 +- clippy_lints/src/needless_borrowed_ref.rs | 3 +- clippy_lints/src/needless_continue.rs | 3 +- clippy_lints/src/needless_pass_by_value.rs | 3 +- clippy_lints/src/needless_question_mark.rs | 3 +- clippy_lints/src/no_effect.rs | 3 +- clippy_lints/src/pass_by_ref_or_value.rs | 3 +- clippy_lints/src/precedence.rs | 3 +- clippy_lints/src/ptr.rs | 3 +- clippy_lints/src/ptr_eq.rs | 5 +- clippy_lints/src/ptr_offset_with_cast.rs | 3 +- clippy_lints/src/question_mark.rs | 3 +- clippy_lints/src/ranges.rs | 5 +- clippy_lints/src/redundant_clone.rs | 5 +- clippy_lints/src/redundant_closure_call.rs | 3 +- clippy_lints/src/redundant_slicing.rs | 3 +- clippy_lints/src/redundant_static_lifetimes.rs | 3 +- clippy_lints/src/ref_option_ref.rs | 8 +- clippy_lints/src/reference.rs | 3 +- clippy_lints/src/repeat_once.rs | 3 +- clippy_lints/src/returns.rs | 3 +- clippy_lints/src/self_assignment.rs | 3 +- clippy_lints/src/semicolon_if_nothing_returned.rs | 3 +- clippy_lints/src/shadow.rs | 3 +- clippy_lints/src/strings.rs | 2 +- clippy_lints/src/suspicious_operation_groupings.rs | 3 +- clippy_lints/src/swap.rs | 3 +- clippy_lints/src/to_digit_is_some.rs | 3 +- clippy_lints/src/trait_bounds.rs | 3 +- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 3 +- clippy_lints/src/transmute/utils.rs | 3 +- clippy_lints/src/try_err.rs | 6 +- clippy_lints/src/types/borrowed_box.rs | 7 +- clippy_lints/src/types/mod.rs | 6 +- clippy_lints/src/types/rc_buffer.rs | 5 +- clippy_lints/src/types/redundant_allocation.rs | 6 +- clippy_lints/src/types/vec_box.rs | 6 +- clippy_lints/src/unicode.rs | 3 +- clippy_lints/src/unnecessary_wraps.rs | 4 +- clippy_lints/src/unused_unit.rs | 3 +- clippy_lints/src/use_self.rs | 3 +- clippy_lints/src/useless_conversion.rs | 4 +- clippy_lints/src/utils/internal_lints.rs | 3 +- clippy_lints/src/vec.rs | 3 +- clippy_lints/src/vec_init_then_push.rs | 3 +- clippy_lints/src/wildcard_imports.rs | 3 +- clippy_lints/src/write.rs | 3 +- clippy_utils/src/hir_utils.rs | 3 +- clippy_utils/src/lib.rs | 416 +------------------- clippy_utils/src/ptr.rs | 3 +- clippy_utils/src/source.rs | 420 +++++++++++++++++++++ clippy_utils/src/sugg.rs | 3 +- 152 files changed, 753 insertions(+), 649 deletions(-) create mode 100644 clippy_utils/src/source.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 77b26faaa58..3b6f64e7769 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help}; +use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index ae300324158..031827e7e89 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,5 +1,6 @@ -use crate::utils::{eq_expr_value, get_trait_def_id, snippet_opt, span_lint_and_then, trait_ref_of_method}; +use crate::utils::{eq_expr_value, get_trait_def_id, span_lint_and_then, trait_ref_of_method}; use crate::utils::{higher, sugg}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 1e185e97df6..a5ed6016874 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet, span_lint_and_then}; +use crate::utils::span_lint_and_then; +use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 6250810bc42..04b0e71e4a3 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,9 +1,7 @@ //! checks for attributes -use crate::utils::{ - first_line_of_span, is_present_in_source, match_panic_def_id, snippet_opt, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, without_block_comments, -}; +use crate::utils::{match_panic_def_id, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 222bb39f89c..f43f98d1dc0 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - differing_macro_contexts, get_parent_expr, snippet_block_with_applicability, span_lint, span_lint_and_sugg, -}; +use crate::utils::{differing_macro_contexts, get_parent_expr, span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index affc7d2c93f..12a07c60438 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - eq_expr_value, get_trait_def_id, in_macro, paths, snippet_opt, span_lint_and_sugg, span_lint_and_then, -}; +use crate::utils::{eq_expr_value, get_trait_def_id, in_macro, paths, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 67cca8dcadb..d02861b1b1e 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, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg, -}; +use crate::utils::{contains_name, get_pat_name, paths, single_segment_path, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 902fb423ff1..62ba19c0e16 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,10 +1,11 @@ +use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::{in_constant, snippet_opt, span_lint_and_sugg}; +use crate::utils::{in_constant, span_lint_and_sugg}; use super::{utils, CAST_LOSSLESS}; diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index ccaad1b8f2a..75aa559359c 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -1,12 +1,12 @@ +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, UintTy}; -use if_chain::if_chain; - -use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use crate::utils::span_lint_and_then; use super::CHAR_LIT_AS_U8; diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index a8d508585b5..723bfa5befe 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -1,10 +1,10 @@ +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, UintTy}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; - use super::{utils, FN_TO_NUMERIC_CAST}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 0085c7b27b2..a3108f8a983 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -1,10 +1,10 @@ +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; - use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c43bbf32949..eb0ee012e6c 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,3 +1,5 @@ +use clippy_utils::source::snippet_opt; +use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit, UnOp}; @@ -5,9 +7,7 @@ use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; -use if_chain::if_chain; - -use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint_and_sugg}; +use crate::utils::{numeric_literal::NumericLiteral, span_lint_and_sugg}; use super::UNNECESSARY_CAST; diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 54bc69e058b..4f3daa427e3 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,5 +1,6 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -9,7 +10,7 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{meets_msrv, span_lint_and_sugg, SpanlessEq}; const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 3d842a46813..5b8ef01505b 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,6 @@ //! calculate cognitive complexity and warn about overly complex functions +use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; @@ -10,7 +11,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::{sym, BytePos}; -use crate::utils::{snippet_opt, span_lint_and_help, LimitStack}; +use crate::utils::{span_lint_and_help, LimitStack}; declare_clippy_lint! { /// **What it does:** Checks for methods with high cognitive complexity. diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 34f0e6ab027..c866f18ef3e 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -12,14 +12,15 @@ //! //! This lint is **warn** by default +use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; use if_chain::if_chain; use rustc_ast::ast; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::sugg::Sugg; -use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then}; -use rustc_errors::Applicability; +use crate::utils::{span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 200b6a565cc..0785e25b0a2 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index e513dcce64e..3171a945eb0 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use crate::utils::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; use rustc_ast::ast; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 6fa1378b8c7..7d975b5a3d9 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,7 +1,6 @@ -use crate::utils::{ - any_parent_is_automatically_derived, contains_name, match_def_path, paths, snippet_with_macro_callsite, -}; +use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; use crate::utils::{span_lint_and_note, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_macro_callsite; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 369efacc9bc..e58dcb942c6 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,3 +1,5 @@ +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ @@ -11,9 +13,7 @@ use rustc_middle::{ }; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use if_chain::if_chain; - -use crate::utils::{snippet, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8842ea9628d..e112338dfea 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_node, in_macro, is_allowed, snippet_with_context, span_lint_and_sugg}; +use crate::utils::{get_parent_node, in_macro, is_allowed, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::peel_mid_ty_refs; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 19f56195ec1..3de027b2cc6 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -1,12 +1,13 @@ //! Lint on unnecessary double comparisons. Some examples: +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{eq_expr_value, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index c97392e3385..195143d720d 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; @@ -8,7 +9,7 @@ use rustc_span::source_map::Spanned; use crate::consts::{constant, Constant}; use crate::utils::paths; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 68363e53f0e..93723219594 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,6 +1,7 @@ +use crate::utils::span_lint_and_then; use crate::utils::SpanlessEq; -use crate::utils::{get_item_name, paths, snippet, snippet_opt}; -use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use crate::utils::{get_item_name, paths}; +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 67a46353856..660131cf176 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -1,7 +1,8 @@ //! lint on enum variants that are prefixed or suffixed by the same characters -use crate::utils::{camel_case, is_present_in_source}; +use crate::utils::camel_case; use crate::utils::{span_lint, span_lint_and_help}; +use clippy_utils::source::is_present_in_source; use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index fa19f16074d..0a5917c10ea 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,7 +1,8 @@ use crate::utils::{ - ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, multispan_sugg, snippet, - span_lint, span_lint_and_then, + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, multispan_sugg, span_lint, + span_lint_and_then, }; +use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index d93c32c179f..e8d5b992b63 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,5 +1,6 @@ use clippy_utils::higher; use clippy_utils::higher::VecArgs; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, type_is_unsafe_function}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -9,7 +10,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{is_adjusted, iter_input_pats, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for closures which just call another function where diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 316f7484862..5e31072523d 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,11 +1,14 @@ -use crate::utils::{indent_of, span_lint_and_then}; +use clippy_utils::source::indent_of; use if_chain::if_chain; + use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; +use crate::utils::span_lint_and_then; + declare_clippy_lint! { /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index b6209f815bc..c0048bb2175 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,7 +1,6 @@ use crate::utils::paths; -use crate::utils::{ - is_expn_of, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, span_lint_and_then, -}; +use crate::utils::{is_expn_of, last_path_segment, match_def_path, match_function_call, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 1bd16e6cce5..590de04717a 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note}; +use crate::utils::{differing_macro_contexts, span_lint_and_help, span_lint_and_note}; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 349cd39ffb6..104c692dcec 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,8 +1,8 @@ use crate::utils::{ attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, - path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, - trait_ref_of_method, + path_to_local, return_ty, span_lint, span_lint_and_help, span_lint_and_then, trait_ref_of_method, }; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; use if_chain::if_chain; use rustc_ast::ast::Attribute; diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index 478008ea48c..875cd33bc8f 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -1,6 +1,7 @@ //! lint on using `x.get(x.len() - 1)` instead of `x.last()` -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{span_lint_and_sugg, SpanlessEq}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 8501d347702..fc93864c74a 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -6,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{constant_simple, Constant}; -use crate::utils::{clip, snippet, span_lint, unsext}; +use crate::utils::{clip, span_lint, unsext}; declare_clippy_lint! { /// **What it does:** Checks for identity operations, e.g., `x + 0`. diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 0cd44a1c3a0..9f7ca95a8f3 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -1,4 +1,5 @@ -use crate::utils::{method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{method_chain_args, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index a527f51b1fd..7e1807786ee 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,4 +1,5 @@ use crate::utils; +use clippy_utils::source::snippet_with_macro_callsite; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,13 +83,13 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if utils::match_qpath(els_call_qpath, &utils::paths::OPTION_NONE); then { - let cond_snip = utils::snippet_with_macro_callsite(cx, cond.span, "[condition]"); + let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { format!("({})", cond_snip) } else { cond_snip.into_owned() }; - let arg_snip = utils::snippet_with_macro_callsite(cx, then_arg.span, ""); + let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, ""); let closure_body = if then_block.stmts.is_empty() { arg_snip.into_owned() } else { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index b4f814e1dcc..e86bd49251d 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_panic_def_id, snippet_opt, span_lint_and_then}; +use crate::utils::{match_panic_def_id, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 49c17a12102..4762d5d40f3 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,3 +1,5 @@ +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind}; @@ -5,9 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; -use if_chain::if_chain; - -use crate::utils::{snippet, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for struct constructors where all fields are shorthand and diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 260b8988d37..9eae653dd67 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -1,11 +1,12 @@ //! lint on blocks unnecessarily using >= with a + 1 or - 1 +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{snippet_opt, span_lint_and_sugg}; +use crate::utils::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 diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index ab4cb33612d..cbb5192bfd9 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,6 +1,7 @@ //! lint when there is a large size difference between variants on an enum -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 9a448ab1256..ceae4243e63 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -1,13 +1,13 @@ +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use if_chain::if_chain; - use crate::rustc_target::abi::LayoutOf; -use crate::utils::{snippet, span_lint_and_help}; +use crate::utils::span_lint_and_help; declare_clippy_lint! { /// **What it does:** Checks for local arrays that may be too large. diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 1e1023b2743..e758a269fbe 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,7 +1,5 @@ -use crate::utils::{ - get_item_name, get_parent_as_impl, is_allowed, snippet_with_applicability, span_lint, span_lint_and_sugg, - span_lint_and_then, -}; +use crate::utils::{get_item_name, get_parent_as_impl, is_allowed, span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 5863eef8a26..7059ba21207 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,4 +1,5 @@ -use crate::utils::{path_to_local_id, snippet, span_lint_and_then, visitors::LocalUsedVisitor}; +use crate::utils::{path_to_local_id, span_lint_and_then, visitors::LocalUsedVisitor}; +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 87a957a9bd2..8bc7bf37ef1 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -4,8 +4,9 @@ use crate::utils::{ in_macro, numeric_literal::{NumericLiteral, Radix}, - snippet_opt, span_lint_and_sugg, + span_lint_and_sugg, }; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 8d98b940c66..1f6d48fe915 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -1,7 +1,8 @@ use super::{ get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP, }; -use crate::utils::{get_enclosing_block, is_integer_const, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{get_enclosing_block, is_integer_const, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 1d778205a2a..e5b3dc7aad7 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,5 +1,6 @@ use super::EXPLICIT_INTO_ITER_LOOP; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index b70585015ca..d2000d80ac1 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,4 +1,6 @@ use super::EXPLICIT_ITER_LOOP; +use crate::utils::{match_trait_method, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; @@ -6,8 +8,6 @@ use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TyS}; use rustc_span::sym; -use crate::utils::{match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg}; - pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { let should_lint = match method_name { "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]), diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index aef21886803..19a68dd78d1 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,6 +1,7 @@ use super::FOR_KV_MAP; use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{multispan_sugg, paths, snippet, span_lint_and_then, sugg}; +use crate::utils::{multispan_sugg, paths, span_lint_and_then, sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs index 063078adcd1..5140448592d 100644 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ b/clippy_lints/src/loops/for_loops_over_fallibles.rs @@ -1,6 +1,8 @@ -use super::FOR_LOOPS_OVER_FALLIBLES; -use crate::utils::{snippet, span_lint_and_help}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; + +use super::FOR_LOOPS_OVER_FALLIBLES; +use crate::utils::span_lint_and_help; use rustc_hir::{Expr, Pat}; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 362124c082a..c9c25641160 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,6 +1,7 @@ use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use crate::utils::sugg::Sugg; -use crate::utils::{get_enclosing_block, higher, path_to_local, snippet, span_lint_and_sugg, sugg}; +use crate::utils::{get_enclosing_block, higher, path_to_local, span_lint_and_sugg, sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index f4d3b9eb272..59f7b23af75 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,6 +1,7 @@ use super::NEEDLESS_COLLECT; use crate::utils::sugg::Sugg; -use crate::utils::{is_trait_method, path_to_local_id, paths, snippet, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{is_trait_method, path_to_local_id, paths, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 6d498149166..674cb34c37b 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -1,9 +1,10 @@ use super::NEEDLESS_RANGE_LOOP; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ - contains_name, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id, paths, snippet, + contains_name, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id, paths, span_lint_and_then, sugg, SpanlessEq, }; +use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index f891f7b8a1f..255d6de4a36 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,5 +1,6 @@ use super::SAME_ITEM_PUSH; -use crate::utils::{snippet_with_macro_callsite, span_lint_and_help}; +use crate::utils::span_lint_and_help; +use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 38400c93c9a..4f83191d919 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,5 +1,6 @@ use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP}; -use crate::utils::{indent_of, single_segment_path, snippet, span_lint_and_sugg}; +use crate::utils::{single_segment_path, span_lint_and_sugg}; +use clippy_utils::source::{indent_of, snippet}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind}; diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 65d8f2f1111..dbd9126861f 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -1,5 +1,6 @@ use super::WHILE_LET_LOOP; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind}; use rustc_lint::{LateContext, LintContext}; diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 619fd1554f0..ccabe586c2b 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -3,8 +3,9 @@ use super::WHILE_LET_ON_ITERATOR; use crate::utils::usage::mutated_variables; use crate::utils::{ get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, - snippet_with_applicability, span_lint_and_sugg, + span_lint_and_sugg, }; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 6d9c78393c8..637f10f6609 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, snippet, span_lint_and_sugg}; +use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index 1b274c79d38..5db2968e42c 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -1,9 +1,10 @@ +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_hir::{Crate, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_lint_and_help}; -use if_chain::if_chain; +use crate::utils::{is_entrypoint_fn, is_no_std_crate, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for recursion using the entrypoint. diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 2e2e693592c..ebc493c0f7e 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,6 @@ use crate::utils::paths::FUTURE_FROM_GENERATOR; -use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then}; +use crate::utils::{match_function_call, span_lint_and_then}; +use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 7c89359f2d9..3896645ca7d 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,11 +1,9 @@ use crate::{ map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF, - utils::{ - is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs, snippet_with_applicability, - snippet_with_context, span_lint_and_sugg, - }, + utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs, span_lint_and_sugg}, }; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 91849e74887..0b8c049b466 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,5 @@ -use crate::utils::{meets_msrv, snippet_opt, span_lint_and_then}; +use crate::utils::{meets_msrv, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; use rustc_attr as attr; diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index b08829a8bbf..f436eccc0dc 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - indent_of, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt, span_lint_and_sugg, -}; +use crate::utils::{match_qpath, path_to_local_id, paths, span_lint_and_sugg}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 42a92104a49..3bfca8bea40 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,9 +1,7 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; -use crate::utils::{ - eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, snippet, span_lint_and_then, -}; - +use crate::utils::{eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, span_lint_and_then}; +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 3a1bde9540d..7a4040539e3 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,6 +1,7 @@ use crate::consts::constant_simple; use crate::utils; use crate::utils::{path_to_local_id, sugg}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -105,12 +106,12 @@ 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(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); - if let Some(indent) = utils::indent_of(cx, expr.span); + if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); + if let Some(indent) = 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)); + reindent_multiline(or_body_snippet.into(), true, Some(indent)); utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 73d924cd51e..e10d7647bcf 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,5 +1,6 @@ use crate::utils::is_trait_method; -use crate::utils::{remove_blocks, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{remove_blocks, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 6f4ce87ea5c..24bcc808585 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,4 +1,5 @@ -use crate::utils::{iter_input_pats, method_chain_args, snippet, span_lint_and_then}; +use crate::utils::{iter_input_pats, method_chain_args, span_lint_and_then}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 65afd691d62..57dcd8709b8 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4689974e850..4ae1ce977f4 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,12 +2,12 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ - expr_block, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, - meets_msrv, multispan_sugg, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, - remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, multispan_sugg, + path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -1614,7 +1614,8 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; - use crate::utils::{is_trait_method, match_qpath, paths, snippet, span_lint_and_then}; + use crate::utils::{is_trait_method, match_qpath, paths, span_lint_and_then}; + use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index d9d86ac9912..fbdc0cdb2d8 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, snippet, span_lint_and_then}; +use crate::utils::{match_def_path, paths, span_lint_and_then}; +use clippy_utils::source::snippet; use clippy_utils::ty::walk_ptrs_ty_depth; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 13f9c9b71f3..bf3f6f7f830 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,9 @@ use crate::utils::{ - in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, + in_macro, match_def_path, match_qpath, meets_msrv, paths, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, }; +use clippy_utils::is_diagnostic_assoc_item; +use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -13,8 +15,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; -use clippy_utils::is_diagnostic_assoc_item; - declare_clippy_lint! { /// **What it does:** Checks for `mem::replace()` on an `Option` with /// `None`. diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 073d684dbe3..99b3be67f18 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,8 +1,9 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use crate::utils::{ - in_macro, match_qpath, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions, + in_macro, match_qpath, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, span_lint_and_sugg, + span_lint_and_then, visitors::find_all_ret_expressions, }; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index a695c27f11b..f81e9a8c524 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 7bc4ed622dc..90ecb2382e7 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,4 +1,5 @@ -use crate::utils::{paths, snippet_with_macro_callsite, span_lint_and_sugg}; +use crate::utils::{paths, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 3de35c930ea..9e96d571337 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, snippet, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{is_expn_of, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 91c11afcaaf..964c4903ed2 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, path_to_local_id, snippet, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{is_trait_method, path_to_local_id, span_lint_and_sugg, SpanlessEq}; +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 9019202df0c..ed75315b52c 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, meets_msrv, snippet, span_lint, span_lint_and_sugg}; +use crate::utils::{is_trait_method, meets_msrv, span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 34787b9a828..097f9fdf2c4 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, snippet, span_lint, span_lint_and_sugg}; +use crate::utils::{is_trait_method, span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 35071a25cec..b9d34b402bb 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, paths, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 42ba62cd4e7..19d05b5c693 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,5 +1,6 @@ use super::INEFFICIENT_TO_STRING; -use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_then}; +use crate::utils::{match_def_path, paths, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 320fd15d6f8..0f393423b7d 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{paths, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{paths, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index 736148d4fb8..f79942576da 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, higher, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, higher, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index ca2e5db4382..98ddfdfdf9c 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_trait_method, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{is_trait_method, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index bdfa133b9e2..d191ea0a831 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, span_lint_and_sugg}; +use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 0b414e0eb95..d090a35a3cf 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{match_qpath, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index 0a8837be752..349b26b9d58 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 3d1c6408dea..56719b3cff2 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, snippet, span_lint_and_sugg}; +use crate::utils::{is_trait_method, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 4346869ae0d..96dbc7ddc63 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,5 +1,6 @@ use crate::utils::usage::mutated_variables; -use crate::utils::{meets_msrv, snippet, span_lint, span_lint_and_sugg}; +use crate::utils::{meets_msrv, span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3e78b662b89..6e0ae9416ff 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -52,6 +52,7 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_ast::ast; @@ -68,8 +69,8 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::{ contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls, - method_chain_args, paths, return_ty, single_segment_path, snippet_with_applicability, span_lint, - span_lint_and_help, span_lint_and_sugg, SpanlessEq, + method_chain_args, paths, return_ty, single_segment_path, span_lint, span_lint_and_help, span_lint_and_sugg, + SpanlessEq, }; declare_clippy_lint! { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 17444336a5e..6597e9f96a8 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 5bd4a5bfe21..eed71d02467 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index d1c78b3a833..398d8f13bd4 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then}; +use crate::utils::{differing_macro_contexts, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use clippy_utils::ty::is_type_diagnostic_item; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 9f24fe79089..634feebe54a 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,8 +1,6 @@ use crate::utils::eager_or_lazy::is_lazyness_candidate; -use crate::utils::{ - contains_return, get_trait_def_id, last_path_segment, paths, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint_and_sugg, -}; +use crate::utils::{contains_return, get_trait_def_id, last_path_segment, paths, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index d903f1e74d3..6054579d988 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - is_trait_method, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, strip_pat_refs, -}; +use crate::utils::{is_trait_method, span_lint_and_help, span_lint_and_sugg, strip_pat_refs}; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index 0ce8b66978d..ff67564b39d 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -1,5 +1,6 @@ use crate::methods::get_hint_if_single_char_arg; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index deacc70b713..18df90c1ab3 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -1,5 +1,6 @@ use crate::methods::get_hint_if_single_char_arg; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 5b8cca34931..1b26e8314af 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,4 +1,5 @@ -use crate::utils::{method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{method_chain_args, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 9aa53f02ef0..4d5cbdd619d 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - is_trait_method, path_to_local_id, remove_blocks, snippet_with_applicability, span_lint_and_sugg, strip_pat_refs, -}; +use crate::utils::{is_trait_method, path_to_local_id, remove_blocks, span_lint_and_sugg, strip_pat_refs}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index d260a771c01..444abde3d0f 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,5 +1,6 @@ +use crate::utils::span_lint_and_sugg; use crate::utils::{eager_or_lazy, usage}; -use crate::utils::{snippet, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index e6b1789a4a8..c6d84aedc0a 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_expr, match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, match_trait_method, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 5c4d4401cd9..f161054cc8c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -19,8 +20,8 @@ use crate::consts::{constant, Constant}; use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, - last_path_segment, match_qpath, snippet, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then, - span_lint_hir_and_then, unsext, SpanlessEq, + last_path_segment, match_qpath, span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then, unsext, + SpanlessEq, }; declare_clippy_lint! { diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 84a0df92f5b..6ec523498e1 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, UnOp, diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index f283ff1715f..22adbdf09a6 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,8 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{is_expn_of, parent_node_is_if_expr, span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 1aadcfd87b6..d8b574af1fe 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -2,7 +2,8 @@ //! //! This lint is **warn** by default -use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then}; +use crate::utils::{is_automatically_derived, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind}; diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index f449f397e7d..5ee71f25694 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use crate::utils::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 30fe2d6225c..4ff90704207 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -33,13 +33,14 @@ //! ``` //! //! This lint is **warn** by default. +use clippy_utils::source::{indent_of, snippet, snippet_block}; use rustc_ast::ast; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::Span; -use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help}; +use crate::utils::span_lint_and_help; declare_clippy_lint! { /// **What it does:** The lint checks for `if`-statements appearing in loops diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 14989283681..6f7a5d85480 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,5 +1,6 @@ use crate::utils::ptr::get_spans; -use crate::utils::{get_trait_def_id, is_self, multispan_sugg, paths, snippet, snippet_opt, span_lint_and_then}; +use crate::utils::{get_trait_def_id, is_self, multispan_sugg, paths, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_ast::ast::Attribute; diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index d616290ee9c..bcc39ff855c 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -145,7 +146,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { entire_expr.span, "question mark operator is useless here", "try", - format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), + format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index c377d373ad6..7a7bc7a44cd 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg}; +use crate::utils::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index c9b59b89306..757ead2c24c 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,6 +1,7 @@ use std::cmp; -use crate::utils::{is_self_ty, snippet, span_lint_and_sugg}; +use crate::utils::{is_self_ty, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_ast::attr; diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index c9d18c3cb72..fbe54e92ab9 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 crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c2649a135eb..bfe8bf33f51 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,7 +1,8 @@ //! Checks for usage of `&Vec[_]` and `&String`. use crate::utils::ptr::get_spans; -use crate::utils::{is_allowed, match_qpath, paths, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{is_allowed, match_qpath, paths, span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 3be792ce5e4..4f83e370c5f 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -1,4 +1,5 @@ use crate::utils; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -54,8 +55,8 @@ impl LateLintPass<'_> for PtrEq { 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); + if let Some(left_snip) = snippet_opt(cx, left_var.span); + if let Some(right_snip) = snippet_opt(cx, right_var.span); then { utils::span_lint_and_sugg( cx, diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index e0996804a59..b801defeb24 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg}; +use crate::utils::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index dc00aaffa58..43431425a43 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -8,7 +9,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; use crate::utils::sugg::Sugg; -use crate::utils::{eq_expr_value, match_def_path, match_qpath, paths, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{eq_expr_value, match_def_path, match_qpath, paths, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for expressions that could be replaced by the question mark operator. diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 59503817c0f..3ce8949bf8b 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,4 +1,5 @@ use crate::consts::{constant, Constant}; +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -14,8 +15,8 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, - snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, + get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, span_lint, span_lint_and_sugg, + span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index d2e5a5db372..84723acd034 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - fn_has_unsatisfiable_preds, match_def_path, paths, snippet_opt, span_lint_hir, span_lint_hir_and_then, -}; +use crate::utils::{fn_has_unsatisfiable_preds, match_def_path, paths, span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use if_chain::if_chain; use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index f398b3fff25..283e25553cf 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then}; +use crate::utils::{span_lint, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; use rustc_ast::visit as ast_visit; diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 992d1901584..85ea91a387b 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -6,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{lint::in_external_macro, ty::TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for redundant slicing expressions which use the full range, and diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index fcfa3c12755..1352a651723 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,4 +1,5 @@ -use crate::utils::{meets_msrv, snippet, span_lint_and_then}; +use crate::utils::{meets_msrv, span_lint_and_then}; +use clippy_utils::source::snippet; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 8cd6692ce03..cec6b06262b 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,12 +1,12 @@ -use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; +use crate::utils::{last_path_segment, span_lint_and_sugg}; +use clippy_utils::source::snippet; +use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; -use if_chain::if_chain; -use rustc_errors::Applicability; - declare_clippy_lint! { /// **What it does:** Checks for usage of `&Option<&T>`. /// diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index e1450466a7c..31e834ac174 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,5 +1,6 @@ use crate::utils::sugg::Sugg; -use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 7d668739c5a..a88078c12a3 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{in_macro, snippet, span_lint_and_sugg}; +use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 40c0f1f4589..eb7fe403fd7 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; @@ -11,7 +12,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs index e096c9aebc1..e62b75de4ca 100644 --- a/clippy_lints/src/self_assignment.rs +++ b/clippy_lints/src/self_assignment.rs @@ -1,4 +1,5 @@ -use crate::utils::{eq_expr_value, snippet, span_lint}; +use crate::utils::{eq_expr_value, span_lint}; +use clippy_utils::source::snippet; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 839c995e525..695d7233af2 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, snippet_with_macro_callsite, span_lint_and_sugg, sugg}; +use crate::utils::{in_macro, span_lint_and_sugg, sugg}; +use clippy_utils::source::snippet_with_macro_callsite; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 32f6bc74642..4a8cacb31fd 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,4 +1,5 @@ -use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; +use crate::utils::{contains_name, higher, iter_input_pats, span_lint_and_then}; +use clippy_utils::source::snippet; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 62b4b40df45..ce93ab23b2f 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -3,6 +3,7 @@ use crate::utils::{ get_parent_expr, is_allowed, match_function_call, method_calls, paths, span_lint, span_lint_and_help, span_lint_and_sugg, }; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -204,7 +205,6 @@ declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - use crate::utils::{snippet, snippet_with_applicability}; use rustc_ast::LitKind; if_chain! { diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 44521885d20..3bdd9b7e4cb 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,5 +1,6 @@ use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index ba2d73a8228..d4a495f3ea9 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,5 +1,6 @@ use crate::utils::sugg::Sugg; -use crate::utils::{differing_macro_contexts, eq_expr_value, snippet_with_applicability, span_lint_and_then}; +use crate::utils::{differing_macro_contexts, eq_expr_value, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index eeda39bfa20..940273afc57 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{match_def_path, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index daff5f81e8c..d3314271c21 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 crate::utils::{in_macro, span_lint_and_help, SpanlessHash}; +use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 01b00bb0a22..4780eb9b14e 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -1,5 +1,6 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; -use crate::utils::{snippet, span_lint_and_sugg, span_lint_and_then, sugg}; +use crate::utils::{span_lint_and_sugg, span_lint_and_then, sugg}; +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index f4b0f205993..0633697687a 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,4 +1,5 @@ -use crate::utils::{last_path_segment, snippet}; +use crate::utils::last_path_segment; +use clippy_utils::source::snippet; use clippy_utils::ty::is_normalizable; use if_chain::if_chain; use rustc_hir::{Expr, GenericArg, QPath, TyKind}; diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index bdb81ea32d7..e356add8e9d 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,7 +1,5 @@ -use crate::utils::{ - differing_macro_contexts, in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, - span_lint_and_sugg, -}; +use crate::utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index a7a511b21cf..01aeea7a67f 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,3 +1,6 @@ +use crate::utils::{match_path, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, @@ -5,10 +8,6 @@ use rustc_hir::{ }; use rustc_lint::LateContext; -use if_chain::if_chain; - -use crate::utils::{match_path, paths, snippet, span_lint_and_sugg}; - use super::BORROWED_BOX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index fcd38718b9b..279a971318c 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -13,6 +13,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; +use clippy_utils::source::{indent_of, reindent_multiline, snippet, snippet_opt, snippet_with_macro_callsite}; use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::{Applicability, DiagnosticBuilder}; @@ -38,9 +39,8 @@ 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, indent_of, int_bits, match_path, multispan_sugg, - reindent_multiline, sext, snippet, snippet_opt, snippet_with_macro_callsite, span_lint, span_lint_and_help, - span_lint_and_then, unsext, + clip, comparisons, differing_macro_contexts, higher, int_bits, match_path, multispan_sugg, sext, span_lint, + span_lint_and_help, span_lint_and_then, unsext, }; declare_clippy_lint! { diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index e34b95147e1..0ace1807535 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,11 +1,10 @@ +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{ - get_qpath_generic_tys, is_ty_param_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, -}; +use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, span_lint_and_sugg}; use super::RC_BUFFER; diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 5da6db179c4..c6f6a2f6564 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -1,12 +1,10 @@ +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{ - get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item, snippet_with_applicability, - span_lint_and_sugg, -}; +use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item, span_lint_and_sugg}; use super::{utils, REDUNDANT_ALLOCATION}; diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 2530cc133c6..6f45442b9ba 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -1,3 +1,5 @@ +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; @@ -6,9 +8,7 @@ use rustc_span::symbol::sym; use rustc_target::abi::LayoutOf; use rustc_typeck::hir_ty_to_ty; -use if_chain::if_chain; - -use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; +use crate::utils::{last_path_segment, span_lint_and_sugg}; use super::VEC_BOX; diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index 93d59cc7fcd..e44fec7ad8e 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_allowed, snippet, span_lint_and_sugg}; +use crate::utils::{is_allowed, span_lint_and_sugg}; +use clippy_utils::source::snippet; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId}; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 8e076397c11..01497de3211 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,7 +1,7 @@ use crate::utils::{ - contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, + contains_return, in_macro, match_qpath, paths, return_ty, span_lint_and_then, visitors::find_all_ret_expressions, }; +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index a31cd5fda84..a90d26fc95c 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::position_before_rarrow; use if_chain::if_chain; use rustc_ast::ast; use rustc_ast::visit::FnKind; @@ -7,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::{position_before_rarrow, 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. diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f0523cec621..de7eb42d56d 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,7 @@ -use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; +use crate::utils::{in_macro, meets_msrv, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 23032c74f9a..e6b4fde560f 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,8 +1,8 @@ use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, - span_lint_and_help, span_lint_and_sugg, + get_parent_expr, match_def_path, match_trait_method, paths, span_lint_and_help, span_lint_and_sugg, }; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 2f4bc74ef64..04b8d9ee2c7 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,8 +1,9 @@ use crate::consts::{constant_simple, Constant}; use crate::utils::{ - is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, snippet, span_lint, + is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; +use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 682cc35a7d7..cd09a5b53e0 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,6 +1,7 @@ use crate::consts::{constant, Constant}; use crate::rustc_target::abi::LayoutOf; -use crate::utils::{higher, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{higher, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index b3262a253e7..4ad787ecf66 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, path_to_local, path_to_local_id, paths, span_lint_and_sugg}; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 094b1a42346..e12ca49fd4c 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index f21f5180554..f2fef73a641 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,7 +1,8 @@ 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 crate::utils::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index e28ad27d9a6..4c13a185d5d 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_context, constant_simple}; -use crate::{differing_macro_contexts, snippet_opt}; +use crate::differing_macro_contexts; +use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 366962d8b77..d895d798b5e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -41,6 +41,7 @@ pub mod numeric_literal; pub mod paths; pub mod ptr; pub mod qualify_min_const_fn; +pub mod source; pub mod sugg; pub mod ty; pub mod usage; @@ -50,14 +51,12 @@ pub use self::attrs::*; 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 if_chain::if_chain; use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -75,11 +74,11 @@ use rustc_middle::ty as rustc_ty; use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; -use rustc_span::hygiene::{self, ExpnKind, MacroKind}; +use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use smallvec::SmallVec; @@ -236,20 +235,6 @@ pub fn in_macro(span: Span) -> bool { } } -// 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. -// For some reason these attributes don't have any expansion info on them, so -// we have to check it this way until there is a better way. -pub fn is_present_in_source(cx: &T, span: Span) -> bool { - if let Some(snippet) = snippet_opt(cx, span) { - if snippet.is_empty() { - return false; - } - } - true -} - /// Checks if given pattern is a wildcard (`_`) pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { matches!(pat.kind, PatKind::Wild) @@ -713,211 +698,6 @@ pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec { fmc.result } -/// Converts a span to a code snippet if available, otherwise use default. -/// -/// This is useful if you want to provide suggestions for your lint or more generally, if you want -/// to convert a given `Span` to a `str`. -/// -/// # Example -/// ```rust,ignore -/// snippet(cx, expr.span, "..") -/// ``` -pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) -} - -/// Same as `snippet`, but it adapts the applicability level by following rules: -/// -/// - Applicability level `Unspecified` will never be changed. -/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. -/// - If the default value is used and the applicability level is `MachineApplicable`, change it to -/// `HasPlaceholders` -pub fn snippet_with_applicability<'a, T: LintContext>( - cx: &T, - span: Span, - default: &'a str, - applicability: &mut Applicability, -) -> Cow<'a, str> { - if *applicability != Applicability::Unspecified && span.from_expansion() { - *applicability = Applicability::MaybeIncorrect; - } - snippet_opt(cx, span).map_or_else( - || { - if *applicability == Applicability::MachineApplicable { - *applicability = Applicability::HasPlaceholders; - } - Cow::Borrowed(default) - }, - From::from, - ) -} - -/// Same as `snippet`, but should only be used when it's clear that the input span is -/// not a macro argument. -pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet(cx, span.source_callsite(), default) -} - -/// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(cx: &T, span: Span) -> Option { - cx.sess().source_map().span_to_snippet(span).ok() -} - -/// Converts a span (from a block) to a code snippet if available, otherwise use default. -/// -/// This trims the code of indentation, except for the first line. Use it for blocks or block-like -/// things which need to be printed as such. -/// -/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the -/// resulting snippet of the given span. -/// -/// # Example -/// -/// ```rust,ignore -/// snippet_block(cx, block.span, "..", None) -/// // where, `block` is the block of the if expr -/// if x { -/// y; -/// } -/// // will return the snippet -/// { -/// y; -/// } -/// ``` -/// -/// ```rust,ignore -/// snippet_block(cx, block.span, "..", Some(if_expr.span)) -/// // where, `block` is the block of the if expr -/// if x { -/// y; -/// } -/// // will return the snippet -/// { -/// y; -/// } // aligned with `if` -/// ``` -/// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block<'a, T: LintContext>( - cx: &T, - span: Span, - default: &'a str, - indent_relative_to: Option, -) -> Cow<'a, str> { - let snip = snippet(cx, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - reindent_multiline(snip, true, indent) -} - -/// Same as `snippet_block`, but adapts the applicability level by the rules of -/// `snippet_with_applicability`. -pub fn snippet_block_with_applicability<'a, T: LintContext>( - cx: &T, - span: Span, - default: &'a str, - indent_relative_to: Option, - applicability: &mut Applicability, -) -> Cow<'a, str> { - let snip = snippet_with_applicability(cx, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - reindent_multiline(snip, true, indent) -} - -/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This -/// will result in the macro call, rather then the expansion, if the span is from a child context. -/// If the span is not from a child context, it will be used directly instead. -/// -/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node -/// would result in `box []`. If given the context of the address of expression, this function will -/// correctly get a snippet of `vec![]`. -/// -/// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context( - cx: &LateContext<'_>, - span: Span, - outer: SyntaxContext, - default: &'a str, - applicability: &mut Applicability, -) -> (Cow<'a, str>, bool) { - let outer_span = hygiene::walk_chain(span, outer); - let (span, is_macro_call) = if outer_span.ctxt() == outer { - (outer_span, span.ctxt() != outer) - } else { - // The span is from a macro argument, and the outer context is the macro using the argument - if *applicability != Applicability::Unspecified { - *applicability = Applicability::MaybeIncorrect; - } - // TODO: get the argument span. - (span, false) - }; - - ( - snippet_with_applicability(cx, span, default, applicability), - is_macro_call, - ) -} - -/// Returns a new Span that extends the original Span to the first non-whitespace char of the first -/// line. -/// -/// ```rust,ignore -/// let x = (); -/// // ^^ -/// // will be converted to -/// let x = (); -/// // ^^^^^^^^^^ -/// ``` -pub fn first_line_of_span(cx: &T, span: Span) -> 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); - 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 -/// -/// ```rust,ignore -/// let x = (); -/// // ^^ -- will return 0 -/// let x = (); -/// // ^^ -- will return 4 -/// ``` -pub fn indent_of(cx: &T, span: Span) -> Option { - snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) -} - -/// Returns the positon just before rarrow -/// -/// ```rust,ignore -/// fn into(self) -> () {} -/// ^ -/// // in case of unformatted code -/// fn into2(self)-> () {} -/// ^ -/// fn into3(self) -> () {} -/// ^ -/// ``` -pub fn position_before_rarrow(s: &str) -> Option { - s.rfind("->").map(|rpos| { - let mut rpos = rpos; - let chars: Vec = s.chars().collect(); - while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } - } - break; - } - rpos - }) -} - /// Extends the span to the beginning of the spans line, incl. whitespaces. /// /// ```rust,ignore @@ -935,66 +715,6 @@ fn line_span(cx: &T, span: Span) -> Span { Span::new(line_start, span.hi(), span.ctxt()) } -/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. -/// Also takes an `Option` which can be put inside the braces. -pub fn expr_block<'a, T: LintContext>( - cx: &T, - expr: &Expr<'_>, - option: Option, - default: &'a str, - indent_relative_to: Option, -) -> Cow<'a, str> { - let code = snippet_block(cx, expr.span, default, indent_relative_to); - let string = option.unwrap_or_default(); - if expr.span.from_expansion() { - Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) - } else if let ExprKind::Block(_, _) = expr.kind { - Cow::Owned(format!("{}{}", code, string)) - } else if string.is_empty() { - Cow::Owned(format!("{{ {} }}", code)) - } else { - Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) - } -} - -/// 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 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| { - if l.is_empty() { - None - } else { - // ignore empty lines - Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0) - } - }) - .min() - .unwrap_or(0); - 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 span of the node, if there is one. pub fn get_node_span(node: Node<'_>) -> Option { match node { @@ -1366,39 +1086,6 @@ pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 { (u << amt) >> amt } -/// Removes block comments from the given `Vec` of lines. -/// -/// # Examples -/// -/// ```rust,ignore -/// without_block_comments(vec!["/*", "foo", "*/"]); -/// // => vec![] -/// -/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); -/// // => vec!["bar"] -/// ``` -pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { - let mut without = vec![]; - - let mut nest_level = 0; - - for line in lines { - if line.contains("/*") { - nest_level += 1; - continue; - } else if line.contains("*/") { - nest_level -= 1; - continue; - } - - if nest_level == 0 { - without.push(line); - } - } - - without -} - pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool { let map = &tcx.hir(); let mut prev_enclosing_node = None; @@ -1777,100 +1464,3 @@ pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { } false } - -#[cfg(test)] -mod test { - use super::{reindent_multiline, without_block_comments}; - - #[test] - 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_reindent_multiline_block() { - assert_eq!("\ - if x { - y - } else { - z - }", reindent_multiline(" if x { - y - } else { - z - }".into(), false, None)); - assert_eq!("\ - if x { - \ty - } else { - \tz - }", reindent_multiline(" if x { - \ty - } else { - \tz - }".into(), false, None)); - } - - #[test] - #[rustfmt::skip] - fn test_reindent_multiline_empty_line() { - assert_eq!("\ - if x { - y - - } else { - z - }", reindent_multiline(" if x { - y - - } else { - z - }".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!["/*", "", "*/"]); - println!("result: {:?}", result); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); - assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); - - let result = without_block_comments(vec!["/* rust", "", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* one-line comment */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["foo", "bar", "baz"]); - assert_eq!(result, vec!["foo", "bar", "baz"]); - } -} diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index df6143edbca..5885cc83560 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,4 +1,5 @@ -use crate::{get_pat_name, match_var, snippet}; +use crate::source::snippet; +use crate::{get_pat_name, match_var}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_lint::LateContext; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs new file mode 100644 index 00000000000..2d794d48dc5 --- /dev/null +++ b/clippy_utils/src/source.rs @@ -0,0 +1,420 @@ +//! Utils for extracting, inspecting or transforming source code + +#![allow(clippy::module_name_repetitions)] + +use crate::line_span; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_span::hygiene; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use std::borrow::Cow; + +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. +/// Also takes an `Option` which can be put inside the braces. +pub fn expr_block<'a, T: LintContext>( + cx: &T, + expr: &Expr<'_>, + option: Option, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let code = snippet_block(cx, expr.span, default, indent_relative_to); + let string = option.unwrap_or_default(); + if expr.span.from_expansion() { + Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) + } else if let ExprKind::Block(_, _) = expr.kind { + Cow::Owned(format!("{}{}", code, string)) + } else if string.is_empty() { + Cow::Owned(format!("{{ {} }}", code)) + } else { + Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) + } +} + +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first +/// line. +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ +/// // will be converted to +/// let x = (); +/// // ^^^^^^^^^^ +/// ``` +pub fn first_line_of_span(cx: &T, span: Span) -> 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); + 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 +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ -- will return 0 +/// let x = (); +/// // ^^ -- will return 4 +/// ``` +pub fn indent_of(cx: &T, span: Span) -> Option { + snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +} + +// 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. +// For some reason these attributes don't have any expansion info on them, so +// we have to check it this way until there is a better way. +pub fn is_present_in_source(cx: &T, span: Span) -> bool { + if let Some(snippet) = snippet_opt(cx, span) { + if snippet.is_empty() { + return false; + } + } + true +} + +/// Returns the positon just before rarrow +/// +/// ```rust,ignore +/// fn into(self) -> () {} +/// ^ +/// // in case of unformatted code +/// fn into2(self)-> () {} +/// ^ +/// fn into3(self) -> () {} +/// ^ +/// ``` +pub fn position_before_rarrow(s: &str) -> Option { + s.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec = s.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }) +} + +/// 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 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| { + if l.is_empty() { + None + } else { + // ignore empty lines + Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0) + } + }) + .min() + .unwrap_or(0); + 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") +} + +/// Converts a span to a code snippet if available, otherwise use default. +/// +/// This is useful if you want to provide suggestions for your lint or more generally, if you want +/// to convert a given `Span` to a `str`. +/// +/// # Example +/// ```rust,ignore +/// snippet(cx, expr.span, "..") +/// ``` +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) +} + +/// Same as `snippet`, but it adapts the applicability level by following rules: +/// +/// - Applicability level `Unspecified` will never be changed. +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to +/// `HasPlaceholders` +pub fn snippet_with_applicability<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + applicability: &mut Applicability, +) -> Cow<'a, str> { + if *applicability != Applicability::Unspecified && span.from_expansion() { + *applicability = Applicability::MaybeIncorrect; + } + snippet_opt(cx, span).map_or_else( + || { + if *applicability == Applicability::MachineApplicable { + *applicability = Applicability::HasPlaceholders; + } + Cow::Borrowed(default) + }, + From::from, + ) +} + +/// Same as `snippet`, but should only be used when it's clear that the input span is +/// not a macro argument. +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { + snippet(cx, span.source_callsite(), default) +} + +/// Converts a span to a code snippet. Returns `None` if not available. +pub fn snippet_opt(cx: &T, span: Span) -> Option { + cx.sess().source_map().span_to_snippet(span).ok() +} + +/// Converts a span (from a block) to a code snippet if available, otherwise use default. +/// +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like +/// things which need to be printed as such. +/// +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the +/// resulting snippet of the given span. +/// +/// # Example +/// +/// ```rust,ignore +/// snippet_block(cx, block.span, "..", None) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } +/// ``` +/// +/// ```rust,ignore +/// snippet_block(cx, block.span, "..", Some(if_expr.span)) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } // aligned with `if` +/// ``` +/// Note that the first line of the snippet always has 0 indentation. +pub fn snippet_block<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let snip = snippet(cx, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + reindent_multiline(snip, true, indent) +} + +/// Same as `snippet_block`, but adapts the applicability level by the rules of +/// `snippet_with_applicability`. +pub fn snippet_block_with_applicability<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option, + applicability: &mut Applicability, +) -> Cow<'a, str> { + let snip = snippet_with_applicability(cx, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + reindent_multiline(snip, true, indent) +} + +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This +/// will result in the macro call, rather then the expansion, if the span is from a child context. +/// If the span is not from a child context, it will be used directly instead. +/// +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node +/// would result in `box []`. If given the context of the address of expression, this function will +/// correctly get a snippet of `vec![]`. +/// +/// This will also return whether or not the snippet is a macro call. +pub fn snippet_with_context( + cx: &LateContext<'_>, + span: Span, + outer: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, +) -> (Cow<'a, str>, bool) { + let outer_span = hygiene::walk_chain(span, outer); + let (span, is_macro_call) = if outer_span.ctxt() == outer { + (outer_span, span.ctxt() != outer) + } else { + // The span is from a macro argument, and the outer context is the macro using the argument + if *applicability != Applicability::Unspecified { + *applicability = Applicability::MaybeIncorrect; + } + // TODO: get the argument span. + (span, false) + }; + + ( + snippet_with_applicability(cx, span, default, applicability), + is_macro_call, + ) +} + +/// Removes block comments from the given `Vec` of lines. +/// +/// # Examples +/// +/// ```rust,ignore +/// without_block_comments(vec!["/*", "foo", "*/"]); +/// // => vec![] +/// +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); +/// // => vec!["bar"] +/// ``` +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { + let mut without = vec![]; + + let mut nest_level = 0; + + for line in lines { + if line.contains("/*") { + nest_level += 1; + continue; + } else if line.contains("*/") { + nest_level -= 1; + continue; + } + + if nest_level == 0 { + without.push(line); + } + } + + without +} + +#[cfg(test)] +mod test { + use super::{reindent_multiline, without_block_comments}; + + #[test] + 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_reindent_multiline_block() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline(" if x { + y + } else { + z + }".into(), false, None)); + assert_eq!("\ + if x { + \ty + } else { + \tz + }", reindent_multiline(" if x { + \ty + } else { + \tz + }".into(), false, None)); + } + + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_empty_line() { + assert_eq!("\ + if x { + y + + } else { + z + }", reindent_multiline(" if x { + y + + } else { + z + }".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!["/*", "", "*/"]); + println!("result: {:?}", result); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); + assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); + + let result = without_block_comments(vec!["/* rust", "", "*/"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* one-line comment */"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["foo", "bar", "baz"]); + assert_eq!(result, vec!["foo", "bar", "baz"]); + } +} diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index d4f6f4281d3..b2fe4317154 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,8 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; +use crate::higher; +use crate::source::{snippet, snippet_opt, snippet_with_macro_callsite}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ast, token}; use rustc_ast_pretty::pprust::token_kind_to_string; -- cgit 1.4.1-3-g733a5 From 1c3a3e7dc62ae3b4b84cc00c067789dae1d722c9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 15 Mar 2021 19:55:45 -0500 Subject: Don't re-export clippy_utils::diagnostics::* --- clippy_lints/src/approx_const.rs | 2 +- clippy_lints/src/arithmetic.rs | 2 +- clippy_lints/src/as_conversions.rs | 3 +-- clippy_lints/src/asm_syntax.rs | 2 +- clippy_lints/src/assertions_on_constants.rs | 3 ++- clippy_lints/src/assign_ops.rs | 3 ++- clippy_lints/src/async_yields_async.rs | 2 +- clippy_lints/src/atomic_ordering.rs | 3 ++- clippy_lints/src/attrs.rs | 3 ++- clippy_lints/src/await_holding_invalid.rs | 3 ++- clippy_lints/src/bit_mask.rs | 2 +- clippy_lints/src/blacklisted_name.rs | 2 +- clippy_lints/src/blocks_in_if_conditions.rs | 3 ++- clippy_lints/src/booleans.rs | 3 ++- clippy_lints/src/bytecount.rs | 3 ++- clippy_lints/src/cargo_common_metadata.rs | 3 ++- clippy_lints/src/case_sensitive_file_extension_comparisons.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 3 ++- clippy_lints/src/casts/cast_possible_truncation.rs | 2 +- clippy_lints/src/casts/cast_possible_wrap.rs | 3 +-- clippy_lints/src/casts/cast_precision_loss.rs | 3 +-- clippy_lints/src/casts/cast_ptr_alignment.rs | 3 ++- clippy_lints/src/casts/cast_ref_to_mut.rs | 6 ++---- clippy_lints/src/casts/cast_sign_loss.rs | 3 ++- clippy_lints/src/casts/char_lit_as_u8.rs | 3 +-- clippy_lints/src/casts/fn_to_numeric_cast.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs | 2 +- clippy_lints/src/casts/ptr_as_ptr.rs | 3 ++- clippy_lints/src/casts/unnecessary_cast.rs | 3 ++- clippy_lints/src/checked_conversions.rs | 3 ++- clippy_lints/src/cognitive_complexity.rs | 3 ++- clippy_lints/src/collapsible_if.rs | 2 +- clippy_lints/src/collapsible_match.rs | 3 ++- clippy_lints/src/comparison_chain.rs | 3 ++- clippy_lints/src/copies.rs | 3 ++- clippy_lints/src/copy_iterator.rs | 2 +- clippy_lints/src/create_dir.rs | 3 ++- clippy_lints/src/dbg_macro.rs | 2 +- clippy_lints/src/default.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 3 +-- clippy_lints/src/dereference.rs | 3 ++- clippy_lints/src/derive.rs | 6 ++---- clippy_lints/src/disallowed_method.rs | 3 ++- clippy_lints/src/doc.rs | 5 ++--- clippy_lints/src/double_comparison.rs | 3 ++- clippy_lints/src/double_parens.rs | 2 +- clippy_lints/src/drop_forget_ref.rs | 3 ++- clippy_lints/src/duration_subsec.rs | 2 +- clippy_lints/src/else_if_without_else.rs | 3 +-- clippy_lints/src/empty_enum.rs | 2 +- clippy_lints/src/entry.rs | 2 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/eq_op.rs | 6 ++---- clippy_lints/src/erasing_op.rs | 2 +- clippy_lints/src/escape.rs | 3 +-- clippy_lints/src/eta_reduction.rs | 3 ++- clippy_lints/src/eval_order_dependence.rs | 3 ++- clippy_lints/src/excessive_bools.rs | 3 ++- clippy_lints/src/exhaustive_items.rs | 4 +--- clippy_lints/src/exit.rs | 3 ++- clippy_lints/src/explicit_write.rs | 3 ++- clippy_lints/src/fallible_impl_from.rs | 3 ++- clippy_lints/src/float_equality_without_abs.rs | 3 ++- clippy_lints/src/float_literal.rs | 3 ++- clippy_lints/src/floating_point_arithmetic.rs | 3 ++- clippy_lints/src/format.rs | 3 ++- clippy_lints/src/formatting.rs | 3 ++- clippy_lints/src/from_over_into.rs | 3 ++- clippy_lints/src/from_str_radix_10.rs | 2 +- clippy_lints/src/functions.rs | 3 ++- clippy_lints/src/future_not_send.rs | 3 ++- clippy_lints/src/get_last_with_len.rs | 3 ++- clippy_lints/src/identity_op.rs | 3 ++- clippy_lints/src/if_let_mutex.rs | 3 ++- clippy_lints/src/if_let_some_result.rs | 3 ++- clippy_lints/src/if_not_else.rs | 3 +-- clippy_lints/src/if_then_some_else_none.rs | 3 ++- clippy_lints/src/implicit_return.rs | 3 ++- clippy_lints/src/implicit_saturating_sub.rs | 3 ++- clippy_lints/src/inconsistent_struct_constructor.rs | 3 +-- clippy_lints/src/indexing_slicing.rs | 3 ++- clippy_lints/src/infinite_iter.rs | 3 ++- clippy_lints/src/inherent_impl.rs | 3 ++- clippy_lints/src/inherent_to_string.rs | 3 ++- clippy_lints/src/inline_fn_without_body.rs | 2 +- clippy_lints/src/int_plus_one.rs | 3 +-- clippy_lints/src/integer_division.rs | 2 +- clippy_lints/src/items_after_statements.rs | 2 +- clippy_lints/src/large_const_arrays.rs | 2 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/large_stack_arrays.rs | 2 +- clippy_lints/src/len_zero.rs | 3 ++- clippy_lints/src/let_if_seq.rs | 3 ++- clippy_lints/src/let_underscore.rs | 3 ++- clippy_lints/src/lifetimes.rs | 3 ++- clippy_lints/src/literal_representation.rs | 2 +- clippy_lints/src/loops/empty_loop.rs | 3 ++- clippy_lints/src/loops/explicit_counter_loop.rs | 3 ++- clippy_lints/src/loops/explicit_into_iter_loop.rs | 2 +- clippy_lints/src/loops/explicit_iter_loop.rs | 3 ++- clippy_lints/src/loops/for_kv_map.rs | 3 ++- clippy_lints/src/loops/for_loops_over_fallibles.rs | 5 ++--- clippy_lints/src/loops/iter_next_loop.rs | 3 ++- clippy_lints/src/loops/manual_flatten.rs | 3 ++- clippy_lints/src/loops/manual_memcpy.rs | 3 ++- clippy_lints/src/loops/mut_range_bound.rs | 3 ++- clippy_lints/src/loops/needless_collect.rs | 3 ++- clippy_lints/src/loops/needless_range_loop.rs | 4 ++-- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/loops/single_element_loop.rs | 3 ++- clippy_lints/src/loops/while_immutable_condition.rs | 2 +- clippy_lints/src/loops/while_let_loop.rs | 2 +- clippy_lints/src/loops/while_let_on_iterator.rs | 2 +- clippy_lints/src/macro_use.rs | 3 ++- clippy_lints/src/main_recursion.rs | 3 ++- clippy_lints/src/manual_async_fn.rs | 3 ++- clippy_lints/src/manual_map.rs | 3 ++- clippy_lints/src/manual_non_exhaustive.rs | 3 ++- clippy_lints/src/manual_ok_or.rs | 3 ++- clippy_lints/src/manual_strip.rs | 3 ++- clippy_lints/src/manual_unwrap_or.rs | 3 ++- clippy_lints/src/map_clone.rs | 3 ++- clippy_lints/src/map_err_ignore.rs | 3 +-- clippy_lints/src/map_identity.rs | 3 ++- clippy_lints/src/map_unit_fn.rs | 3 ++- clippy_lints/src/match_on_vec_items.rs | 2 +- clippy_lints/src/matches.rs | 11 +++++++---- clippy_lints/src/mem_discriminant.rs | 3 ++- clippy_lints/src/mem_forget.rs | 3 ++- clippy_lints/src/mem_replace.rs | 6 ++---- clippy_lints/src/methods/bind_instead_of_map.rs | 6 ++---- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/clone_on_copy.rs | 3 ++- clippy_lints/src/methods/clone_on_ref_ptr.rs | 3 ++- clippy_lints/src/methods/expect_fun_call.rs | 3 ++- clippy_lints/src/methods/expect_used.rs | 2 +- clippy_lints/src/methods/filetype_is_file.rs | 3 ++- clippy_lints/src/methods/filter_flat_map.rs | 3 ++- clippy_lints/src/methods/filter_map.rs | 3 ++- clippy_lints/src/methods/filter_map_flat_map.rs | 3 ++- clippy_lints/src/methods/filter_map_identity.rs | 3 ++- clippy_lints/src/methods/filter_map_map.rs | 3 ++- clippy_lints/src/methods/filter_map_next.rs | 3 ++- clippy_lints/src/methods/filter_next.rs | 3 ++- clippy_lints/src/methods/flat_map_identity.rs | 3 ++- clippy_lints/src/methods/from_iter_instead_of_collect.rs | 3 ++- clippy_lints/src/methods/get_unwrap.rs | 3 ++- clippy_lints/src/methods/implicit_clone.rs | 2 +- clippy_lints/src/methods/inefficient_to_string.rs | 3 ++- clippy_lints/src/methods/inspect_for_each.rs | 3 ++- clippy_lints/src/methods/into_iter_on_ref.rs | 3 ++- clippy_lints/src/methods/iter_cloned_collect.rs | 2 +- clippy_lints/src/methods/iter_count.rs | 3 ++- clippy_lints/src/methods/iter_next_slice.rs | 3 ++- clippy_lints/src/methods/iter_nth.rs | 2 +- clippy_lints/src/methods/iter_nth_zero.rs | 3 ++- clippy_lints/src/methods/iter_skip_next.rs | 3 ++- clippy_lints/src/methods/iterator_step_by_zero.rs | 3 ++- clippy_lints/src/methods/manual_saturating_arithmetic.rs | 3 ++- clippy_lints/src/methods/map_collect_result_unit.rs | 3 ++- clippy_lints/src/methods/map_flatten.rs | 3 ++- clippy_lints/src/methods/map_unwrap_or.rs | 3 ++- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/methods/ok_expect.rs | 2 +- clippy_lints/src/methods/option_as_ref_deref.rs | 3 ++- clippy_lints/src/methods/option_map_or_none.rs | 3 ++- clippy_lints/src/methods/option_map_unwrap_or.rs | 3 ++- clippy_lints/src/methods/or_fun_call.rs | 3 ++- clippy_lints/src/methods/search_is_some.rs | 3 ++- clippy_lints/src/methods/single_char_insert_string.rs | 2 +- clippy_lints/src/methods/single_char_pattern.rs | 2 +- clippy_lints/src/methods/single_char_push_string.rs | 2 +- clippy_lints/src/methods/skip_while_next.rs | 3 ++- clippy_lints/src/methods/string_extend_chars.rs | 3 ++- clippy_lints/src/methods/suspicious_map.rs | 3 ++- clippy_lints/src/methods/uninit_assumed_init.rs | 3 ++- clippy_lints/src/methods/unnecessary_filter_map.rs | 3 ++- clippy_lints/src/methods/unnecessary_fold.rs | 3 ++- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 2 +- clippy_lints/src/methods/unwrap_used.rs | 2 +- clippy_lints/src/methods/useless_asref.rs | 3 ++- clippy_lints/src/methods/wrong_self_convention.rs | 2 +- clippy_lints/src/methods/zst_offset.rs | 2 +- clippy_lints/src/minmax.rs | 3 ++- clippy_lints/src/misc.rs | 4 ++-- clippy_lints/src/misc_early.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 3 ++- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 3 ++- clippy_lints/src/multiple_crate_versions.rs | 3 ++- clippy_lints/src/mut_key.rs | 3 ++- clippy_lints/src/mut_mut.rs | 3 ++- clippy_lints/src/mut_mutex_lock.rs | 2 +- clippy_lints/src/mut_reference.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 3 ++- clippy_lints/src/mutex_atomic.rs | 2 +- clippy_lints/src/needless_arbitrary_self_type.rs | 3 ++- clippy_lints/src/needless_bool.rs | 3 ++- clippy_lints/src/needless_borrow.rs | 3 ++- clippy_lints/src/needless_borrowed_ref.rs | 2 +- clippy_lints/src/needless_continue.rs | 3 +-- clippy_lints/src/needless_pass_by_value.rs | 3 ++- clippy_lints/src/needless_question_mark.rs | 3 ++- clippy_lints/src/needless_update.rs | 2 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 3 ++- clippy_lints/src/neg_multiply.rs | 2 +- clippy_lints/src/new_without_default.rs | 3 ++- clippy_lints/src/no_effect.rs | 2 +- clippy_lints/src/non_copy_const.rs | 3 ++- clippy_lints/src/non_expressive_names.rs | 2 +- clippy_lints/src/open_options.rs | 3 ++- clippy_lints/src/option_env_unwrap.rs | 3 ++- clippy_lints/src/option_if_let_else.rs | 3 ++- clippy_lints/src/overflow_check_conditional.rs | 3 ++- clippy_lints/src/panic_in_result_fn.rs | 3 ++- clippy_lints/src/panic_unimplemented.rs | 3 ++- clippy_lints/src/partialeq_ne_impl.rs | 3 ++- clippy_lints/src/pass_by_ref_or_value.rs | 3 ++- clippy_lints/src/path_buf_push_overwrite.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 3 ++- clippy_lints/src/precedence.rs | 2 +- clippy_lints/src/ptr.rs | 3 ++- clippy_lints/src/ptr_eq.rs | 3 ++- clippy_lints/src/ptr_offset_with_cast.rs | 2 +- clippy_lints/src/question_mark.rs | 3 ++- clippy_lints/src/ranges.rs | 6 ++---- clippy_lints/src/redundant_clone.rs | 3 ++- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/redundant_else.rs | 2 +- clippy_lints/src/redundant_field_names.rs | 3 ++- clippy_lints/src/redundant_pub_crate.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 3 +-- clippy_lints/src/redundant_static_lifetimes.rs | 3 ++- clippy_lints/src/ref_option_ref.rs | 3 ++- clippy_lints/src/reference.rs | 3 ++- clippy_lints/src/regex.rs | 3 ++- clippy_lints/src/repeat_once.rs | 3 ++- clippy_lints/src/returns.rs | 3 ++- clippy_lints/src/self_assignment.rs | 3 ++- clippy_lints/src/semicolon_if_nothing_returned.rs | 3 ++- clippy_lints/src/serde_api.rs | 3 ++- clippy_lints/src/shadow.rs | 3 ++- clippy_lints/src/single_component_path_imports.rs | 3 ++- clippy_lints/src/size_of_in_element_count.rs | 3 ++- clippy_lints/src/slow_vector_initialization.rs | 3 ++- clippy_lints/src/stable_sort_primitive.rs | 3 ++- clippy_lints/src/strings.rs | 6 ++---- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/suspicious_trait_impl.rs | 3 ++- clippy_lints/src/swap.rs | 3 ++- clippy_lints/src/tabs_in_doc_comments.rs | 2 +- clippy_lints/src/temporary_assignment.rs | 3 ++- clippy_lints/src/to_digit_is_some.rs | 3 ++- clippy_lints/src/to_string_in_display.rs | 3 ++- clippy_lints/src/trait_bounds.rs | 3 ++- clippy_lints/src/transmute/crosspointer_transmute.rs | 2 +- clippy_lints/src/transmute/transmute_float_to_int.rs | 3 ++- clippy_lints/src/transmute/transmute_int_to_bool.rs | 3 ++- clippy_lints/src/transmute/transmute_int_to_char.rs | 3 ++- clippy_lints/src/transmute/transmute_int_to_float.rs | 3 ++- clippy_lints/src/transmute/transmute_ptr_to_ptr.rs | 3 ++- clippy_lints/src/transmute/transmute_ptr_to_ref.rs | 3 ++- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 3 ++- .../src/transmute/transmutes_expressible_as_ptr_casts.rs | 3 ++- clippy_lints/src/transmute/unsound_collection_transmute.rs | 3 ++- clippy_lints/src/transmute/useless_transmute.rs | 3 ++- clippy_lints/src/transmute/wrong_transmute.rs | 2 +- clippy_lints/src/transmuting_null.rs | 3 ++- clippy_lints/src/try_err.rs | 3 ++- clippy_lints/src/types/borrowed_box.rs | 3 ++- clippy_lints/src/types/box_vec.rs | 3 ++- clippy_lints/src/types/linked_list.rs | 3 ++- clippy_lints/src/types/mod.rs | 6 ++---- clippy_lints/src/types/option_option.rs | 3 ++- clippy_lints/src/types/rc_buffer.rs | 3 ++- clippy_lints/src/types/redundant_allocation.rs | 3 ++- clippy_lints/src/types/vec_box.rs | 3 ++- clippy_lints/src/undropped_manually_drops.rs | 3 ++- clippy_lints/src/unicode.rs | 3 ++- clippy_lints/src/unit_return_expecting_ord.rs | 3 ++- clippy_lints/src/unnamed_address.rs | 3 ++- clippy_lints/src/unnecessary_sort_by.rs | 6 +++--- clippy_lints/src/unnecessary_wraps.rs | 5 ++--- clippy_lints/src/unnested_or_patterns.rs | 3 ++- clippy_lints/src/unsafe_removed_from_name.rs | 2 +- clippy_lints/src/unused_io_amount.rs | 3 ++- clippy_lints/src/unused_self.rs | 2 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/unwrap.rs | 3 ++- clippy_lints/src/unwrap_in_result.rs | 3 ++- clippy_lints/src/upper_case_acronyms.rs | 4 ++-- clippy_lints/src/use_self.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 5 ++--- clippy_lints/src/utils/internal_lints.rs | 7 +++---- clippy_lints/src/vec.rs | 3 ++- clippy_lints/src/vec_init_then_push.rs | 3 ++- clippy_lints/src/vec_resize_to_zero.rs | 2 +- clippy_lints/src/verbose_file_reads.rs | 3 ++- clippy_lints/src/wildcard_dependencies.rs | 3 ++- clippy_lints/src/wildcard_imports.rs | 3 ++- clippy_lints/src/write.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 2 +- clippy_lints/src/zero_sized_map_values.rs | 3 ++- clippy_utils/src/lib.rs | 3 +-- 307 files changed, 530 insertions(+), 368 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 1d511a86c90..3d04abe094d 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index 61fdf9495b9..c560f545d6a 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -1,5 +1,5 @@ use crate::consts::constant_simple; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index c30d65bbc57..4b31e16094e 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -1,10 +1,9 @@ +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** Checks for usage of `as` conversions. /// diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs index ef1f1a14afc..b970c71b753 100644 --- a/clippy_lints/src/asm_syntax.rs +++ b/clippy_lints/src/asm_syntax.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::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}; diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 3b6f64e7769..bae4d4957a9 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, span_lint_and_help}; +use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, UnOp}; diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 031827e7e89..8581e176e0c 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,5 +1,6 @@ -use crate::utils::{eq_expr_value, get_trait_def_id, span_lint_and_then, trait_ref_of_method}; +use crate::utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method}; use crate::utils::{higher, sugg}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use if_chain::if_chain; diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index a5ed6016874..e6c7c68f91a 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 703d8a6f62b..231e74e16c0 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, span_lint_and_help}; +use crate::utils::match_def_path; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 04b0e71e4a3..6be6722375d 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,6 +1,7 @@ //! checks for attributes -use crate::utils::{match_panic_def_id, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::match_panic_def_id; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 14b6a156c62..3a3296c2c3d 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index a4ee54076ee..2b8400344f5 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; use crate::utils::sugg::Sugg; -use crate::utils::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/blacklisted_name.rs b/clippy_lints/src/blacklisted_name.rs index 153870fb416..b26ef33e056 100644 --- a/clippy_lints/src/blacklisted_name.rs +++ b/clippy_lints/src/blacklisted_name.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index f43f98d1dc0..343c4821c56 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, get_parent_expr, span_lint, span_lint_and_sugg}; +use crate::utils::{differing_macro_contexts, get_parent_expr}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; use if_chain::if_chain; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 12a07c60438..1303613b8c4 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,4 +1,5 @@ -use crate::utils::{eq_expr_value, get_trait_def_id, in_macro, paths, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{eq_expr_value, get_trait_def_id, in_macro, paths}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index d02861b1b1e..c1ca787c1e5 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,4 +1,5 @@ -use crate::utils::{contains_name, get_pat_name, paths, single_segment_path, span_lint_and_sugg}; +use crate::utils::{contains_name, get_pat_name, paths, single_segment_path}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; use if_chain::if_chain; diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index cc2869ab495..f0ef482164a 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -2,7 +2,8 @@ use std::path::PathBuf; -use crate::utils::{run_lints, span_lint}; +use crate::utils::run_lints; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index b15fe65352a..c9ef379be56 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind, PathSegment}; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 62ba19c0e16..4b660188d82 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; @@ -5,7 +6,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::{in_constant, span_lint_and_sugg}; +use crate::utils::in_constant; use super::{utils, CAST_LOSSLESS}; diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 522ef5348be..75d7c55b637 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -3,7 +3,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use super::{utils, CAST_POSSIBLE_TRUNCATION}; diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 931252415ad..2c5c1d7cb46 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,10 +1,9 @@ +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use crate::utils::span_lint; - use super::{utils, CAST_POSSIBLE_WRAP}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/cast_precision_loss.rs b/clippy_lints/src/casts/cast_precision_loss.rs index b6905c21c78..63ac8fd2dd2 100644 --- a/clippy_lints/src/casts/cast_precision_loss.rs +++ b/clippy_lints/src/casts/cast_precision_loss.rs @@ -1,10 +1,9 @@ +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::span_lint; - use super::{utils, CAST_PRECISION_LOSS}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 87fb5557be0..6a45ec61c54 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -6,7 +7,7 @@ use rustc_target::abi::LayoutOf; use if_chain::if_chain; -use crate::utils::{is_hir_ty_cfg_dependant, span_lint}; +use crate::utils::is_hir_ty_cfg_dependant; use super::CAST_PTR_ALIGNMENT; diff --git a/clippy_lints/src/casts/cast_ref_to_mut.rs b/clippy_lints/src/casts/cast_ref_to_mut.rs index 3fdc1c6168b..d9bf1ea58b9 100644 --- a/clippy_lints/src/casts/cast_ref_to_mut.rs +++ b/clippy_lints/src/casts/cast_ref_to_mut.rs @@ -1,11 +1,9 @@ +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; -use if_chain::if_chain; - -use crate::utils::span_lint; - use super::CAST_REF_TO_MUT; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 9656fbebd77..2965a840a82 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -5,7 +5,8 @@ use rustc_middle::ty::{self, Ty}; use if_chain::if_chain; use crate::consts::{constant, Constant}; -use crate::utils::{method_chain_args, sext, span_lint}; +use crate::utils::{method_chain_args, sext}; +use clippy_utils::diagnostics::span_lint; use super::CAST_SIGN_LOSS; diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 75aa559359c..099a0de881f 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::LitKind; @@ -6,8 +7,6 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, UintTy}; -use crate::utils::span_lint_and_then; - use super::CHAR_LIT_AS_U8; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index 723bfa5befe..35350d8a25b 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index a3108f8a983..6287f479b5b 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index abfbadf3642..2c25f865cb8 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -8,8 +8,9 @@ use rustc_semver::RustcVersion; use if_chain::if_chain; +use crate::utils::meets_msrv; use crate::utils::sugg::Sugg; -use crate::utils::{meets_msrv, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use super::PTR_AS_PTR; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index eb0ee012e6c..a84e658024a 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; @@ -7,7 +8,7 @@ use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; -use crate::utils::{numeric_literal::NumericLiteral, span_lint_and_sugg}; +use crate::utils::numeric_literal::NumericLiteral; use super::UNNECESSARY_CAST; diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 4f3daa427e3..8b62e315e04 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,5 +1,6 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -10,7 +11,7 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{meets_msrv, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{meets_msrv, SpanlessEq}; const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 5b8ef01505b..8134f3af3ef 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,6 @@ //! calculate cognitive complexity and warn about overly complex functions +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::Attribute; @@ -11,7 +12,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::{sym, BytePos}; -use crate::utils::{span_lint_and_help, LimitStack}; +use crate::utils::LimitStack; declare_clippy_lint! { /// **What it does:** Checks for methods with high cognitive complexity. diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index c866f18ef3e..dbe70f90732 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -12,6 +12,7 @@ //! //! This lint is **warn** by default +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; use if_chain::if_chain; use rustc_ast::ast; @@ -20,7 +21,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::sugg::Sugg; -use crate::utils::{span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 3c45525684b..5ff2518bd94 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,5 +1,6 @@ use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{path_to_local, span_lint_and_then, SpanlessEq}; +use crate::utils::{path_to_local, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 9843033a2e0..5411905791c 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq}; +use crate::utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 944aaafb46d..f86f0da6d6f 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,5 +1,6 @@ use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, if_sequence, span_lint_and_note}; +use crate::utils::{get_parent_expr, if_sequence}; +use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 434d8958da5..35079c6bedc 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::ty::is_copy; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 0785e25b0a2..01ca2837a99 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_sugg}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 3171a945eb0..286cc7e223e 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::snippet_opt; use rustc_ast::ast; use rustc_ast::tokenstream::TokenStream; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 7d975b5a3d9..0b074c088d4 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,5 +1,5 @@ use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; -use crate::utils::{span_lint_and_note, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_macro_callsite; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index e58dcb942c6..d136db9373c 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; @@ -13,8 +14,6 @@ use rustc_middle::{ }; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_sugg; - declare_clippy_lint! { /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type /// inference. diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index e112338dfea..62ec2f8ce48 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_node, in_macro, is_allowed, span_lint_and_sugg}; +use crate::utils::{get_parent_node, in_macro, is_allowed}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::peel_mid_ty_refs; use rustc_ast::util::parser::PREC_PREFIX; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 460fe385272..a7f2106e357 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,8 +1,6 @@ use crate::utils::paths; -use crate::utils::{ - get_trait_def_id, is_allowed, is_automatically_derived, match_def_path, span_lint_and_help, span_lint_and_note, - span_lint_and_then, -}; +use crate::utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_hir::def_id::DefId; diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 56dc6d18a58..16023bc53b0 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,4 +1,5 @@ -use crate::utils::{fn_def_id, span_lint}; +use crate::utils::fn_def_id; +use clippy_utils::diagnostics::span_lint; use rustc_data_structures::fx::FxHashSet; use rustc_hir::Expr; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 4f3c573691e..d9952325c4b 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty, span_lint, span_lint_and_note, -}; +use crate::utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use itertools::Itertools; diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 3de027b2cc6..549720feb2d 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -1,5 +1,6 @@ //! Lint on unnecessary double comparisons. Some examples: +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -7,7 +8,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{eq_expr_value, span_lint_and_sugg}; +use crate::utils::eq_expr_value; declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index abbcaf43f41..5afdcb3c09f 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index dd145ba6867..fe86adcec45 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 195143d720d..40c75f568ae 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -9,7 +9,7 @@ use rustc_span::source_map::Spanned; use crate::consts::{constant, Constant}; use crate::utils::paths; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 95123e6ff6f..26984df9539 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -1,12 +1,11 @@ //! Lint on if expressions with an else if, but without a final else branch. +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** Checks for usage of if expressions with an `else if` branch, /// but without a final `else` branch. diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 077c3b75fb8..c92984a9834 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -1,6 +1,6 @@ //! lint when there is an enum with no variants -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 93723219594..cc774831491 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,6 +1,6 @@ -use crate::utils::span_lint_and_then; use crate::utils::SpanlessEq; use crate::utils::{get_item_name, paths}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index aa235642ac3..7a98ae39d3a 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -2,7 +2,7 @@ //! don't fit into an `i32` use crate::consts::{miri_to_const, Constant}; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::util::IntTypeExt; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 660131cf176..d2d8acb443b 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -1,7 +1,7 @@ //! lint on enum variants that are prefixed or suffixed by the same characters use crate::utils::camel_case; -use crate::utils::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::is_present_in_source; use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 0a5917c10ea..1b2f66fb628 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,7 +1,5 @@ -use crate::utils::{ - ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, multispan_sugg, span_lint, - span_lint_and_then, -}; +use crate::utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index dbd1ff514f0..59602616781 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -1,10 +1,10 @@ +use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{constant_simple, Constant}; -use crate::utils::span_lint; declare_clippy_lint! { /// **What it does:** Checks for erasing operations, e.g., `x * 0`. diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index a7258eea0ad..06da14a9844 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::contains_ty; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node}; @@ -11,8 +12,6 @@ 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; - #[derive(Copy, Clone)] pub struct BoxedLocal { pub too_large_for_stack: u64, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index e8d5b992b63..2557e9f2796 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; @@ -10,7 +11,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_adjusted, iter_input_pats, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{is_adjusted, iter_input_pats}; declare_clippy_lint! { /// **What it does:** Checks for closures which just call another function where diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 83cee11c3a8..3711ba4a7ee 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_expr, path_to_local, path_to_local_id, span_lint, span_lint_and_note}; +use crate::utils::{get_parent_expr, path_to_local, path_to_local_id}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 6a85b57af07..683e0435351 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,4 +1,5 @@ -use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help}; +use crate::utils::{attr_by_name, in_macro, match_path_ast}; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 5e31072523d..60ad2e8ee14 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,14 +1,12 @@ +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; use if_chain::if_chain; - use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::span_lint_and_then; - declare_clippy_lint! { /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 91585927000..adfb644b199 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint}; +use crate::utils::{is_entrypoint_fn, match_def_path, paths}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index f8038d06e50..0861cba4675 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg}; +use crate::utils::{is_expn_of, match_function_call, paths}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index cc4e570956b..5efe465a667 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, match_panic_def_id, method_chain_args, span_lint_and_then}; +use crate::utils::{is_expn_of, match_panic_def_id, method_chain_args}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index c1c08597ee6..b3593d328cc 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_then, sugg}; +use crate::utils::{match_def_path, paths, sugg}; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 8e256f34684..6446fe62a64 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,4 +1,5 @@ -use crate::utils::{numeric_literal, span_lint_and_sugg}; +use crate::utils::numeric_literal; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 086a791520f..8b53384ecbd 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,8 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, span_lint_and_sugg, sugg}; +use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index c0048bb2175..db27225707d 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,5 +1,6 @@ use crate::utils::paths; -use crate::utils::{is_expn_of, last_path_segment, match_def_path, match_function_call, span_lint_and_then}; +use crate::utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 590de04717a..4dadd2ba936 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, span_lint_and_help, span_lint_and_note}; +use crate::utils::differing_macro_contexts; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index b644bb07990..5eea4d14759 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,5 +1,6 @@ use crate::utils::paths::INTO; -use crate::utils::{match_def_path, meets_msrv, span_lint_and_help}; +use crate::utils::{match_def_path, meets_msrv}; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index b92c8ccfb1b..b9a44f26381 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -7,7 +8,6 @@ use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; -use crate::utils::span_lint_and_sugg; use crate::utils::sugg::Sugg; declare_clippy_lint! { diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 104c692dcec..37288d7a0b2 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,7 +1,8 @@ use crate::utils::{ attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, - path_to_local, return_ty, span_lint, span_lint_and_help, span_lint_and_then, trait_ref_of_method, + path_to_local, return_ty, trait_ref_of_method, }; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; use if_chain::if_chain; diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 9e1a8864a3e..36960e7f51b 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -1,4 +1,5 @@ use crate::utils; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; @@ -84,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { fulfillment_cx.select_all_or_error(&infcx) }); if let Err(send_errors) = send_result { - utils::span_lint_and_then( + span_lint_and_then( cx, FUTURE_NOT_SEND, span, diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index 875cd33bc8f..6f14ede0ecb 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -1,6 +1,7 @@ //! lint on using `x.get(x.len() - 1)` instead of `x.last()` -use crate::utils::{span_lint_and_sugg, SpanlessEq}; +use crate::utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index fc93864c74a..385ea020328 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -7,7 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{constant_simple, Constant}; -use crate::utils::{clip, span_lint, unsext}; +use crate::utils::{clip, unsext}; +use clippy_utils::diagnostics::span_lint; declare_clippy_lint! { /// **What it does:** Checks for identity operations, e.g., `x + 0`. diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index f53d1b1cf3b..c2ec8b3ffd1 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,4 +1,5 @@ -use crate::utils::{span_lint_and_help, SpanlessEq}; +use crate::utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 9f7ca95a8f3..59094d1147a 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -1,4 +1,5 @@ -use crate::utils::{method_chain_args, span_lint_and_sugg}; +use crate::utils::method_chain_args; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index b86d2e76656..c56f67df061 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -1,13 +1,12 @@ //! lint on if branches that could be swapped so no `!` operation is necessary //! on the condition +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** Checks for usage of `!` or `!=` in an if condition with an /// else branch. diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 7e1807786ee..c4e87300b7b 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,4 +1,5 @@ use crate::utils; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; @@ -100,7 +101,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone { cond_snip, closure_body, ); - utils::span_lint_and_help( + span_lint_and_help( cx, IF_THEN_SOME_ELSE_NONE, expr.span, diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index e86bd49251d..70ea9a92279 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_panic_def_id, span_lint_and_then}; +use crate::utils::match_panic_def_id; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 16e162badb5..3d2798f3fb4 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, match_qpath, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{in_macro, match_qpath, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 4762d5d40f3..b7986da7820 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; @@ -7,8 +8,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; -use crate::utils::span_lint_and_sugg; - declare_clippy_lint! { /// **What it does:** Checks for struct constructors where all fields are shorthand and /// the order of the field init shorthand in the constructor is inconsistent diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index c919ec097a2..4c0f976f79a 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,7 +1,8 @@ //! lint on indexing and slicing operations use crate::consts::{constant, Constant}; -use crate::utils::{higher, span_lint, span_lint_and_help}; +use crate::utils::higher; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 6bce205ec3a..addd95c6eae 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,9 +1,10 @@ +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{implements_trait, match_type}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{get_trait_def_id, higher, match_qpath, paths, span_lint}; +use crate::utils::{get_trait_def_id, higher, match_qpath, paths}; declare_clippy_lint! { /// **What it does:** Checks for iteration that is guaranteed to be infinite. diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 005c461f105..f838ce190dc 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,6 +1,7 @@ //! lint on inherent implementations -use crate::utils::{in_macro, span_lint_and_then}; +use crate::utils::in_macro; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 9207413cd69..6b29feeb4ed 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::{ImplItem, ImplItemKind}; @@ -5,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::{get_trait_def_id, paths, return_ty, span_lint_and_help, trait_ref_of_method}; +use crate::utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; declare_clippy_lint! { /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 00acbd6cc3f..87fe5123cf6 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -1,7 +1,7 @@ //! checks for `#[inline]` on trait methods without bodies -use crate::utils::span_lint_and_then; use crate::utils::sugg::DiagnosticBuilderExt; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 9eae653dd67..c4a1222b51f 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -1,13 +1,12 @@ //! lint on blocks unnecessarily using >= with a + 1 or - 1 +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::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 /// diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index 39b4605e72f..e5482f675e7 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 0927d218446..c69571f32a2 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -1,6 +1,6 @@ //! lint when items are used after statements -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index a76595ed089..48dc5fefe99 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -1,5 +1,5 @@ use crate::rustc_target::abi::LayoutOf; -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index cbb5192bfd9..76584dc1822 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,6 +1,6 @@ //! lint when there is a large size difference between variants on an enum -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index ceae4243e63..c46b98022c6 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; @@ -7,7 +8,6 @@ use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use crate::rustc_target::abi::LayoutOf; -use crate::utils::span_lint_and_help; declare_clippy_lint! { /// **What it does:** Checks for local arrays that may be too large. diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e758a269fbe..a644a4113ae 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_item_name, get_parent_as_impl, is_allowed, span_lint, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{get_item_name, get_parent_as_impl, is_allowed}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 7059ba21207..4b7e02398fb 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,4 +1,5 @@ -use crate::utils::{path_to_local_id, span_lint_and_then, visitors::LocalUsedVisitor}; +use crate::utils::{path_to_local_id, visitors::LocalUsedVisitor}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 2da7137b48b..eee2503b481 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_must_use_ty, match_type}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; @@ -6,7 +7,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, paths, span_lint_and_help}; +use crate::utils::{is_must_use_func_call, paths}; declare_clippy_lint! { /// **What it does:** Checks for `let _ = ` diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 33ff01a30e8..0848aaed815 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint, trait_ref_of_method}; +use crate::utils::{in_macro, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::{ walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 8bc7bf37ef1..dd303bc0a01 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -4,8 +4,8 @@ use crate::utils::{ in_macro, numeric_literal::{NumericLiteral, Radix}, - span_lint_and_sugg, }; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; diff --git a/clippy_lints/src/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs index 43e85538f28..d52a061fc8f 100644 --- a/clippy_lints/src/loops/empty_loop.rs +++ b/clippy_lints/src/loops/empty_loop.rs @@ -1,5 +1,6 @@ use super::EMPTY_LOOP; -use crate::utils::{is_in_panic_handler, is_no_std_crate, span_lint_and_help}; +use crate::utils::{is_in_panic_handler, is_no_std_crate}; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 1f6d48fe915..87ce4e4c20c 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -1,7 +1,8 @@ use super::{ get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP, }; -use crate::utils::{get_enclosing_block, is_integer_const, span_lint_and_sugg}; +use crate::utils::{get_enclosing_block, is_integer_const}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index e5b3dc7aad7..4871a031187 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,5 +1,5 @@ use super::EXPLICIT_INTO_ITER_LOOP; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index d2000d80ac1..259e95e5ef4 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,5 +1,6 @@ use super::EXPLICIT_ITER_LOOP; -use crate::utils::{match_trait_method, paths, span_lint_and_sugg}; +use crate::utils::{match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 19a68dd78d1..bf037f392e0 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,6 +1,7 @@ use super::FOR_KV_MAP; use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{multispan_sugg, paths, span_lint_and_then, sugg}; +use crate::utils::{paths, sugg}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs index 5140448592d..d49b0517dcf 100644 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ b/clippy_lints/src/loops/for_loops_over_fallibles.rs @@ -1,8 +1,7 @@ +use super::FOR_LOOPS_OVER_FALLIBLES; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; - -use super::FOR_LOOPS_OVER_FALLIBLES; -use crate::utils::span_lint_and_help; use rustc_hir::{Expr, Pat}; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index d6b40fa9fa8..27a03562800 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -1,9 +1,10 @@ use super::ITER_NEXT_LOOP; +use clippy_utils::diagnostics::span_lint; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -use crate::utils::{is_trait_method, span_lint}; +use crate::utils::is_trait_method; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool { if is_trait_method(cx, arg, sym::Iterator) { diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 3d3ae6f3152..43580d6071a 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,6 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; -use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id, span_lint_and_then}; +use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index c9c25641160..ff68d136999 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,6 +1,7 @@ use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use crate::utils::sugg::Sugg; -use crate::utils::{get_enclosing_block, higher, path_to_local, span_lint_and_sugg, sugg}; +use crate::utils::{get_enclosing_block, higher, path_to_local, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 3ae592950f1..175aee53a9a 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,5 +1,6 @@ use super::MUT_RANGE_BOUND; -use crate::utils::{higher, path_to_local, span_lint}; +use crate::utils::{higher, path_to_local}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 59f7b23af75..a3b731eefa0 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,6 +1,7 @@ use super::NEEDLESS_COLLECT; use crate::utils::sugg::Sugg; -use crate::utils::{is_trait_method, path_to_local_id, paths, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{is_trait_method, path_to_local_id, paths}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 674cb34c37b..ca679d7ebe1 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -1,9 +1,9 @@ use super::NEEDLESS_RANGE_LOOP; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ - contains_name, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id, paths, - span_lint_and_then, sugg, SpanlessEq, + contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq, }; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use if_chain::if_chain; diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 45e1001d755..82ec2635aeb 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,5 +1,5 @@ use super::NEVER_LOOP; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind}; use rustc_lint::LateContext; use std::iter::{once, Iterator}; diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 255d6de4a36..849d7ec718c 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,5 +1,5 @@ use super::SAME_ITEM_PUSH; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 4f83191d919..172eb963ae3 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,5 +1,6 @@ use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP}; -use crate::utils::{single_segment_path, span_lint_and_sugg}; +use crate::utils::single_segment_path; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 05e0a722563..a037a06de81 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -1,7 +1,7 @@ use super::WHILE_IMMUTABLE_CONDITION; use crate::consts::constant; -use crate::utils::span_lint_and_then; use crate::utils::usage::mutated_variables; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index dbd9126861f..ffe8c0c5494 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -1,5 +1,5 @@ use super::WHILE_LET_LOOP; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind}; diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index ccabe586c2b..29ad9e735cc 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -3,8 +3,8 @@ use super::WHILE_LET_ON_ITERATOR; use crate::utils::usage::mutated_variables; use crate::utils::{ get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, - span_lint_and_sugg, }; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use if_chain::if_chain; diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 637f10f6609..92f95645734 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; +use crate::utils::in_macro; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index 5db2968e42c..6c0308cbd92 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -1,10 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_hir::{Crate, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{is_entrypoint_fn, is_no_std_crate, span_lint_and_help}; +use crate::utils::{is_entrypoint_fn, is_no_std_crate}; declare_clippy_lint! { /// **What it does:** Checks for recursion using the entrypoint. diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index ebc493c0f7e..1db5cf56962 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,6 @@ +use crate::utils::match_function_call; use crate::utils::paths::FUTURE_FROM_GENERATOR; -use crate::utils::{match_function_call, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 3896645ca7d..fd563bec769 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,8 +1,9 @@ use crate::{ map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF, - utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs, span_lint_and_sugg}, + utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs}, }; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use rustc_ast::util::parser::PREC_POSTFIX; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 0b8c049b466..705c1ec1859 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,5 @@ -use crate::utils::{meets_msrv, span_lint_and_then}; +use crate::utils::meets_msrv; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index f436eccc0dc..a1e5c752f83 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_qpath, path_to_local_id, paths, span_lint_and_sugg}; +use crate::utils::{match_qpath, path_to_local_id, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 3bfca8bea40..bc4e255b03d 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,6 +1,7 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; -use crate::utils::{eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, span_lint_and_then}; +use crate::utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 7a4040539e3..0e030e0e261 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,6 +1,7 @@ use crate::consts::constant_simple; use crate::utils; use crate::utils::{path_to_local_id, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -112,7 +113,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); - utils::span_lint_and_sugg( + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index e10d7647bcf..fd39052871b 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,5 +1,6 @@ use crate::utils::is_trait_method; -use crate::utils::{remove_blocks, span_lint_and_sugg}; +use crate::utils::remove_blocks; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use if_chain::if_chain; diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 76fe8e776ea..a6a63961be5 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,5 +1,4 @@ -use crate::utils::span_lint_and_help; - +use clippy_utils::diagnostics::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}; diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 79570367ed9..84ec23c4e2f 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks, span_lint_and_sugg}; +use crate::utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 24bcc808585..3bd4a965765 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,4 +1,5 @@ -use crate::utils::{iter_input_pats, method_chain_args, span_lint_and_then}; +use crate::utils::{iter_input_pats, method_chain_args}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 57dcd8709b8..ccaa5e98c83 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::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4ae1ce977f4..1aa09b82822 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,11 +2,13 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, multispan_sugg, - path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, span_lint_and_help, - span_lint_and_note, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, + path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, strip_pat_refs, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::diagnostics::{ + multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, +}; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use if_chain::if_chain; @@ -1614,7 +1616,8 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; - use crate::utils::{is_trait_method, match_qpath, paths, span_lint_and_then}; + use crate::utils::{is_trait_method, match_qpath, paths}; + use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index fbdc0cdb2d8..85353d4cdde 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_then}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::walk_ptrs_ty_depth; use if_chain::if_chain; diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs index d34f9761e26..1a8cb82514b 100644 --- a/clippy_lints/src/mem_forget.rs +++ b/clippy_lints/src/mem_forget.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index bf3f6f7f830..87cb66a6770 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,5 @@ -use crate::utils::{ - in_macro, match_def_path, match_qpath, meets_msrv, paths, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, -}; +use crate::utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diagnostic_assoc_item; use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 99b3be67f18..7d6104ebb7f 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,8 +1,6 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; -use crate::utils::{ - in_macro, match_qpath, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, span_lint_and_sugg, - span_lint_and_then, visitors::find_all_ret_expressions, -}; +use crate::utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions}; +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::match_type; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index f81e9a8c524..4f88f80a304 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 954d589a47e..d1c7789a241 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -1,4 +1,5 @@ -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 90ecb2382e7..b49ae42b12f 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,4 +1,5 @@ -use crate::utils::{paths, span_lint_and_sugg}; +use crate::utils::paths; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 9e96d571337..1c673c11d65 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, span_lint_and_sugg}; +use crate::utils::is_expn_of; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 37c41194b4a..64531b29ade 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 791fe3647cb..005c94a6018 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_expr, paths, span_lint_and_help}; +use crate::utils::{get_parent_expr, paths}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs index 42f05aa0b3a..4820bb137c1 100644 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ b/clippy_lints/src/methods/filter_flat_map.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_help}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 964c4903ed2..af91370df20 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, path_to_local_id, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{is_trait_method, path_to_local_id, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs index e113f3f71b1..5294ef97528 100644 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ b/clippy_lints/src/methods/filter_map_flat_map.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_help}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 5f627b42abc..4461baf4f7c 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths, span_lint_and_sugg}; +use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs index 2e704c4c555..8f17350054b 100644 --- a/clippy_lints/src/methods/filter_map_map.rs +++ b/clippy_lints/src/methods/filter_map_map.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_help}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index ed75315b52c..8fbadd1d457 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, meets_msrv, span_lint, span_lint_and_sugg}; +use crate::utils::{is_trait_method, meets_msrv}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 097f9fdf2c4..af16ea19007 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint, span_lint_and_sugg}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 19ddceeccce..669ac1f743f 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, match_qpath, paths, span_lint_and_sugg}; +use crate::utils::{is_trait_method, match_qpath, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 9b46adaac58..5d0a07992c8 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, paths, span_lint_and_sugg, sugg}; +use crate::utils::{get_trait_def_id, paths, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index b9d34b402bb..ac855419d00 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, paths, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index a769493d11d..04461ad5c3a 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 19d05b5c693..56de7a8bc5e 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,5 +1,6 @@ use super::INEFFICIENT_TO_STRING; -use crate::utils::{match_def_path, paths, span_lint_and_then}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index e7c3a433fe1..c9bbfbc37eb 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -1,8 +1,9 @@ +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{source_map::Span, sym}; -use crate::utils::{is_trait_method, span_lint_and_help}; +use crate::utils::is_trait_method; use super::INSPECT_FOR_EACH; diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index f28f082e6fc..a862b55e139 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_trait_method, paths, span_lint_and_sugg}; +use crate::utils::{match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::has_iter_method; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index 1e1d3fdcf70..cd575c9cb02 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,5 +1,5 @@ use crate::methods::derefs_to_slice; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 0f393423b7d..bfd62ff1f0d 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{paths, span_lint_and_sugg}; +use crate::utils::paths; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index f79942576da..ce599682bbe 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,5 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, higher, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, higher}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 17600bb8153..2619793e1e2 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,6 +1,6 @@ use crate::methods::derefs_to_slice; use crate::methods::iter_nth_zero; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 98ddfdfdf9c..f2b2dd8c097 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_trait_method, span_lint_and_sugg}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index d191ea0a831..6100613bcac 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_sugg}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 019a08f746e..51c1ab0484e 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_trait_method, span_lint}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index d090a35a3cf..0c8a002e359 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_qpath, span_lint_and_sugg}; +use crate::utils::match_qpath; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index 349b26b9d58..b59998fc8b4 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_sugg}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 56719b3cff2..6f5e723fbfb 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_sugg}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 96dbc7ddc63..8ef02b4a641 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,5 +1,6 @@ +use crate::utils::meets_msrv; use crate::utils::usage::mutated_variables; -use crate::utils::{meets_msrv, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 47617e4722e..af8fe7abd96 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -52,6 +52,7 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use if_chain::if_chain; @@ -69,8 +70,7 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::{ contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls, - method_chain_args, paths, return_ty, single_segment_path, span_lint, span_lint_and_help, span_lint_and_sugg, - SpanlessEq, + method_chain_args, paths, return_ty, single_segment_path, SpanlessEq, }; declare_clippy_lint! { diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index 2618d04b242..e6ce9cac397 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 6597e9f96a8..f921d7b16c0 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, span_lint_and_sugg}; +use crate::utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index eed71d02467..e8b057c2d74 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_qpath, paths, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 398d8f13bd4..c6459648cd5 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, span_lint_and_then}; +use crate::utils::differing_macro_contexts; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 634feebe54a..1b43802a08e 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,5 +1,6 @@ use crate::utils::eager_or_lazy::is_lazyness_candidate; -use crate::utils::{contains_return, get_trait_def_id, last_path_segment, paths, span_lint_and_sugg}; +use crate::utils::{contains_return, get_trait_def_id, last_path_segment, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 6054579d988..18e1064b018 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_help, span_lint_and_sugg, strip_pat_refs}; +use crate::utils::{is_trait_method, strip_pat_refs}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index ff67564b39d..cd87d2c56b7 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -1,5 +1,5 @@ use crate::methods::get_hint_if_single_char_arg; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index 61cbc9d2f0a..12d6418d700 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -1,5 +1,5 @@ use crate::methods::get_hint_if_single_char_arg; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 18df90c1ab3..882703a987d 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -1,5 +1,5 @@ use crate::methods::get_hint_if_single_char_arg; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index a9ff78c3260..8995226191b 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, span_lint_and_help}; +use crate::utils::is_trait_method; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 1b26e8314af..d9b97168490 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,4 +1,5 @@ -use crate::utils::{method_chain_args, span_lint_and_sugg}; +use crate::utils::method_chain_args; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 0ffa71de30c..fd4651dd182 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,5 +1,6 @@ use crate::utils::usage::mutated_variables; -use crate::utils::{expr_or_init, is_trait_method, span_lint_and_help}; +use crate::utils::{expr_or_init, is_trait_method}; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 798b66192c8..071856c3ba6 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, match_qpath, paths, span_lint}; +use crate::utils::{match_def_path, match_qpath, paths}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 686874c0a24..670134c68f2 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,5 +1,6 @@ use crate::utils::usage::mutated_variables; -use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths, span_lint}; +use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 4d5cbdd619d..3ccde57de3f 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_trait_method, path_to_local_id, remove_blocks, span_lint_and_sugg, strip_pat_refs}; +use crate::utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 444abde3d0f..3874673bf9f 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,5 +1,5 @@ -use crate::utils::span_lint_and_sugg; use crate::utils::{eager_or_lazy, usage}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index efe43a6a952..2f5806115bd 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index c6d84aedc0a..13c95e33fef 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_expr, match_trait_method, paths, span_lint_and_sugg}; +use crate::utils::{get_parent_expr, match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index c8bcad7be3e..b728d7d8d08 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,5 +1,5 @@ use crate::methods::SelfKind; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs index f1335726736..9f6a7c4db17 100644 --- a/clippy_lints/src/methods/zst_offset.rs +++ b/clippy_lints/src/methods/zst_offset.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 8d0c3b8e0fe..6a4e70e50af 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, match_trait_method, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index f161054cc8c..03d07287005 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::implements_trait; use if_chain::if_chain; @@ -20,8 +21,7 @@ use crate::consts::{constant, Constant}; use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, - last_path_segment, match_qpath, span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then, unsext, - SpanlessEq, + last_path_segment, match_qpath, unsext, SpanlessEq, }; declare_clippy_lint! { diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 6ec523498e1..3c6a7071c24 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 4cc20b8d38c..aba4accccb1 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,5 +1,6 @@ use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method}; +use crate::utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::has_drop; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 6ec4c38d0f9..fd6f31faa94 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -5,7 +5,7 @@ // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_ast::attr; diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index da59c820999..dd4488f3f02 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::{self, LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index da3ae1d652f..1b00cf2c75d 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{sext, span_lint_and_then}; +use crate::utils::sext; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index c1773cef7a8..6eaeaebe781 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -1,6 +1,7 @@ //! lint on multiple versions of a crate being used -use crate::utils::{run_lints, span_lint}; +use crate::utils::run_lints; +use clippy_utils::diagnostics::span_lint; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{Crate, CRATE_HIR_ID}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 908b7bb7ce0..39aec79734c 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; +use crate::utils::{match_def_path, paths, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeFoldable; diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d7239b328bb..c11ccc94890 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,4 +1,5 @@ -use crate::utils::{higher, span_lint}; +use crate::utils::higher; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index e4a57661f97..b9ba74c7d02 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 3f0b765df15..0c09ddb8073 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 9ac127abe0a..fb8895e08d0 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,4 +1,5 @@ -use crate::utils::{higher, is_direct_expn_of, span_lint}; +use crate::utils::{higher, is_direct_expn_of}; +use clippy_utils::diagnostics::span_lint; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 9f746ce2e1a..354e2c3fb74 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -2,7 +2,7 @@ //! //! This lint is **warn** by default -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 7687962bdd9..c62ff7323f3 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; +use crate::utils::in_macro; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 22adbdf09a6..4c67304d23e 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,8 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{is_expn_of, parent_node_is_if_expr, span_lint, span_lint_and_sugg}; +use crate::utils::{is_expn_of, parent_node_is_if_expr}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index d8b574af1fe..72436b51dcb 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -2,7 +2,8 @@ //! //! This lint is **warn** by default -use crate::utils::{is_automatically_derived, span_lint_and_then}; +use crate::utils::is_automatically_derived; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 5ee71f25694..7fbffe04a3f 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 4ff90704207..91c97ef7c2a 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -33,6 +33,7 @@ //! ``` //! //! This lint is **warn** by default. +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; use rustc_ast::ast; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -40,8 +41,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::Span; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** The lint checks for `if`-statements appearing in loops /// that contain a `continue` statement in either their main blocks or their diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 6f7a5d85480..fc8ee6cb8ef 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,5 +1,6 @@ use crate::utils::ptr::get_spans; -use crate::utils::{get_trait_def_id, is_self, multispan_sugg, paths, span_lint_and_then}; +use crate::utils::{get_trait_def_id, is_self, paths}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use if_chain::if_chain; diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index bcc39ff855c..ecf48d082af 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -140,7 +141,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), }; - utils::span_lint_and_sugg( + span_lint_and_sugg( cx, NEEDLESS_QUESTION_MARK, entire_expr.span, diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 41cf541ecf5..e93de8a252a 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; 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 67d49f06ad9..e291885d34d 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -5,7 +6,7 @@ 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::{self, paths, span_lint}; +use crate::utils::{self, paths}; declare_clippy_lint! { /// **What it does:** diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index ef7cc65cfcf..7b00879251f 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -5,7 +6,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{self, Constant}; -use crate::utils::span_lint; declare_clippy_lint! { /// **What it does:** Checks for multiplication by -1 as a form of negation. diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index de2899c3462..9fbb6b02742 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then}; +use crate::utils::{get_trait_def_id, return_ty}; +use clippy_utils::diagnostics::span_lint_hir_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 7a7bc7a44cd..83953a16bc8 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 8aebce67917..5db614497e3 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -4,6 +4,7 @@ use std::ptr; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -18,7 +19,7 @@ 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, span_lint_and_then}; +use crate::utils::in_constant; use if_chain::if_chain; // FIXME: this is a correctness problem but there's no suitable diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 7c74b316018..52661416de6 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind, }; diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 71e77932f7a..8e8aaa67afa 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -1,4 +1,5 @@ -use crate::utils::{paths, span_lint}; +use crate::utils::paths; +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::match_type; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index fd653044a1b..343159f9ae2 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_direct_expn_of, span_lint_and_help}; +use crate::utils::is_direct_expn_of; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 8a6d257848f..eab08a58320 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,7 +1,8 @@ use crate::utils; use crate::utils::eager_or_lazy; +use crate::utils::paths; use crate::utils::sugg::Sugg; -use crate::utils::{paths, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 3c041bac234..524b96921fe 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -1,4 +1,5 @@ -use crate::utils::{span_lint, SpanlessEq}; +use crate::utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 1821e2611ff..880951f3e3e 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,4 +1,5 @@ -use crate::utils::{find_macro_calls, return_ty, span_lint_and_then}; +use crate::utils::{find_macro_calls, return_ty}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 359620cc079..ad4ed831941 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, match_panic_call, span_lint}; +use crate::utils::{is_expn_of, match_panic_call}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index aca1ed5ca65..06985cef4ff 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_automatically_derived, span_lint_hir}; +use crate::utils::is_automatically_derived; +use clippy_utils::diagnostics::span_lint_hir; use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 757ead2c24c..c14273840c4 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,6 +1,7 @@ use std::cmp; -use crate::utils::{is_self_ty, span_lint_and_sugg}; +use crate::utils::is_self_ty; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use if_chain::if_chain; diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 46533682d42..95ffae28d8c 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::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 5539331d046..dd63cf99e42 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,4 +1,5 @@ -use crate::utils::{last_path_segment, span_lint_and_help}; +use crate::utils::last_path_segment; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{ intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, QPath, Stmt, StmtKind, diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index fbe54e92ab9..9cf00c953b9 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index bfe8bf33f51..779eddcc170 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,7 +1,8 @@ //! Checks for usage of `&Vec[_]` and `&String`. use crate::utils::ptr::get_spans; -use crate::utils::{is_allowed, match_qpath, paths, span_lint, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{is_allowed, match_qpath, paths}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; use if_chain::if_chain; diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 4f83e370c5f..82408c639b1 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -1,4 +1,5 @@ use crate::utils; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; @@ -58,7 +59,7 @@ impl LateLintPass<'_> for PtrEq { if let Some(left_snip) = snippet_opt(cx, left_var.span); if let Some(right_snip) = snippet_opt(cx, right_var.span); then { - utils::span_lint_and_sugg( + span_lint_and_sugg( cx, PTR_EQ, expr.span, diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index b801defeb24..c04b4255256 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 43431425a43..c4686623487 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -9,7 +9,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; use crate::utils::sugg::Sugg; -use crate::utils::{eq_expr_value, match_def_path, match_qpath, paths, span_lint_and_sugg}; +use crate::utils::{eq_expr_value, match_def_path, match_qpath, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for expressions that could be replaced by the question mark operator. diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 3ce8949bf8b..d476d95256f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,4 +1,5 @@ use crate::consts::{constant, Constant}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; @@ -14,10 +15,7 @@ use rustc_span::symbol::Ident; use std::cmp::Ordering; use crate::utils::sugg::Sugg; -use crate::utils::{ - get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, span_lint, span_lint_and_sugg, - span_lint_and_then, -}; +use crate::utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path}; use crate::utils::{higher, SpanlessEq}; declare_clippy_lint! { diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 84723acd034..3d799328bb5 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,4 +1,5 @@ -use crate::utils::{fn_has_unsatisfiable_preds, match_def_path, paths, span_lint_hir, span_lint_hir_and_then}; +use crate::utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use if_chain::if_chain; diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 283e25553cf..2977a108d14 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs index 3d585cd27a3..061526c6f09 100644 --- a/clippy_lints/src/redundant_else.rs +++ b/clippy_lints/src/redundant_else.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_ast::visit::{walk_expr, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 38dcf7a192c..fd11150bce2 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,4 +1,5 @@ -use crate::utils::{meets_msrv, span_lint_and_sugg}; +use crate::utils::meets_msrv; +use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index c876bae2303..e091095de13 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VisibilityKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 85ea91a387b..6da7b5fbcc8 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; @@ -7,8 +8,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{lint::in_external_macro, ty::TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_sugg; - declare_clippy_lint! { /// **What it does:** Checks for redundant slicing expressions which use the full range, and /// do not change the type. diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 1352a651723..da8339a795a 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,4 +1,5 @@ -use crate::utils::{meets_msrv, span_lint_and_then}; +use crate::utils::meets_msrv; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index cec6b06262b..452fef0694f 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,4 +1,5 @@ -use crate::utils::{last_path_segment, span_lint_and_sugg}; +use crate::utils::last_path_segment; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 31e834ac174..ec2acb6a0ab 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,5 +1,6 @@ +use crate::utils::in_macro; use crate::utils::sugg::Sugg; -use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 1edea613148..bfa21ba9c97 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index a88078c12a3..b39e9d5a78c 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{in_macro, span_lint_and_sugg}; +use crate::utils::in_macro; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index eb7fe403fd7..59d3c0ca5f0 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -12,7 +13,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; -use crate::utils::{fn_def_id, in_macro, match_qpath, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath}; declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs index e62b75de4ca..320d1a8dbe3 100644 --- a/clippy_lints/src/self_assignment.rs +++ b/clippy_lints/src/self_assignment.rs @@ -1,4 +1,5 @@ -use crate::utils::{eq_expr_value, span_lint}; +use crate::utils::eq_expr_value; +use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 695d7233af2..e43947025b9 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg, sugg}; +use crate::utils::{in_macro, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 90cf1b6c861..632715852c5 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, paths, span_lint}; +use crate::utils::{get_trait_def_id, paths}; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 4a8cacb31fd..8ef58b6d563 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,4 +1,5 @@ -use crate::utils::{contains_name, higher, iter_input_pats, span_lint_and_then}; +use crate::utils::{contains_name, higher, iter_input_pats}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_hir::intravisit::FnKind; use rustc_hir::{ diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 1fc4ff5c2e6..6d15a6c444e 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; +use crate::utils::in_macro; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_ast::{Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 87e386baadc..8920d446b9c 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -1,7 +1,8 @@ //! Lint on use of `size_of` or `size_of_val` of T in an expression //! expecting a count of T -use crate::utils::{match_def_path, paths, span_lint_and_help}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir::BinOpKind; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 96f6881556c..7f0f21084af 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,5 +1,6 @@ use crate::utils::sugg::Sugg; -use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq}; +use crate::utils::{get_enclosing_block, match_qpath, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 276a9338819..85e4bb806e7 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg}; +use crate::utils::{is_slice_of_primitives, sugg::Sugg}; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ce93ab23b2f..626166f9008 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,8 +1,6 @@ use crate::utils::SpanlessEq; -use crate::utils::{ - get_parent_expr, is_allowed, match_function_call, method_calls, paths, span_lint, span_lint_and_help, - span_lint_and_sugg, -}; +use crate::utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 3bdd9b7e4cb..364c99adc69 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,5 +1,5 @@ use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; use if_chain::if_chain; diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 0b7d08cb164..ef14b6c5136 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method}; +use crate::utils::{get_trait_def_id, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index d4a495f3ea9..83fe3c49959 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,5 +1,6 @@ use crate::utils::sugg::Sugg; -use crate::utils::{differing_macro_contexts, eq_expr_value, span_lint_and_then}; +use crate::utils::{differing_macro_contexts, eq_expr_value}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 74ccd9235de..88bd2feaadd 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index fb891866364..75a13cb9187 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_adjusted, span_lint}; +use crate::utils::is_adjusted; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 940273afc57..abc9b2fe88e 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, span_lint_and_sugg}; +use crate::utils::match_def_path; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 84ec2aa18ab..42605c01089 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths, span_lint}; +use crate::utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index d3314271c21..cfcc8da9910 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_help, SpanlessHash}; +use crate::utils::{in_macro, SpanlessHash}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; diff --git a/clippy_lints/src/transmute/crosspointer_transmute.rs b/clippy_lints/src/transmute/crosspointer_transmute.rs index ce87defaa94..25d0543c861 100644 --- a/clippy_lints/src/transmute/crosspointer_transmute.rs +++ b/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -1,5 +1,5 @@ use super::CROSSPOINTER_TRANSMUTE; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 562d880e39a..f61b5362b5a 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -1,5 +1,6 @@ use super::TRANSMUTE_FLOAT_TO_INT; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_ast as ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/transmute/transmute_int_to_bool.rs b/clippy_lints/src/transmute/transmute_int_to_bool.rs index 5b609f906a3..ebdd1eba744 100644 --- a/clippy_lints/src/transmute/transmute_int_to_bool.rs +++ b/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -1,5 +1,6 @@ use super::TRANSMUTE_INT_TO_BOOL; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 29d2450618a..afecfc3d701 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -1,5 +1,6 @@ use super::TRANSMUTE_INT_TO_CHAR; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index f83fba8966a..c762a7ab885 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -1,5 +1,6 @@ use super::TRANSMUTE_INT_TO_FLOAT; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index f4e60a3020c..820c56a215e 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -1,5 +1,6 @@ use super::TRANSMUTE_PTR_TO_PTR; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index f5dbbbe33bc..049210e555c 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,6 +1,7 @@ use super::utils::get_type_snippet; use super::TRANSMUTE_PTR_TO_REF; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, QPath}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 4780eb9b14e..993ef8698f8 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -1,5 +1,6 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; -use crate::utils::{span_lint_and_sugg, span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index dea896622f1..da7d1016d97 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,6 +1,7 @@ use super::utils::can_be_expressed_as_pointer_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; -use crate::utils::{span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index 503c5e0ff38..9bea88be7da 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -1,6 +1,7 @@ use super::utils::is_layout_incompatible; use super::UNSOUND_COLLECTION_TRANSMUTE; -use crate::utils::{match_def_path, paths, span_lint}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::span_lint; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 83441514af0..29ffe03b673 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -1,5 +1,6 @@ use super::USELESS_TRANSMUTE; -use crate::utils::{span_lint, span_lint_and_then, sugg}; +use crate::utils::sugg; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/wrong_transmute.rs b/clippy_lints/src/transmute/wrong_transmute.rs index d6d77f2c834..2118f3d6950 100644 --- a/clippy_lints/src/transmute/wrong_transmute.rs +++ b/clippy_lints/src/transmute/wrong_transmute.rs @@ -1,5 +1,5 @@ use super::WRONG_TRANSMUTE; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 2ba2b646f00..3a2b1359a54 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{match_qpath, paths, span_lint}; +use crate::utils::{match_qpath, paths}; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e356add8e9d..1fce03b4f47 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths, span_lint_and_sugg}; +use crate::utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 01aeea7a67f..eab81b1e246 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_path, paths, span_lint_and_sugg}; +use crate::utils::{match_path, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/types/box_vec.rs b/clippy_lints/src/types/box_vec.rs index 6aa98e435e1..6d04a47a5c8 100644 --- a/clippy_lints/src/types/box_vec.rs +++ b/clippy_lints/src/types/box_vec.rs @@ -1,8 +1,9 @@ +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{is_ty_param_diagnostic_item, span_lint_and_help}; +use crate::utils::is_ty_param_diagnostic_item; use super::BOX_VEC; diff --git a/clippy_lints/src/types/linked_list.rs b/clippy_lints/src/types/linked_list.rs index 47eb4ede4e4..e9e1995f6a5 100644 --- a/clippy_lints/src/types/linked_list.rs +++ b/clippy_lints/src/types/linked_list.rs @@ -1,7 +1,8 @@ +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{self as hir, def_id::DefId}; use rustc_lint::LateContext; -use crate::utils::{match_def_path, paths, span_lint_and_help}; +use crate::utils::{match_def_path, paths}; use super::LINKEDLIST; diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 279a971318c..ac4fe502a7a 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -13,6 +13,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; +use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::source::{indent_of, reindent_multiline, snippet, snippet_opt, snippet_with_macro_callsite}; use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item}; use if_chain::if_chain; @@ -38,10 +39,7 @@ 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, int_bits, match_path, multispan_sugg, sext, span_lint, - span_lint_and_help, span_lint_and_then, unsext, -}; +use crate::utils::{clip, comparisons, differing_macro_contexts, higher, int_bits, match_path, sext, unsext}; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index dc5db963b4e..79c5a32de2c 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,8 +1,9 @@ +use clippy_utils::diagnostics::span_lint; use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{is_ty_param_diagnostic_item, span_lint}; +use crate::utils::is_ty_param_diagnostic_item; use super::OPTION_OPTION; diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 0ace1807535..d5fc23f2e39 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,10 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, span_lint_and_sugg}; +use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; use super::RC_BUFFER; diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index c6f6a2f6564..71c014f96f7 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -1,10 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item, span_lint_and_sugg}; +use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item}; use super::{utils, REDUNDANT_ALLOCATION}; diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 6f45442b9ba..8cedb0ede2b 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; @@ -8,7 +9,7 @@ use rustc_span::symbol::sym; use rustc_target::abi::LayoutOf; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{last_path_segment, span_lint_and_sugg}; +use crate::utils::last_path_segment; use super::VEC_BOX; diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 8c34ca16e6f..943573a2b53 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_function_call, paths, span_lint_and_help}; +use crate::utils::{match_function_call, paths}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_lang_item; use rustc_hir::{lang_items, Expr}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index e44fec7ad8e..c37bbf297f8 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_allowed, span_lint_and_sugg}; +use crate::utils::is_allowed; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index c6ae8b9b598..d0456347f8a 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help}; +use crate::utils::{get_trait_def_id, paths}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, StmtKind}; diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index 9582c162e77..2c51a8556d9 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; +use crate::utils::{match_def_path, paths}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 7e385b00646..aa83f6a74be 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,5 @@ -use crate::utils; use crate::utils::sugg::Sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -233,7 +233,7 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { 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( + Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg( cx, UNNECESSARY_SORT_BY, expr.span, @@ -256,7 +256,7 @@ impl LateLintPass<'_> for UnnecessarySortBy { Applicability::MachineApplicable }, ), - Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg( cx, UNNECESSARY_SORT_BY, expr.span, diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 01497de3211..cb20192b683 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - contains_return, in_macro, match_qpath, paths, return_ty, span_lint_and_then, visitors::find_all_ret_expressions, -}; +use crate::utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 7f4f16f8faf..42ff1809ff4 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,7 +1,8 @@ #![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 crate::utils::over; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 154082a0fdb..16ad9d2dfd3 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 43166d26787..31fd61f99f2 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_try, match_trait_method, paths, span_lint}; +use crate::utils::{is_try, match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 812482cf5cf..4642bf07c45 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,9 +1,9 @@ +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; use crate::utils::visitors::LocalUsedVisitor; declare_clippy_lint! { diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index a90d26fc95c..c45b851211c 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.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::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 43b5f138a42..75b2f2da602 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,4 +1,5 @@ -use crate::utils::{differing_macro_contexts, span_lint_and_then, usage::is_potentially_mutated}; +use crate::utils::{differing_macro_contexts, usage::is_potentially_mutated}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 6a8f8211566..a85f3ce5c9c 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,4 +1,5 @@ -use crate::utils::{method_chain_args, return_ty, span_lint_and_then}; +use crate::utils::{method_chain_args, return_ty}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 0470e1dbbb8..7ce9aa13184 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Item, ItemKind, Variant}; @@ -81,7 +81,7 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { // assume that two-letter words are some kind of valid abbreviation like FP for false positive // (and don't warn) if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2) - // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive + // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive // upper-case-acronyms-aggressive config option enabled || (be_aggressive && ident != &corrected) { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index de7eb42d56d..9a42c833470 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,7 +1,7 @@ +use crate::utils::{in_macro, meets_msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; - -use crate::utils::{in_macro, meets_msrv, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index e6b4fde560f..1eb152daac8 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,6 @@ use crate::utils::sugg::Sugg; -use crate::utils::{ - get_parent_expr, match_def_path, match_trait_method, paths, span_lint_and_help, span_lint_and_sugg, -}; +use crate::utils::{get_parent_expr, match_def_path, match_trait_method, paths}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 04b8d9ee2c7..a566362ae78 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,8 +1,7 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{ - is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, span_lint, - span_lint_and_help, span_lint_and_sugg, SpanlessEq, -}; +use crate::utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq}; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use if_chain::if_chain; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index cd09a5b53e0..9e142413365 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,6 +1,7 @@ use crate::consts::{constant, Constant}; use crate::rustc_target::abi::LayoutOf; -use crate::utils::{higher, span_lint_and_sugg}; +use crate::utils::higher; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use if_chain::if_chain; diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 4ad787ecf66..3ef55a82a17 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, path_to_local, path_to_local_id, paths, span_lint_and_sugg}; +use crate::utils::{match_def_path, path_to_local, path_to_local_id, paths}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs index d2494b321ef..9a2fb1414a1 100644 --- a/clippy_lints/src/vec_resize_to_zero.rs +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs index 079a4279c60..01dc54dc5fd 100644 --- a/clippy_lints/src/verbose_file_reads.rs +++ b/clippy_lints/src/verbose_file_reads.rs @@ -1,4 +1,5 @@ -use crate::utils::{paths, span_lint_and_help}; +use crate::utils::paths; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; diff --git a/clippy_lints/src/wildcard_dependencies.rs b/clippy_lints/src/wildcard_dependencies.rs index cd1864f461d..8f96b962279 100644 --- a/clippy_lints/src/wildcard_dependencies.rs +++ b/clippy_lints/src/wildcard_dependencies.rs @@ -1,4 +1,5 @@ -use crate::utils::{run_lints, span_lint}; +use crate::utils::run_lints; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e12ca49fd4c..424ca2a4c2f 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; +use crate::utils::in_macro; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index f2fef73a641..fd3e5a7ce91 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::ops::Range; -use crate::utils::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle}; diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 11d96e15ff1..3b4890ad560 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index ab27b60cfa4..82466da6862 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; @@ -8,7 +9,7 @@ use rustc_span::sym; use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{paths, span_lint_and_help}; +use crate::utils::paths; declare_clippy_lint! { /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d895d798b5e..d3cf2a34709 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -33,7 +33,7 @@ pub mod attrs; pub mod camel_case; pub mod comparisons; pub mod consts; -mod diagnostics; +pub mod diagnostics; pub mod eager_or_lazy; pub mod higher; mod hir_utils; @@ -48,7 +48,6 @@ pub mod usage; pub mod visitors; pub use self::attrs::*; -pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::collections::hash_map::Entry; -- cgit 1.4.1-3-g733a5 From ecbef77bd71c2a4b4ad8dbb0b0ffa67affe29cc3 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 12 Mar 2021 12:04:44 +0900 Subject: Extract lints of unit_types group from types group --- clippy_lints/src/lib.rs | 23 ++- clippy_lints/src/types/mod.rs | 396 +----------------------------------- clippy_lints/src/unit_types/mod.rs | 398 +++++++++++++++++++++++++++++++++++++ 3 files changed, 415 insertions(+), 402 deletions(-) create mode 100644 clippy_lints/src/unit_types/mod.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 52c342d580e..f80aee182e2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -350,6 +350,7 @@ mod types; mod undropped_manually_drops; mod unicode; mod unit_return_expecting_ord; +mod unit_types; mod unnamed_address; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -960,20 +961,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::BOX_VEC, &types::IMPLICIT_HASHER, &types::INVALID_UPCAST_COMPARISONS, - &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, - &types::UNIT_ARG, - &types::UNIT_CMP, &types::VEC_BOX, &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, + &unit_types::LET_UNIT_VALUE, + &unit_types::UNIT_ARG, + &unit_types::UNIT_CMP, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, @@ -1084,8 +1085,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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); + store.register_late_pass(|| box unit_types::LetUnitValue); + store.register_late_pass(|| box unit_types::UnitCmp); store.register_late_pass(|| box loops::Loops); store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); @@ -1160,7 +1161,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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); + store.register_late_pass(|| box unit_types::UnitArg); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); @@ -1415,11 +1416,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(&types::IMPLICIT_HASHER), LintId::of(&types::INVALID_UPCAST_COMPARISONS), - LintId::of(&types::LET_UNIT_VALUE), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), + LintId::of(&unit_types::LET_UNIT_VALUE), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unused_self::UNUSED_SELF), @@ -1708,12 +1709,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::TYPE_COMPLEXITY), - LintId::of(&types::UNIT_ARG), - LintId::of(&types::UNIT_CMP), 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(&unit_types::UNIT_ARG), + LintId::of(&unit_types::UNIT_CMP), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), @@ -1934,8 +1935,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), LintId::of(&types::BORROWED_BOX), LintId::of(&types::TYPE_COMPLEXITY), - LintId::of(&types::UNIT_ARG), LintId::of(&types::VEC_BOX), + LintId::of(&unit_types::UNIT_ARG), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), @@ -2005,10 +2006,10 @@ 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::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(&unit_types::UNIT_CMP), 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/types/mod.rs b/clippy_lints/src/types/mod.rs index ac4fe502a7a..5103a259559 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -14,23 +14,21 @@ use std::cmp::Ordering; use std::collections::BTreeMap; use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::{indent_of, reindent_multiline, snippet, snippet_opt, snippet_with_macro_callsite}; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item}; use if_chain::if_chain; -use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_errors::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, Local, MatchSource, MutTy, Node, QPath, Stmt, StmtKind, TraitFn, TraitItem, - TraitItemKind, TyKind, + BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, IntTy, Ty, TyS, TypeckResults, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; use rustc_target::abi::LayoutOf; @@ -39,7 +37,7 @@ 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, int_bits, match_path, sext, unsext}; +use crate::utils::{clip, comparisons, differing_macro_contexts, int_bits, match_path, sext, unsext}; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. @@ -389,390 +387,6 @@ impl Types { } } -declare_clippy_lint! { - /// **What it does:** Checks for binding a unit value. - /// - /// **Why is this bad?** A unit value cannot usefully be used anywhere. So - /// binding one is kind of pointless. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let x = { - /// 1; - /// }; - /// ``` - pub LET_UNIT_VALUE, - pedantic, - "creating a `let` binding to a value of unit type, which usually can't be used afterwards" -} - -declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]); - -impl<'tcx> LateLintPass<'tcx> for LetUnitValue { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { - if is_unit(cx.typeck_results().pat_ty(&local.pat)) { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; - } - if higher::is_from_for_desugar(local) { - return; - } - span_lint_and_then( - cx, - LET_UNIT_VALUE, - stmt.span, - "this let-binding has unit value", - |diag| { - if let Some(expr) = &local.init { - let snip = snippet_with_macro_callsite(cx, expr.span, "()"); - diag.span_suggestion( - stmt.span, - "omit the `let` binding", - format!("{};", snip), - Applicability::MachineApplicable, // snippet - ); - } - }, - ); - } - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for comparisons to unit. This includes all binary - /// comparisons (like `==` and `<`) and asserts. - /// - /// **Why is this bad?** Unit is always equal to itself, and thus is just a - /// clumsily written constant. Mostly this happens when someone accidentally - /// adds semicolons at the end of the operands. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # fn foo() {}; - /// # fn bar() {}; - /// # fn baz() {}; - /// if { - /// foo(); - /// } == { - /// bar(); - /// } { - /// baz(); - /// } - /// ``` - /// is equal to - /// ```rust - /// # fn foo() {}; - /// # fn bar() {}; - /// # fn baz() {}; - /// { - /// foo(); - /// bar(); - /// baz(); - /// } - /// ``` - /// - /// For asserts: - /// ```rust - /// # fn foo() {}; - /// # fn bar() {}; - /// assert_eq!({ foo(); }, { bar(); }); - /// ``` - /// will always succeed - pub UNIT_CMP, - correctness, - "comparing unit values" -} - -declare_lint_pass!(UnitCmp => [UNIT_CMP]); - -impl<'tcx> LateLintPass<'tcx> for UnitCmp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if expr.span.from_expansion() { - if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { - let result = match &*symbol.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", - _ => return, - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "`{}` of unit values detected. This will always {}", - symbol.as_str(), - result - ), - ); - } - } - } - } - return; - } - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { - let result = match op { - BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", - _ => "false", - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "{}-comparison of unit values detected. This will always be {}", - op.as_str(), - result - ), - ); - } - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for passing a unit value as an argument to a function without using a - /// unit literal (`()`). - /// - /// **Why is this bad?** This is likely the result of an accidental semicolon. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// foo({ - /// let a = bar(); - /// baz(a); - /// }) - /// ``` - pub UNIT_ARG, - complexity, - "passing unit to a function" -} - -declare_lint_pass!(UnitArg => [UNIT_ARG]); - -impl<'tcx> LateLintPass<'tcx> for UnitArg { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - // apparently stuff in the desugaring of `?` can trigger this - // so check for that here - // only the calls to `Try::from_error` is marked as desugared, - // so we need to check both the current Expr and its parent. - if is_questionmark_desugar_marked_call(expr) { - return; - } - if_chain! { - let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; - if is_questionmark_desugar_marked_call(parent_expr); - then { - return; - } - } - - match expr.kind { - ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { - let args_to_recover = args - .iter() - .filter(|arg| { - if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { - !matches!( - &arg.kind, - ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) - ) - } else { - false - } - }) - .collect::>(); - if !args_to_recover.is_empty() { - lint_unit_args(cx, expr, &args_to_recover); - } - }, - _ => (), - } - } -} - -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 { - ("", "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 "; - applicability = Applicability::MaybeIncorrect; - }); - - 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)) - .filter_map(|arg| snippet_opt(cx, arg.span)) - .collect(); - - 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( - &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, - ); - } - } - }, - ); -} - -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 { - callee.span.is_desugaring(DesugaringKind::QuestionMark) - } else { - false - } -} - -fn is_unit(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) -} - -fn is_unit_literal(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) -} - declare_clippy_lint! { /// **What it does:** Checks for types used in structs, parameters and `let` /// declarations above a certain complexity threshold. diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs new file mode 100644 index 00000000000..6450ef600ca --- /dev/null +++ b/clippy_lints/src/unit_types/mod.rs @@ -0,0 +1,398 @@ +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, MatchSource, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +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::{ExpnKind, MacroKind}; + +use if_chain::if_chain; + +use crate::utils::diagnostics::{span_lint, span_lint_and_then}; +use crate::utils::higher; +use crate::utils::source::{indent_of, reindent_multiline, snippet_opt, snippet_with_macro_callsite}; + +declare_clippy_lint! { + /// **What it does:** Checks for binding a unit value. + /// + /// **Why is this bad?** A unit value cannot usefully be used anywhere. So + /// binding one is kind of pointless. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = { + /// 1; + /// }; + /// ``` + pub LET_UNIT_VALUE, + pedantic, + "creating a `let` binding to a value of unit type, which usually can't be used afterwards" +} + +declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]); + +impl<'tcx> LateLintPass<'tcx> for LetUnitValue { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Local(ref local) = stmt.kind { + if is_unit(cx.typeck_results().pat_ty(&local.pat)) { + if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { + return; + } + if higher::is_from_for_desugar(local) { + return; + } + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + if let Some(expr) = &local.init { + let snip = snippet_with_macro_callsite(cx, expr.span, "()"); + diag.span_suggestion( + stmt.span, + "omit the `let` binding", + format!("{};", snip), + Applicability::MachineApplicable, // snippet + ); + } + }, + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons to unit. This includes all binary + /// comparisons (like `==` and `<`) and asserts. + /// + /// **Why is this bad?** Unit is always equal to itself, and thus is just a + /// clumsily written constant. Mostly this happens when someone accidentally + /// adds semicolons at the end of the operands. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// # fn baz() {}; + /// if { + /// foo(); + /// } == { + /// bar(); + /// } { + /// baz(); + /// } + /// ``` + /// is equal to + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// # fn baz() {}; + /// { + /// foo(); + /// bar(); + /// baz(); + /// } + /// ``` + /// + /// For asserts: + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// assert_eq!({ foo(); }, { bar(); }); + /// ``` + /// will always succeed + pub UNIT_CMP, + correctness, + "comparing unit values" +} + +declare_lint_pass!(UnitCmp => [UNIT_CMP]); + +impl<'tcx> LateLintPass<'tcx> for UnitCmp { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() { + if let Some(callee) = expr.span.source_callee() { + if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { + if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + let op = cmp.node; + if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { + let result = match &*symbol.as_str() { + "assert_eq" | "debug_assert_eq" => "succeed", + "assert_ne" | "debug_assert_ne" => "fail", + _ => return, + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "`{}` of unit values detected. This will always {}", + symbol.as_str(), + result + ), + ); + } + } + } + } + return; + } + if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + let op = cmp.node; + if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { + let result = match op { + BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", + _ => "false", + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "{}-comparison of unit values detected. This will always be {}", + op.as_str(), + result + ), + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for passing a unit value as an argument to a function without using a + /// unit literal (`()`). + /// + /// **Why is this bad?** This is likely the result of an accidental semicolon. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// foo({ + /// let a = bar(); + /// baz(a); + /// }) + /// ``` + pub UNIT_ARG, + complexity, + "passing unit to a function" +} + +declare_lint_pass!(UnitArg => [UNIT_ARG]); + +impl<'tcx> LateLintPass<'tcx> for UnitArg { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + // apparently stuff in the desugaring of `?` can trigger this + // so check for that here + // only the calls to `Try::from_error` is marked as desugared, + // so we need to check both the current Expr and its parent. + if is_questionmark_desugar_marked_call(expr) { + return; + } + if_chain! { + let map = &cx.tcx.hir(); + let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); + if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; + if is_questionmark_desugar_marked_call(parent_expr); + then { + return; + } + } + + match expr.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { + let args_to_recover = args + .iter() + .filter(|arg| { + if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { + !matches!( + &arg.kind, + ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) + ) + } else { + false + } + }) + .collect::>(); + if !args_to_recover.is_empty() { + lint_unit_args(cx, expr, &args_to_recover); + } + }, + _ => (), + } + } +} + +fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { + use rustc_span::hygiene::DesugaringKind; + if let ExprKind::Call(ref callee, _) = expr.kind { + callee.span.is_desugaring(DesugaringKind::QuestionMark) + } else { + false + } +} + +fn is_unit(ty: Ty<'_>) -> bool { + matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) +} + +fn is_unit_literal(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) +} + +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 "; + applicability = Applicability::MaybeIncorrect; + }); + + 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)) + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + 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( + &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, + ); + } + } + }, + ); +} + +fn is_empty_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: &[], + expr: None, + .. + }, + _, + ) + ) +} + +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 +} -- cgit 1.4.1-3-g733a5 From 37bffb7797ff7ffa65eeda3f936a1d45670c157b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 12 Mar 2021 12:15:45 +0900 Subject: Extract utility functions to utils.rs --- clippy_lints/src/unit_types/mod.rs | 13 ++++--------- clippy_lints/src/unit_types/utils.rs | 10 ++++++++++ 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 clippy_lints/src/unit_types/utils.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index 6450ef600ca..d71f9d7d24b 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -1,9 +1,10 @@ +mod utils; + use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, MatchSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; 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::{ExpnKind, MacroKind}; @@ -13,6 +14,8 @@ use crate::utils::diagnostics::{span_lint, span_lint_and_then}; use crate::utils::higher; use crate::utils::source::{indent_of, reindent_multiline, snippet_opt, snippet_with_macro_callsite}; +use utils::{is_unit, is_unit_literal}; + declare_clippy_lint! { /// **What it does:** Checks for binding a unit value. /// @@ -244,14 +247,6 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { } } -fn is_unit(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) -} - -fn is_unit_literal(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) -} - 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 { diff --git a/clippy_lints/src/unit_types/utils.rs b/clippy_lints/src/unit_types/utils.rs new file mode 100644 index 00000000000..a15a7b812c5 --- /dev/null +++ b/clippy_lints/src/unit_types/utils.rs @@ -0,0 +1,10 @@ +use rustc_hir::{Expr, ExprKind}; +use rustc_middle::ty::{self, Ty}; + +pub(super) fn is_unit(ty: Ty<'_>) -> bool { + matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) +} + +pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) +} -- cgit 1.4.1-3-g733a5 From d17f54538f602480a10357f7677020190b9bac8e Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 12 Mar 2021 12:28:31 +0900 Subject: Move let_unit_value to its own module --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 40 +++++++++++++++++++++++++++ clippy_lints/src/unit_types/mod.rs | 39 ++++---------------------- 3 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 clippy_lints/src/unit_types/let_unit_value.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f80aee182e2..a8c4eb61a85 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1085,7 +1085,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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 unit_types::LetUnitValue); + store.register_late_pass(|| box unit_types::UnitTypes); store.register_late_pass(|| box unit_types::UnitCmp); store.register_late_pass(|| box loops::Loops); store.register_late_pass(|| box main_recursion::MainRecursion::default()); diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs new file mode 100644 index 00000000000..126f6aa746a --- /dev/null +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -0,0 +1,40 @@ +use rustc_errors::Applicability; +use rustc_hir::{Stmt, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; + +use crate::utils::diagnostics::span_lint_and_then; +use crate::utils::higher; +use crate::utils::source::snippet_with_macro_callsite; + +use super::{utils, LET_UNIT_VALUE}; + +pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { + if let StmtKind::Local(ref local) = stmt.kind { + if utils::is_unit(cx.typeck_results().pat_ty(&local.pat)) { + if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { + return; + } + if higher::is_from_for_desugar(local) { + return; + } + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + if let Some(expr) = &local.init { + let snip = snippet_with_macro_callsite(cx, expr.span, "()"); + diag.span_suggestion( + stmt.span, + "omit the `let` binding", + format!("{};", snip), + Applicability::MachineApplicable, // snippet + ); + } + }, + ); + } + } +} diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index d71f9d7d24b..530c0fcf53d 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -1,18 +1,17 @@ +mod let_unit_value; mod utils; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, MatchSource, Node, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use if_chain::if_chain; use crate::utils::diagnostics::{span_lint, span_lint_and_then}; -use crate::utils::higher; -use crate::utils::source::{indent_of, reindent_multiline, snippet_opt, snippet_with_macro_callsite}; +use crate::utils::source::{indent_of, reindent_multiline, snippet_opt}; use utils::{is_unit, is_unit_literal}; @@ -35,37 +34,11 @@ declare_clippy_lint! { "creating a `let` binding to a value of unit type, which usually can't be used afterwards" } -declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]); +declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE]); -impl<'tcx> LateLintPass<'tcx> for LetUnitValue { +impl<'tcx> LateLintPass<'tcx> for UnitTypes { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { - if is_unit(cx.typeck_results().pat_ty(&local.pat)) { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; - } - if higher::is_from_for_desugar(local) { - return; - } - span_lint_and_then( - cx, - LET_UNIT_VALUE, - stmt.span, - "this let-binding has unit value", - |diag| { - if let Some(expr) = &local.init { - let snip = snippet_with_macro_callsite(cx, expr.span, "()"); - diag.span_suggestion( - stmt.span, - "omit the `let` binding", - format!("{};", snip), - Applicability::MachineApplicable, // snippet - ); - } - }, - ); - } - } + let_unit_value::check(cx, stmt); } } -- cgit 1.4.1-3-g733a5 From 1bb221243b853856596241ff3c4210e80a114d34 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 12 Mar 2021 12:38:31 +0900 Subject: Move unit_cmp to its own module --- clippy_lints/src/lib.rs | 1 - clippy_lints/src/unit_types/mod.rs | 71 +++++---------------------------- clippy_lints/src/unit_types/unit_cmp.rs | 57 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 61 deletions(-) create mode 100644 clippy_lints/src/unit_types/unit_cmp.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a8c4eb61a85..c52c2ea80c5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1086,7 +1086,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); store.register_late_pass(|| box unit_types::UnitTypes); - store.register_late_pass(|| box unit_types::UnitCmp); store.register_late_pass(|| box loops::Loops); store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index 530c0fcf53d..40cc9d9dbfb 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -1,16 +1,16 @@ mod let_unit_value; +mod unit_cmp; mod utils; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, MatchSource, Node, Stmt, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use if_chain::if_chain; -use crate::utils::diagnostics::{span_lint, span_lint_and_then}; +use crate::utils::diagnostics::span_lint_and_then; use crate::utils::source::{indent_of, reindent_multiline, snippet_opt}; use utils::{is_unit, is_unit_literal}; @@ -34,14 +34,6 @@ declare_clippy_lint! { "creating a `let` binding to a value of unit type, which usually can't be used afterwards" } -declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE]); - -impl<'tcx> LateLintPass<'tcx> for UnitTypes { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - let_unit_value::check(cx, stmt); - } -} - declare_clippy_lint! { /// **What it does:** Checks for comparisons to unit. This includes all binary /// comparisons (like `==` and `<`) and asserts. @@ -89,56 +81,15 @@ declare_clippy_lint! { "comparing unit values" } -declare_lint_pass!(UnitCmp => [UNIT_CMP]); +declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP]); -impl<'tcx> LateLintPass<'tcx> for UnitCmp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if expr.span.from_expansion() { - if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { - let result = match &*symbol.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", - _ => return, - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "`{}` of unit values detected. This will always {}", - symbol.as_str(), - result - ), - ); - } - } - } - } - return; - } - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { - let result = match op { - BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", - _ => "false", - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "{}-comparison of unit values detected. This will always be {}", - op.as_str(), - result - ), - ); - } - } +impl LateLintPass<'_> for UnitTypes { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + let_unit_value::check(cx, stmt); + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + unit_cmp::check(cx, expr); } } diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs new file mode 100644 index 00000000000..e19fc1ec9ef --- /dev/null +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -0,0 +1,57 @@ +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::hygiene::{ExpnKind, MacroKind}; + +use crate::utils::diagnostics::span_lint; + +use super::{utils, UNIT_CMP}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + if let Some(callee) = expr.span.source_callee() { + if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { + if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + let op = cmp.node; + if op.is_comparison() && utils::is_unit(cx.typeck_results().expr_ty(left)) { + let result = match &*symbol.as_str() { + "assert_eq" | "debug_assert_eq" => "succeed", + "assert_ne" | "debug_assert_ne" => "fail", + _ => return, + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "`{}` of unit values detected. This will always {}", + symbol.as_str(), + result + ), + ); + } + } + } + } + return; + } + + if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + let op = cmp.node; + if op.is_comparison() && utils::is_unit(cx.typeck_results().expr_ty(left)) { + let result = match op { + BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", + _ => "false", + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "{}-comparison of unit values detected. This will always be {}", + op.as_str(), + result + ), + ); + } + } +} -- cgit 1.4.1-3-g733a5 From 6211b49ac1c9a013869943a003efbd5a71a73c93 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 12 Mar 2021 12:47:21 +0900 Subject: Move unit_arg to its own module --- clippy_lints/src/lib.rs | 1 - clippy_lints/src/unit_types/mod.rs | 228 ++------------------------------ clippy_lints/src/unit_types/unit_arg.rs | 209 +++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 220 deletions(-) create mode 100644 clippy_lints/src/unit_types/unit_arg.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c52c2ea80c5..f479d756a33 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1160,7 +1160,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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 unit_types::UnitArg); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index 40cc9d9dbfb..64420a03933 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -1,20 +1,12 @@ mod let_unit_value; +mod unit_arg; mod unit_cmp; mod utils; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, Stmt, StmtKind}; +use rustc_hir::{Expr, Stmt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use if_chain::if_chain; - -use crate::utils::diagnostics::span_lint_and_then; -use crate::utils::source::{indent_of, reindent_multiline, snippet_opt}; - -use utils::{is_unit, is_unit_literal}; - declare_clippy_lint! { /// **What it does:** Checks for binding a unit value. /// @@ -81,18 +73,6 @@ declare_clippy_lint! { "comparing unit values" } -declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP]); - -impl LateLintPass<'_> for UnitTypes { - fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { - let_unit_value::check(cx, stmt); - } - - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - unit_cmp::check(cx, expr); - } -} - declare_clippy_lint! { /// **What it does:** Checks for passing a unit value as an argument to a function without using a /// unit literal (`()`). @@ -113,205 +93,15 @@ declare_clippy_lint! { "passing unit to a function" } -declare_lint_pass!(UnitArg => [UNIT_ARG]); - -impl<'tcx> LateLintPass<'tcx> for UnitArg { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - // apparently stuff in the desugaring of `?` can trigger this - // so check for that here - // only the calls to `Try::from_error` is marked as desugared, - // so we need to check both the current Expr and its parent. - if is_questionmark_desugar_marked_call(expr) { - return; - } - if_chain! { - let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; - if is_questionmark_desugar_marked_call(parent_expr); - then { - return; - } - } - - match expr.kind { - ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { - let args_to_recover = args - .iter() - .filter(|arg| { - if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { - !matches!( - &arg.kind, - ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) - ) - } else { - false - } - }) - .collect::>(); - if !args_to_recover.is_empty() { - lint_unit_args(cx, expr, &args_to_recover); - } - }, - _ => (), - } - } -} +declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); -fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { - use rustc_span::hygiene::DesugaringKind; - if let ExprKind::Call(ref callee, _) = expr.kind { - callee.span.is_desugaring(DesugaringKind::QuestionMark) - } else { - false +impl LateLintPass<'_> for UnitTypes { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + let_unit_value::check(cx, stmt); } -} - -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 "; - applicability = Applicability::MaybeIncorrect; - }); - - 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)) - .filter_map(|arg| snippet_opt(cx, arg.span)) - .collect(); - - 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( - &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, - ); - } - } - }, - ); -} - -fn is_empty_block(expr: &Expr<'_>) -> bool { - matches!( - expr.kind, - ExprKind::Block( - Block { - stmts: &[], - expr: None, - .. - }, - _, - ) - ) -} - -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) - ); + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + unit_cmp::check(cx, expr); + unit_arg::check(cx, expr); } - stmts_and_call_snippet } diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs new file mode 100644 index 00000000000..49b5725f528 --- /dev/null +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -0,0 +1,209 @@ +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind}; +use rustc_lint::LateContext; + +use if_chain::if_chain; + +use crate::utils::diagnostics::span_lint_and_then; +use crate::utils::source::{indent_of, reindent_multiline, snippet_opt}; + +use super::{utils, UNIT_ARG}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + // apparently stuff in the desugaring of `?` can trigger this + // so check for that here + // only the calls to `Try::from_error` is marked as desugared, + // so we need to check both the current Expr and its parent. + if is_questionmark_desugar_marked_call(expr) { + return; + } + if_chain! { + let map = &cx.tcx.hir(); + let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); + if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; + if is_questionmark_desugar_marked_call(parent_expr); + then { + return; + } + } + + match expr.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { + let args_to_recover = args + .iter() + .filter(|arg| { + if utils::is_unit(cx.typeck_results().expr_ty(arg)) && !utils::is_unit_literal(arg) { + !matches!( + &arg.kind, + ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) + ) + } else { + false + } + }) + .collect::>(); + if !args_to_recover.is_empty() { + lint_unit_args(cx, expr, &args_to_recover); + } + }, + _ => (), + } +} + +fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { + use rustc_span::hygiene::DesugaringKind; + if let ExprKind::Call(ref callee, _) = expr.kind { + callee.span.is_desugaring(DesugaringKind::QuestionMark) + } else { + false + } +} + +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 "; + applicability = Applicability::MaybeIncorrect; + }); + + 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)) + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + 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( + &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, + ); + } + } + }, + ); +} + +fn is_empty_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: &[], + expr: None, + .. + }, + _, + ) + ) +} + +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 +} -- cgit 1.4.1-3-g733a5 From 5a439f5a82815c029c8b2bfa28d9525aaac1c320 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 16 Mar 2021 10:37:05 +0900 Subject: Remove unit_types::utils::is_unit --- clippy_lints/src/unit_types/let_unit_value.rs | 4 ++-- clippy_lints/src/unit_types/unit_arg.rs | 2 +- clippy_lints/src/unit_types/unit_cmp.rs | 6 +++--- clippy_lints/src/unit_types/utils.rs | 5 ----- 4 files changed, 6 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 126f6aa746a..55715cc6bf3 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -7,11 +7,11 @@ use crate::utils::diagnostics::span_lint_and_then; use crate::utils::higher; use crate::utils::source::snippet_with_macro_callsite; -use super::{utils, LET_UNIT_VALUE}; +use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let StmtKind::Local(ref local) = stmt.kind { - if utils::is_unit(cx.typeck_results().pat_ty(&local.pat)) { + if cx.typeck_results().pat_ty(&local.pat).is_unit() { if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { return; } diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 49b5725f528..d4ef645fc8d 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -36,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { let args_to_recover = args .iter() .filter(|arg| { - if utils::is_unit(cx.typeck_results().expr_ty(arg)) && !utils::is_unit_literal(arg) { + if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) { !matches!( &arg.kind, ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index e19fc1ec9ef..f9c0374c86d 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -4,7 +4,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use crate::utils::diagnostics::span_lint; -use super::{utils, UNIT_CMP}; +use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { let op = cmp.node; - if op.is_comparison() && utils::is_unit(cx.typeck_results().expr_ty(left)) { + if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { let result = match &*symbol.as_str() { "assert_eq" | "debug_assert_eq" => "succeed", "assert_ne" | "debug_assert_ne" => "fail", @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { let op = cmp.node; - if op.is_comparison() && utils::is_unit(cx.typeck_results().expr_ty(left)) { + if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { let result = match op { BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", _ => "false", diff --git a/clippy_lints/src/unit_types/utils.rs b/clippy_lints/src/unit_types/utils.rs index a15a7b812c5..4e194a05e8d 100644 --- a/clippy_lints/src/unit_types/utils.rs +++ b/clippy_lints/src/unit_types/utils.rs @@ -1,9 +1,4 @@ use rustc_hir::{Expr, ExprKind}; -use rustc_middle::ty::{self, Ty}; - -pub(super) fn is_unit(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) -} pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool { matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) -- cgit 1.4.1-3-g733a5 From c5b3a719ed85ae48e813ee07cb9e7b56c6a763d9 Mon Sep 17 00:00:00 2001 From: Yukio Tanaka Date: Tue, 16 Mar 2021 19:46:40 +0900 Subject: Fix FP of `manual_unwrap_or` in const fn --- clippy_lints/src/manual_unwrap_or.rs | 33 ++++++++++++++++++++++++++++----- tests/ui/manual_unwrap_or.fixed | 15 +++++++++++++++ tests/ui/manual_unwrap_or.rs | 15 +++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 0e030e0e261..203f018a9e2 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -6,12 +6,12 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::{hir_id::HirId, intravisit::FnKind, Arm, Body, Expr, ExprKind, FnDecl, Pat, PatKind, StmtKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; +use rustc_span::{source_map::Span, sym}; declare_clippy_lint! { /// **What it does:** @@ -44,11 +44,34 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); impl LateLintPass<'_> for ManualUnwrapOr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + span: Span, + _: HirId, + ) { + if in_external_macro(cx.sess(), span) { return; } - lint_manual_unwrap_or(cx, expr); + if_chain! { + if let FnKind::ItemFn(_, _, header, _) = kind; + if !header.is_const(); + let expr = &body.value; + if let ExprKind::Block(block, _) = expr.kind; + then { + for stmt in block.stmts { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = &stmt.kind { + lint_manual_unwrap_or(cx, expr); + } + } + if let Some(expr) = block.expr { + lint_manual_unwrap_or(cx, expr); + } + } + } } } diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 81d903c15d3..2f57957f55b 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -136,4 +136,19 @@ fn result_unwrap_or() { }; } +// don't lint in const fn +const fn const_fn_unwrap_or() { + match Some(1) { + Some(s) => s, + None => 0, + }; +} + +const fn const_fn_unwrap() { + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(_) => "Bob", + }; +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 16105d379c3..1088047da75 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -175,4 +175,19 @@ fn result_unwrap_or() { }; } +// don't lint in const fn +const fn const_fn_unwrap_or() { + match Some(1) { + Some(s) => s, + None => 0, + }; +} + +const fn const_fn_unwrap() { + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(_) => "Bob", + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 02ceeb59d4853eecec3a52a7fbd9f6acc1c3f91c Mon Sep 17 00:00:00 2001 From: Yukio Tanaka Date: Wed, 17 Mar 2021 00:06:42 +0900 Subject: Use in_constant instead of is_const --- clippy_lints/src/manual_unwrap_or.rs | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 203f018a9e2..615e2d5c2af 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,17 +1,17 @@ use crate::consts::constant_simple; use crate::utils; -use crate::utils::{path_to_local_id, sugg}; +use crate::utils::{in_constant, path_to_local_id, sugg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{hir_id::HirId, intravisit::FnKind, Arm, Body, Expr, ExprKind, FnDecl, Pat, PatKind, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Span, sym}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -44,34 +44,11 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); impl LateLintPass<'_> for ManualUnwrapOr { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - span: Span, - _: HirId, - ) { - if in_external_macro(cx.sess(), span) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { return; } - if_chain! { - if let FnKind::ItemFn(_, _, header, _) = kind; - if !header.is_const(); - let expr = &body.value; - if let ExprKind::Block(block, _) = expr.kind; - then { - for stmt in block.stmts { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = &stmt.kind { - lint_manual_unwrap_or(cx, expr); - } - } - if let Some(expr) = block.expr { - lint_manual_unwrap_or(cx, expr); - } - } - } + lint_manual_unwrap_or(cx, expr); } } -- cgit 1.4.1-3-g733a5 From 4450c21f5125c363397d87ff74ed1a1d52a9f1a8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 1 Mar 2021 13:14:52 -0500 Subject: Keep track of spans in format strings --- clippy_lints/src/write.rs | 238 ++++++++++++++++++++++++++++------------------ 1 file changed, 148 insertions(+), 90 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index fd3e5a7ce91..b5470c3bc70 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,9 +2,8 @@ use std::borrow::Cow; use std::ops::Range; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle}; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, Path, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; @@ -12,8 +11,9 @@ use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::kw; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::symbol::{kw, Symbol}; +use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use smallvec::SmallVec; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -354,7 +354,117 @@ fn newline_span(fmtstr: &StrLit) -> Span { sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) } +/// Stores a list of replacement spans for each argument, but only if all the replacements used an +/// empty format string. +#[derive(Default)] +struct SimpleFormatArgs { + unnamed: Vec>, + named: Vec<(Symbol, SmallVec<[Span; 1]>)>, +} +impl SimpleFormatArgs { + fn get_unnamed(&self) -> impl Iterator { + self.unnamed.iter().map(|x| match x.as_slice() { + // Ignore the dummy span added from out of order format arguments. + [DUMMY_SP] => &[], + x => x, + }) + } + + fn get_named(&self, n: &Path) -> &[Span] { + self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice()) + } + + fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) { + use rustc_parse_format::{ + AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, + }; + + const SIMPLE: FormatSpec<'_> = FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + precision_span: None, + width: CountImplied, + width_span: None, + ty: "", + ty_span: None, + }; + + match arg.position { + ArgumentIs(n) | ArgumentImplicitlyIs(n) => { + if self.unnamed.len() <= n { + // Use a dummy span to mark all unseen arguments. + self.unnamed.resize_with(n, || SmallVec::from([DUMMY_SP])); + if arg.format == SIMPLE { + self.unnamed.push(SmallVec::from([span])); + } else { + self.unnamed.push(SmallVec::new()); + } + } else { + let args = &mut self.unnamed[n]; + match (args.as_mut_slice(), arg.format == SIMPLE) { + // A non-empty format string has been seen already. + ([], _) => (), + // Replace the dummy span, if it exists. + ([dummy @ DUMMY_SP], true) => *dummy = span, + ([_, ..], true) => args.push(span), + ([_, ..], false) => *args = SmallVec::new(), + } + } + }, + ArgumentNamed(n) => { + if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { + match x.1.as_slice() { + // A non-empty format string has been seen already. + [] => (), + [_, ..] if arg.format == SIMPLE => x.1.push(span), + [_, ..] => x.1 = SmallVec::new(), + } + } else if arg.format == SIMPLE { + self.named.push((n, SmallVec::from([span]))); + } else { + self.named.push((n, SmallVec::new())); + } + }, + }; + } +} + impl Write { + /// Parses a format string into a collection of spans for each argument. This only keeps track + /// of empty format arguments. Will also lint usages of debug format strings outside of debug + /// impls. + fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str: &StrLit) -> Option { + use rustc_parse_format::{ParseMode, Parser, Piece}; + + let str_sym = str.symbol.as_str(); + let style = match str.style { + StrStyle::Cooked => None, + StrStyle::Raw(n) => Some(n as usize), + }; + + let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str.span), false, ParseMode::Format); + let mut args = SimpleFormatArgs::default(); + + while let Some(arg) = parser.next() { + let arg = match arg { + Piece::String(_) => continue, + Piece::NextArgument(arg) => arg, + }; + let span = parser.arg_places.last().map_or(DUMMY_SP, |&x| str.span.from_inner(x)); + + if !self.in_debug_impl && arg.format.ty == "?" { + // FIXME: modify rustc's fmt string parser to give us the current span + span_lint(cx, USE_DEBUG, str.span, "use of `Debug`-based formatting"); + } + + args.push(arg, span); + } + + parser.errors.is_empty().then(move || args) + } + /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes /// the contents of the string, whether it's a raw string, and the span of the literal in the @@ -376,57 +486,31 @@ impl Write { /// ``` #[allow(clippy::too_many_lines)] 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 mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); - let mut expr: Option = None; - if is_write { - expr = match parser.parse_expr().map_err(|mut err| err.cancel()) { - Ok(p) => Some(p.into_inner()), - Err(_) => return (None, None), - }; - // might be `writeln!(foo)` - if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() { - return (None, expr); + let expr = if is_write { + match parser.parse_expr().map(|e| e.into_inner()).map_err(|mut e| e.cancel()) { + // write!(e, ...) + Ok(p) if parser.eat(&token::Comma) => Some(p), + // write!(e) or error + e => return (None, e.ok()), } - } + } else { + None + }; let fmtstr = match parser.parse_str_lit() { Ok(fmtstr) => fmtstr, Err(_) => return (None, expr), }; - let tmp = fmtstr.symbol.as_str(); - let mut args = vec![]; - let mut fmt_parser = Parser::new(&tmp, None, None, false, ParseMode::Format); - while let Some(piece) = fmt_parser.next() { - if !fmt_parser.errors.is_empty() { - return (None, expr); - } - if let Piece::NextArgument(arg) = piece { - if !self.in_debug_impl && arg.format.ty == "?" { - // FIXME: modify rustc's fmt string parser to give us the current span - span_lint(cx, USE_DEBUG, parser.prev_token.span, "use of `Debug`-based formatting"); - } - args.push(arg); - } - } + + let args = match self.parse_fmt_string(cx, &fmtstr) { + Some(args) => args, + None => return (Some(fmtstr), expr), + }; + let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL }; - let mut idx = 0; + let mut unnamed_args = args.get_unnamed(); loop { - const SIMPLE: FormatSpec<'_> = FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: "", - ty_span: None, - }; if !parser.eat(&token::Comma) { return (Some(fmtstr), expr); } @@ -435,52 +519,26 @@ impl Write { } else { return (Some(fmtstr), None); }; - match &token_expr.kind { + let (fmt_spans, span) = match &token_expr.kind { ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => { - let mut all_simple = true; - let mut seen = false; - for arg in &args { - match arg.position { - ArgumentImplicitlyIs(n) | ArgumentIs(n) => { - if n == idx { - all_simple &= arg.format == SIMPLE; - seen = true; - } - }, - ArgumentNamed(_) => {}, - } - } - if all_simple && seen { - span_lint(cx, lint, token_expr.span, "literal with an empty format string"); - } - idx += 1; + (unnamed_args.next().unwrap_or(&[]), token_expr.span) }, - ExprKind::Assign(lhs, rhs, _) => { - if_chain! { - if let ExprKind::Lit(ref lit) = rhs.kind; - if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)); - if let ExprKind::Path(_, p) = &lhs.kind; - then { - let mut all_simple = true; - let mut seen = false; - for arg in &args { - match arg.position { - ArgumentImplicitlyIs(_) | ArgumentIs(_) => {}, - ArgumentNamed(name) => { - if *p == name { - seen = true; - all_simple &= arg.format == SIMPLE; - } - }, - } - } - if all_simple && seen { - span_lint(cx, lint, rhs.span, "literal with an empty format string"); - } - } - } + ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) { + (ExprKind::Path(_, p), ExprKind::Lit(lit)) + if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => + { + (args.get_named(p), rhs.span) + }, + _ => continue, }, - _ => idx += 1, + _ => { + unnamed_args.next(); + continue; + }, + }; + + if !fmt_spans.is_empty() { + span_lint(cx, lint, span, "literal with an empty format string"); } } } -- cgit 1.4.1-3-g733a5 From 4c1047167d20460dcb84e3d947787ce91d5fd0d4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 1 Mar 2021 13:28:36 -0500 Subject: More specific spans for `use_debug` lint --- clippy_lints/src/write.rs | 2 +- tests/ui/print.stderr | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index b5470c3bc70..210ddc7465f 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -456,7 +456,7 @@ impl Write { if !self.in_debug_impl && arg.format.ty == "?" { // FIXME: modify rustc's fmt string parser to give us the current span - span_lint(cx, USE_DEBUG, str.span, "use of `Debug`-based formatting"); + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); } args.push(arg, span); diff --git a/tests/ui/print.stderr b/tests/ui/print.stderr index 208d9532628..1754c418381 100644 --- a/tests/ui/print.stderr +++ b/tests/ui/print.stderr @@ -1,8 +1,8 @@ error: use of `Debug`-based formatting - --> $DIR/print.rs:11:19 + --> $DIR/print.rs:11:20 | LL | write!(f, "{:?}", 43.1415) - | ^^^^^^ + | ^^^^ | = note: `-D clippy::use-debug` implied by `-D warnings` @@ -33,10 +33,10 @@ LL | print!("Hello {:?}", "World"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of `Debug`-based formatting - --> $DIR/print.rs:28:12 + --> $DIR/print.rs:28:19 | LL | print!("Hello {:?}", "World"); - | ^^^^^^^^^^^^ + | ^^^^ error: use of `print!` --> $DIR/print.rs:30:5 @@ -45,10 +45,10 @@ LL | print!("Hello {:#?}", "#orld"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of `Debug`-based formatting - --> $DIR/print.rs:30:12 + --> $DIR/print.rs:30:19 | LL | print!("Hello {:#?}", "#orld"); - | ^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From a7fa2a6fa88477ab2542bb15347e3085d9547f95 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 1 Mar 2021 16:31:04 -0500 Subject: Add suggestion to `write_literal` and `print_literal` Don't lint on a mixture of raw and regular strings Fix spans in format strings --- clippy_lints/src/write.rs | 62 +++++++++++++++++------ tests/ui/print_literal.stderr | 70 +++++++++++++++++++++++--- tests/ui/write_literal.stderr | 70 +++++++++++++++++++++++--- tests/ui/write_literal_2.rs | 27 ++++++++++ tests/ui/write_literal_2.stderr | 106 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 305 insertions(+), 30 deletions(-) create mode 100644 tests/ui/write_literal_2.rs create mode 100644 tests/ui/write_literal_2.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 210ddc7465f..e416eab7914 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,10 +1,11 @@ use std::borrow::Cow; -use std::ops::Range; +use std::iter; +use std::ops::{Deref, Range}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, Path, StrLit, StrStyle}; -use rustc_ast::token; +use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; +use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; use rustc_lexer::unescape::{self, EscapeError}; @@ -438,7 +439,7 @@ impl Write { fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str: &StrLit) -> Option { use rustc_parse_format::{ParseMode, Parser, Piece}; - let str_sym = str.symbol.as_str(); + let str_sym = str.symbol_unescaped.as_str(); let style = match str.style { StrStyle::Cooked => None, StrStyle::Raw(n) => Some(n as usize), @@ -514,21 +515,17 @@ impl Write { if !parser.eat(&token::Comma) { return (Some(fmtstr), expr); } + + let comma_span = parser.prev_token.span; let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) { expr } else { return (Some(fmtstr), None); }; - let (fmt_spans, span) = match &token_expr.kind { - ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => { - (unnamed_args.next().unwrap_or(&[]), token_expr.span) - }, + let (fmt_spans, lit) = match &token_expr.kind { + ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit), ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) { - (ExprKind::Path(_, p), ExprKind::Lit(lit)) - if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => - { - (args.get_named(p), rhs.span) - }, + (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit), _ => continue, }, _ => { @@ -537,8 +534,45 @@ impl Write { }, }; + let replacement: String = match lit.token.kind { + LitKind::Integer | LitKind::Float | LitKind::Err => continue, + LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { + lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") + }, + LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { + lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") + }, + LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, + LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str().deref() { + "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", + "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, + "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\", + "\\'" => "'", + "{" => "{{", + "}" => "}}", + x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with("\\") => continue, + x => x, + } + .into(), + LitKind::Bool => lit.token.symbol.as_str().deref().into(), + }; + if !fmt_spans.is_empty() { - span_lint(cx, lint, span, "literal with an empty format string"); + span_lint_and_then( + cx, + lint, + token_expr.span, + "literal with an empty format string", + |diag| { + diag.multipart_suggestion( + "try this", + iter::once((comma_span.to(token_expr.span), String::new())) + .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement))) + .collect(), + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index e284aece236..54a4084c89e 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -5,66 +5,120 @@ LL | print!("Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::print-literal` implied by `-D warnings` +help: try this + | +LL | print!("Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:26:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("Hello {} world", world); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:27:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:32:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("hello {1}", "world"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:32:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("{0} world", "hello"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:33:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("{1} hello", "world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:33:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("world {0}", "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:36:35 + --> $DIR/print_literal.rs:36:29 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("hello {bar}", bar = "world"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:36:50 + --> $DIR/print_literal.rs:36:44 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("{foo} world", foo = "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:37:35 + --> $DIR/print_literal.rs:37:29 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("{bar} hello", bar = "world"); + | ^^^^^-- error: literal with an empty format string - --> $DIR/print_literal.rs:37:50 + --> $DIR/print_literal.rs:37:44 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("world {foo}", foo = "hello"); + | ^^^^^ -- error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index e54d89ecf29..507a78e8280 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -5,66 +5,120 @@ LL | write!(&mut v, "Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` +help: try this + | +LL | write!(&mut v, "Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:31:44 | LL | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "Hello {} world", world); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:32:34 | LL | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:37:33 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "hello {1}", "world"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:37:42 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{0} world", "hello"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:38:33 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{1} hello", "world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:38:42 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "world {0}", "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:41:43 + --> $DIR/write_literal.rs:41:37 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "hello {bar}", bar = "world"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:41:58 + --> $DIR/write_literal.rs:41:52 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{foo} world", foo = "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:42:43 + --> $DIR/write_literal.rs:42:37 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{bar} hello", bar = "world"); + | ^^^^^-- error: literal with an empty format string - --> $DIR/write_literal.rs:42:58 + --> $DIR/write_literal.rs:42:52 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "world {foo}", foo = "hello"); + | ^^^^^ -- error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs new file mode 100644 index 00000000000..f341e8215e1 --- /dev/null +++ b/tests/ui/write_literal_2.rs @@ -0,0 +1,27 @@ +#![allow(unused_must_use)] +#![warn(clippy::write_literal)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + writeln!(&mut v, "{}", "{hello}"); + writeln!(&mut v, r"{}", r"{hello}"); + writeln!(&mut v, "{}", '\''); + writeln!(&mut v, "{}", '"'); + writeln!(&mut v, r"{}", '"'); // don't lint + writeln!(&mut v, r"{}", '\''); + writeln!( + &mut v, + "some {}", + "hello \ + world!" + ); + writeln!( + &mut v, + "some {}\ + {} \\ {}", + "1", "2", "3", + ); +} diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr new file mode 100644 index 00000000000..5b488358011 --- /dev/null +++ b/tests/ui/write_literal_2.stderr @@ -0,0 +1,106 @@ +error: literal with an empty format string + --> $DIR/write_literal_2.rs:9:28 + | +LL | writeln!(&mut v, "{}", "{hello}"); + | ^^^^^^^^^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` +help: try this + | +LL | writeln!(&mut v, "{{hello}}"); + | ^^^^^^^^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:10:29 + | +LL | writeln!(&mut v, r"{}", r"{hello}"); + | ^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, r"{{hello}}"); + | ^^^^^^^^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:11:28 + | +LL | writeln!(&mut v, "{}", '/''); + | ^^^^ + | +help: try this + | +LL | writeln!(&mut v, "'"); + | ^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:12:28 + | +LL | writeln!(&mut v, "{}", '"'); + | ^^^ + | +help: try this + | +LL | writeln!(&mut v, "/""); + | ^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:14:29 + | +LL | writeln!(&mut v, r"{}", '/''); + | ^^^^ + | +help: try this + | +LL | writeln!(&mut v, r"'"); + | ^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:18:9 + | +LL | / "hello / +LL | | world!" + | |_______________^ + | +help: try this + | +LL | "some hello / +LL | world!" + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:9 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | "some 1{} / {}", "2", "3", + | ^ -- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:14 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | 2 / {}", +LL | "1", "3", + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:19 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | {} / 3", +LL | "1", "2", + | + +error: aborting due to 9 previous errors + -- cgit 1.4.1-3-g733a5 From d45873b4c151898876da73746300ac29d5448e62 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 16 Mar 2021 12:06:41 -0400 Subject: Remove SmallVec --- clippy_lints/src/write.rs | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index e416eab7914..12a47a6b703 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -14,7 +14,6 @@ use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, Span, DUMMY_SP}; -use smallvec::SmallVec; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -359,8 +358,8 @@ fn newline_span(fmtstr: &StrLit) -> Span { /// empty format string. #[derive(Default)] struct SimpleFormatArgs { - unnamed: Vec>, - named: Vec<(Symbol, SmallVec<[Span; 1]>)>, + unnamed: Vec>, + named: Vec<(Symbol, Vec)>, } impl SimpleFormatArgs { fn get_unnamed(&self) -> impl Iterator { @@ -396,11 +395,11 @@ impl SimpleFormatArgs { ArgumentIs(n) | ArgumentImplicitlyIs(n) => { if self.unnamed.len() <= n { // Use a dummy span to mark all unseen arguments. - self.unnamed.resize_with(n, || SmallVec::from([DUMMY_SP])); + self.unnamed.resize_with(n, || vec![DUMMY_SP]); if arg.format == SIMPLE { - self.unnamed.push(SmallVec::from([span])); + self.unnamed.push(vec![span]); } else { - self.unnamed.push(SmallVec::new()); + self.unnamed.push(Vec::new()); } } else { let args = &mut self.unnamed[n]; @@ -410,7 +409,7 @@ impl SimpleFormatArgs { // Replace the dummy span, if it exists. ([dummy @ DUMMY_SP], true) => *dummy = span, ([_, ..], true) => args.push(span), - ([_, ..], false) => *args = SmallVec::new(), + ([_, ..], false) => *args = Vec::new(), } } }, @@ -420,12 +419,12 @@ impl SimpleFormatArgs { // A non-empty format string has been seen already. [] => (), [_, ..] if arg.format == SIMPLE => x.1.push(span), - [_, ..] => x.1 = SmallVec::new(), + [_, ..] => x.1 = Vec::new(), } } else if arg.format == SIMPLE { - self.named.push((n, SmallVec::from([span]))); + self.named.push((n, vec![span])); } else { - self.named.push((n, SmallVec::new())); + self.named.push((n, Vec::new())); } }, }; @@ -436,16 +435,16 @@ impl Write { /// Parses a format string into a collection of spans for each argument. This only keeps track /// of empty format arguments. Will also lint usages of debug format strings outside of debug /// impls. - fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str: &StrLit) -> Option { + fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option { use rustc_parse_format::{ParseMode, Parser, Piece}; - let str_sym = str.symbol_unescaped.as_str(); - let style = match str.style { + let str_sym = str_lit.symbol_unescaped.as_str(); + let style = match str_lit.style { StrStyle::Cooked => None, StrStyle::Raw(n) => Some(n as usize), }; - let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str.span), false, ParseMode::Format); + let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format); let mut args = SimpleFormatArgs::default(); while let Some(arg) = parser.next() { @@ -453,7 +452,10 @@ impl Write { Piece::String(_) => continue, Piece::NextArgument(arg) => arg, }; - let span = parser.arg_places.last().map_or(DUMMY_SP, |&x| str.span.from_inner(x)); + let span = parser + .arg_places + .last() + .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x)); if !self.in_debug_impl && arg.format.ty == "?" { // FIXME: modify rustc's fmt string parser to give us the current span @@ -489,7 +491,11 @@ impl Write { fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); let expr = if is_write { - match parser.parse_expr().map(|e| e.into_inner()).map_err(|mut e| e.cancel()) { + match parser + .parse_expr() + .map(rustc_ast::ptr::P::into_inner) + .map_err(|mut e| e.cancel()) + { // write!(e, ...) Ok(p) if parser.eat(&token::Comma) => Some(p), // write!(e) or error @@ -543,14 +549,14 @@ impl Write { lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") }, LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, - LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str().deref() { + LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() { "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\", "\\'" => "'", "{" => "{{", "}" => "}}", - x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with("\\") => continue, + x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue, x => x, } .into(), -- cgit 1.4.1-3-g733a5 From 0743e841f07b7c41f65187fbf71f90bb5dc71982 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 16 Mar 2021 11:06:34 -0500 Subject: Don't re-export clippy_utils::* --- clippy_lints/src/assertions_on_constants.rs | 2 +- clippy_lints/src/assign_ops.rs | 6 +++--- clippy_lints/src/atomic_ordering.rs | 2 +- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/await_holding_invalid.rs | 2 +- clippy_lints/src/bit_mask.rs | 2 +- clippy_lints/src/blocks_in_if_conditions.rs | 2 +- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/bytecount.rs | 2 +- clippy_lints/src/cargo_common_metadata.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 3 +-- clippy_lints/src/casts/cast_possible_truncation.rs | 3 +-- clippy_lints/src/casts/cast_ptr_alignment.rs | 6 ++---- clippy_lints/src/casts/cast_sign_loss.rs | 10 ++++------ clippy_lints/src/casts/mod.rs | 3 +-- clippy_lints/src/casts/ptr_as_ptr.rs | 10 ++++------ clippy_lints/src/casts/unnecessary_cast.rs | 3 +-- clippy_lints/src/checked_conversions.rs | 3 +-- clippy_lints/src/cognitive_complexity.rs | 3 +-- clippy_lints/src/collapsible_if.rs | 3 +-- clippy_lints/src/collapsible_match.rs | 4 ++-- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/copies.rs | 4 ++-- clippy_lints/src/create_dir.rs | 2 +- clippy_lints/src/default.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/derive.rs | 4 ++-- clippy_lints/src/disallowed_method.rs | 2 +- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/double_comparison.rs | 3 +-- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/duration_subsec.rs | 2 +- clippy_lints/src/entry.rs | 4 ++-- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/eq_op.rs | 2 +- clippy_lints/src/eta_reduction.rs | 3 +-- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/excessive_bools.rs | 2 +- clippy_lints/src/exit.rs | 2 +- clippy_lints/src/explicit_write.rs | 2 +- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/float_equality_without_abs.rs | 2 +- clippy_lints/src/float_literal.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/format.rs | 4 ++-- clippy_lints/src/formatting.rs | 2 +- clippy_lints/src/from_over_into.rs | 4 ++-- clippy_lints/src/from_str_radix_10.rs | 3 +-- clippy_lints/src/functions.rs | 8 ++++---- clippy_lints/src/future_not_send.rs | 4 ++-- clippy_lints/src/get_last_with_len.rs | 2 +- clippy_lints/src/identity_op.rs | 2 +- clippy_lints/src/if_let_mutex.rs | 2 +- clippy_lints/src/if_let_some_result.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 10 +++++----- clippy_lints/src/implicit_return.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/infinite_iter.rs | 3 +-- clippy_lints/src/inherent_impl.rs | 2 +- clippy_lints/src/inherent_to_string.rs | 3 +-- clippy_lints/src/inline_fn_without_body.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/let_if_seq.rs | 2 +- clippy_lints/src/let_underscore.rs | 3 +-- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/literal_representation.rs | 6 +++--- clippy_lints/src/loops/empty_loop.rs | 2 +- clippy_lints/src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/loops/for_kv_map.rs | 4 ++-- clippy_lints/src/loops/iter_next_loop.rs | 3 +-- clippy_lints/src/loops/manual_flatten.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 4 ++-- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/loops/needless_collect.rs | 4 ++-- clippy_lints/src/loops/needless_range_loop.rs | 8 ++++---- clippy_lints/src/loops/single_element_loop.rs | 2 +- clippy_lints/src/loops/utils.rs | 2 +- clippy_lints/src/loops/while_immutable_condition.rs | 2 +- clippy_lints/src/loops/while_let_on_iterator.rs | 8 ++++---- clippy_lints/src/macro_use.rs | 2 +- clippy_lints/src/main_recursion.rs | 3 +-- clippy_lints/src/manual_async_fn.rs | 4 ++-- clippy_lints/src/manual_map.rs | 7 ++----- clippy_lints/src/manual_non_exhaustive.rs | 2 +- clippy_lints/src/manual_ok_or.rs | 2 +- clippy_lints/src/manual_strip.rs | 4 ++-- clippy_lints/src/manual_unwrap_or.rs | 14 +++++++------- clippy_lints/src/map_clone.rs | 4 ++-- clippy_lints/src/map_identity.rs | 2 +- clippy_lints/src/map_unit_fn.rs | 2 +- clippy_lints/src/matches.rs | 16 ++++++++-------- clippy_lints/src/mem_discriminant.rs | 2 +- clippy_lints/src/mem_forget.rs | 2 +- clippy_lints/src/mem_replace.rs | 2 +- clippy_lints/src/methods/bind_instead_of_map.rs | 2 +- clippy_lints/src/methods/clone_on_copy.rs | 2 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 2 +- clippy_lints/src/methods/filetype_is_file.rs | 2 +- clippy_lints/src/methods/filter_flat_map.rs | 2 +- clippy_lints/src/methods/filter_map.rs | 2 +- clippy_lints/src/methods/filter_map_flat_map.rs | 2 +- clippy_lints/src/methods/filter_map_identity.rs | 2 +- clippy_lints/src/methods/filter_map_map.rs | 2 +- clippy_lints/src/methods/filter_map_next.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 2 +- clippy_lints/src/methods/flat_map_identity.rs | 2 +- .../src/methods/from_iter_instead_of_collect.rs | 2 +- clippy_lints/src/methods/get_unwrap.rs | 2 +- clippy_lints/src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/inspect_for_each.rs | 3 +-- clippy_lints/src/methods/into_iter_on_ref.rs | 2 +- clippy_lints/src/methods/iter_count.rs | 2 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/methods/iter_skip_next.rs | 2 +- clippy_lints/src/methods/iterator_step_by_zero.rs | 2 +- .../src/methods/manual_saturating_arithmetic.rs | 2 +- clippy_lints/src/methods/map_collect_result_unit.rs | 2 +- clippy_lints/src/methods/map_flatten.rs | 2 +- clippy_lints/src/methods/map_unwrap_or.rs | 4 ++-- clippy_lints/src/methods/mod.rs | 9 ++++----- clippy_lints/src/methods/option_as_ref_deref.rs | 2 +- clippy_lints/src/methods/option_map_or_none.rs | 2 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 4 ++-- clippy_lints/src/methods/search_is_some.rs | 2 +- clippy_lints/src/methods/skip_while_next.rs | 2 +- clippy_lints/src/methods/string_extend_chars.rs | 2 +- clippy_lints/src/methods/suspicious_map.rs | 4 ++-- clippy_lints/src/methods/uninit_assumed_init.rs | 2 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 7 +++---- clippy_lints/src/methods/unnecessary_fold.rs | 2 +- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 2 +- clippy_lints/src/methods/useless_asref.rs | 2 +- clippy_lints/src/minmax.rs | 2 +- clippy_lints/src/misc.rs | 4 ++-- clippy_lints/src/missing_const_for_fn.rs | 4 ++-- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/multiple_crate_versions.rs | 2 +- clippy_lints/src/mut_key.rs | 2 +- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/needless_arbitrary_self_type.rs | 2 +- clippy_lints/src/needless_bool.rs | 4 ++-- clippy_lints/src/needless_borrow.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/needless_question_mark.rs | 11 +++++------ clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 5 ++--- clippy_lints/src/new_without_default.rs | 6 +++--- clippy_lints/src/non_copy_const.rs | 5 ++--- clippy_lints/src/open_options.rs | 2 +- clippy_lints/src/option_env_unwrap.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 19 +++++++++---------- clippy_lints/src/overflow_check_conditional.rs | 2 +- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/panic_unimplemented.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 2 +- clippy_lints/src/ptr.rs | 4 ++-- clippy_lints/src/ptr_eq.rs | 4 ++-- clippy_lints/src/question_mark.rs | 7 +++---- clippy_lints/src/ranges.rs | 7 +++---- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/redundant_field_names.rs | 2 +- clippy_lints/src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/ref_option_ref.rs | 2 +- clippy_lints/src/reference.rs | 4 ++-- clippy_lints/src/regex.rs | 2 +- clippy_lints/src/repeat_once.rs | 2 +- clippy_lints/src/returns.rs | 3 +-- clippy_lints/src/self_assignment.rs | 2 +- clippy_lints/src/semicolon_if_nothing_returned.rs | 2 +- clippy_lints/src/serde_api.rs | 2 +- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 2 +- clippy_lints/src/size_of_in_element_count.rs | 2 +- clippy_lints/src/slow_vector_initialization.rs | 4 ++-- clippy_lints/src/stable_sort_primitive.rs | 4 +--- clippy_lints/src/strings.rs | 4 ++-- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/suspicious_trait_impl.rs | 4 ++-- clippy_lints/src/swap.rs | 4 ++-- clippy_lints/src/temporary_assignment.rs | 2 +- clippy_lints/src/to_digit_is_some.rs | 2 +- clippy_lints/src/to_string_in_display.rs | 2 +- clippy_lints/src/trait_bounds.rs | 2 +- clippy_lints/src/transmute/mod.rs | 2 +- clippy_lints/src/transmute/transmute_float_to_int.rs | 2 +- clippy_lints/src/transmute/transmute_int_to_bool.rs | 2 +- clippy_lints/src/transmute/transmute_int_to_char.rs | 2 +- clippy_lints/src/transmute/transmute_int_to_float.rs | 2 +- clippy_lints/src/transmute/transmute_ptr_to_ptr.rs | 2 +- clippy_lints/src/transmute/transmute_ptr_to_ref.rs | 2 +- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 2 +- .../transmute/transmutes_expressible_as_ptr_casts.rs | 2 +- .../src/transmute/unsound_collection_transmute.rs | 2 +- clippy_lints/src/transmute/useless_transmute.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_lints/src/transmuting_null.rs | 2 +- clippy_lints/src/try_err.rs | 2 +- clippy_lints/src/types/borrowed_box.rs | 2 +- clippy_lints/src/types/box_vec.rs | 3 +-- clippy_lints/src/types/linked_list.rs | 3 +-- clippy_lints/src/types/mod.rs | 8 ++++---- clippy_lints/src/types/option_option.rs | 3 +-- clippy_lints/src/types/rc_buffer.rs | 3 +-- clippy_lints/src/types/redundant_allocation.rs | 3 +-- clippy_lints/src/types/utils.rs | 6 ++---- clippy_lints/src/types/vec_box.rs | 3 +-- clippy_lints/src/undropped_manually_drops.rs | 2 +- clippy_lints/src/unicode.rs | 2 +- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 7 +++---- clippy_lints/src/unit_types/unit_arg.rs | 8 +++----- clippy_lints/src/unit_types/unit_cmp.rs | 3 +-- clippy_lints/src/unnamed_address.rs | 2 +- clippy_lints/src/unnecessary_sort_by.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 4 ++-- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_lints/src/unused_self.rs | 3 +-- clippy_lints/src/unused_unit.rs | 3 +-- clippy_lints/src/unwrap.rs | 2 +- clippy_lints/src/unwrap_in_result.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/useless_conversion.rs | 4 ++-- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/inspector.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 3 +-- clippy_lints/src/utils/mod.rs | 2 -- clippy_lints/src/vec.rs | 2 +- clippy_lints/src/vec_init_then_push.rs | 2 +- clippy_lints/src/vec_resize_to_zero.rs | 7 +++---- clippy_lints/src/verbose_file_reads.rs | 2 +- clippy_lints/src/wildcard_dependencies.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 2 +- clippy_lints/src/zero_sized_map_values.rs | 3 +-- 243 files changed, 344 insertions(+), 399 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index bae4d4957a9..16905781c56 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,7 +1,7 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; +use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 8581e176e0c..5b8fb9b436a 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,8 +1,8 @@ -use crate::utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method}; -use crate::utils::{higher, sugg}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method}; +use clippy_utils::{higher, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { $($trait_name:ident),+) => { match $op { $(hir::BinOpKind::$trait_name => { - let [krate, module] = crate::utils::paths::OPS_MODULE; + let [krate, module] = clippy_utils::paths::OPS_MODULE; let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")]; let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) { trait_id diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 231e74e16c0..dfb18199325 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -1,5 +1,5 @@ -use crate::utils::match_def_path; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::match_def_path; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 6be6722375d..f805f716e6b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,7 +1,7 @@ //! checks for attributes -use crate::utils::match_panic_def_id; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::match_panic_def_id; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 3a3296c2c3d..68eee0520b3 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,5 +1,5 @@ -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{match_def_path, paths}; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 2b8400344f5..f7daf3dab49 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::sugg::Sugg; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 343c4821c56..badcf8d2a43 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,7 +1,7 @@ -use crate::utils::{differing_macro_contexts, get_parent_expr}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::{differing_macro_contexts, get_parent_expr}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 1303613b8c4..58d9aa9c005 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ -use crate::utils::{eq_expr_value, get_trait_def_id, in_macro, paths}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index c1ca787c1e5..846ac08e93a 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,7 +1,7 @@ -use crate::utils::{contains_name, get_pat_name, paths, single_segment_path}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; +use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index f0ef482164a..fce5c559672 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -2,8 +2,8 @@ use std::path::PathBuf; -use crate::utils::run_lints; use clippy_utils::diagnostics::span_lint; +use clippy_utils::run_lints; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 4b660188d82..869deecfbd5 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_constant; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; @@ -6,8 +7,6 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use crate::utils::in_constant; - use super::{utils, CAST_LOSSLESS}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 75d7c55b637..833ad122e0d 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,10 +1,9 @@ +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use clippy_utils::diagnostics::span_lint; - use super::{utils, CAST_POSSIBLE_TRUNCATION}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 6a45ec61c54..5208156ffd2 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_hir_ty_cfg_dependant; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; use rustc_target::abi::LayoutOf; -use if_chain::if_chain; - -use crate::utils::is_hir_ty_cfg_dependant; - use super::CAST_PTR_ALIGNMENT; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 2965a840a82..bf722d0a3f4 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,13 +1,11 @@ +use crate::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{method_chain_args, sext}; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use if_chain::if_chain; - -use crate::consts::{constant, Constant}; -use crate::utils::{method_chain_args, sext}; -use clippy_utils::diagnostics::span_lint; - use super::CAST_SIGN_LOSS; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b726bd75f1d..d9e172c01a7 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -12,14 +12,13 @@ mod ptr_as_ptr; mod unnecessary_cast; mod utils; +use clippy_utils::is_hir_ty_cfg_dependant; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::is_hir_ty_cfg_dependant; - declare_clippy_lint! { /// **What it does:** Checks for casts from any numerical to a float type where /// the receiving type cannot store all values from the original type without diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 2c25f865cb8..9113e5a0920 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,17 +1,15 @@ use std::borrow::Cow; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::meets_msrv; +use clippy_utils::sugg::Sugg; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, TypeAndMut}; use rustc_semver::RustcVersion; -use if_chain::if_chain; - -use crate::utils::meets_msrv; -use crate::utils::sugg::Sugg; -use clippy_utils::diagnostics::span_lint_and_sugg; - use super::PTR_AS_PTR; const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index a84e658024a..9ed359922fd 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; @@ -8,8 +9,6 @@ use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; -use crate::utils::numeric_literal::NumericLiteral; - use super::UNNECESSARY_CAST; pub(super) fn check( diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 8b62e315e04..ed46cac493a 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{meets_msrv, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -11,8 +12,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{meets_msrv, SpanlessEq}; - const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); declare_clippy_lint! { diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 8134f3af3ef..4cc542f723c 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::LimitStack; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; @@ -12,8 +13,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::{sym, BytePos}; -use crate::utils::LimitStack; - declare_clippy_lint! { /// **What it does:** Checks for methods with high cognitive complexity. /// diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index dbe70f90732..dae5c86bd44 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -14,14 +14,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; +use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::sugg::Sugg; - declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed /// by `&&`-combining their conditions. diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 5ff2518bd94..e2b3686ddf0 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,6 +1,6 @@ -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{path_to_local, SpanlessEq}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{path_to_local, SpanlessEq}; use if_chain::if_chain; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 5411905791c..d601cb7c224 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ -use crate::utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index f86f0da6d6f..46093a16571 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,6 +1,6 @@ -use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, if_sequence}; use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::{get_parent_expr, if_sequence}; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 01ca2837a99..ac890c90c97 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,6 +1,6 @@ -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 0b074c088d4..d046e894792 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,6 +1,6 @@ -use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 62ec2f8ce48..1415f8e235a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,7 +1,7 @@ -use crate::utils::{get_parent_node, in_macro, is_allowed}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::{get_parent_node, in_macro, is_allowed}; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index a7f2106e357..834136f910d 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_allowed, is_automatically_derived, match_def_path}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::paths; use clippy_utils::ty::is_copy; +use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 16023bc53b0..ded0a0fff54 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,5 +1,5 @@ -use crate::utils::fn_def_id; use clippy_utils::diagnostics::span_lint; +use clippy_utils::fn_def_id; use rustc_data_structures::fx::FxHashSet; use rustc_hir::Expr; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index d9952325c4b..5fa278b2897 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,6 +1,6 @@ -use crate::utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind}; diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 549720feb2d..1d291565ec1 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -1,6 +1,7 @@ //! Lint on unnecessary double comparisons. Some examples: use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -8,8 +9,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::eq_expr_value; - declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. /// diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index fe86adcec45..7e7ec017bbb 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,6 +1,6 @@ -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::ty::is_copy; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 40c75f568ae..746c1f6df91 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -8,8 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use crate::consts::{constant, Constant}; -use crate::utils::paths; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index cc774831491..25eb048448c 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,8 +1,8 @@ -use crate::utils::SpanlessEq; -use crate::utils::{get_item_name, paths}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::SpanlessEq; +use clippy_utils::{get_item_name, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d2d8acb443b..0ecc0bc3eb6 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -1,6 +1,6 @@ //! lint on enum variants that are prefixed or suffixed by the same characters -use crate::utils::camel_case; +use clippy_utils::camel_case; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::is_present_in_source; use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 1b2f66fb628..6d7046ac8b7 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,7 +1,7 @@ -use crate::utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of}; use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2557e9f2796..99253555a95 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,6 +3,7 @@ use clippy_utils::higher; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, type_is_unsafe_function}; +use clippy_utils::{is_adjusted, iter_input_pats}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath}; @@ -11,8 +12,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_adjusted, iter_input_pats}; - declare_clippy_lint! { /// **What it does:** Checks for closures which just call another function where /// the function can be called directly. `unsafe` functions or calls where types diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 3711ba4a7ee..5d5b67de843 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -1,5 +1,5 @@ -use crate::utils::{get_parent_expr, path_to_local, path_to_local_id}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 683e0435351..6c9652fd87d 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,5 +1,5 @@ -use crate::utils::{attr_by_name, in_macro, match_path_ast}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{attr_by_name, in_macro, match_path_ast}; use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index adfb644b199..635b6d83eab 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_entrypoint_fn, match_def_path, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_entrypoint_fn, match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 0861cba4675..4146b7baa10 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_expn_of, match_function_call, paths}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 5efe465a667..52a5a7acf0d 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,6 +1,6 @@ -use crate::utils::{is_expn_of, match_panic_def_id, method_chain_args}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index b3593d328cc..0c59db360f9 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,5 +1,5 @@ -use crate::utils::{match_def_path, paths, sugg}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{match_def_path, paths, sugg}; use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 6446fe62a64..1ca5c685a75 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,5 +1,5 @@ -use crate::utils::numeric_literal; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::numeric_literal; use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 8b53384ecbd..0b5f0379ce6 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,8 +2,8 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index db27225707d..a33f987b423 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,8 +1,8 @@ -use crate::utils::paths; -use crate::utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 4dadd2ba936..b10e83c0ea8 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -1,5 +1,5 @@ -use crate::utils::differing_macro_contexts; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::differing_macro_contexts; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5eea4d14759..e5ec245e502 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,6 +1,6 @@ -use crate::utils::paths::INTO; -use crate::utils::{match_def_path, meets_msrv}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths::INTO; +use clippy_utils::{match_def_path, meets_msrv}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index b9a44f26381..3da5bc95b6d 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; @@ -8,8 +9,6 @@ use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; -use crate::utils::sugg::Sugg; - declare_clippy_lint! { /// **What it does:** /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)` diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 37288d7a0b2..730492fc7e3 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,10 +1,10 @@ -use crate::utils::{ - attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, - path_to_local, return_ty, trait_ref_of_method, -}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; +use clippy_utils::{ + attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, + path_to_local, return_ty, trait_ref_of_method, +}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 36960e7f51b..04730ace887 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -1,5 +1,5 @@ -use crate::utils; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::return_ty; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if let FnKind::Closure = kind { return; } - let ret_ty = utils::return_ty(cx, hir_id); + let ret_ty = return_ty(cx, hir_id); if let Opaque(id, subst) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(id); let mut is_future = false; diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index 6f14ede0ecb..cbcef567c53 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -1,9 +1,9 @@ //! lint on using `x.get(x.len() - 1)` instead of `x.last()` -use crate::utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 385ea020328..8bed5e1bf64 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -7,8 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{constant_simple, Constant}; -use crate::utils::{clip, unsext}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{clip, unsext}; declare_clippy_lint! { /// **What it does:** Checks for identity operations, e.g., `x + 0`. diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index c2ec8b3ffd1..4aab43256bf 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,6 +1,6 @@ -use crate::utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, ExprKind, MatchSource}; diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 59094d1147a..6e9280c8c7e 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -1,5 +1,5 @@ -use crate::utils::method_chain_args; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index c4e87300b7b..0b5bf060d4c 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,6 @@ -use crate::utils; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -58,7 +58,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl LateLintPass<'_> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if !utils::meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) { return; } @@ -67,7 +67,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } // We only care about the top-most `if` in the chain - if utils::parent_node_is_if_expr(expr, cx) { + if parent_node_is_if_expr(expr, cx) { return; } @@ -77,12 +77,12 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let Some(ref then_expr) = then_block.expr; if let ExprKind::Call(ref then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; - if utils::match_qpath(then_call_qpath, &utils::paths::OPTION_SOME); + if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); if let ExprKind::Block(ref els_block, _) = els.kind; if els_block.stmts.is_empty(); if let Some(ref els_expr) = els_block.expr; if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; - if utils::match_qpath(els_call_qpath, &utils::paths::OPTION_NONE); + if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); then { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 70ea9a92279..6863645a92d 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,5 +1,5 @@ -use crate::utils::match_panic_def_id; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::match_panic_def_id; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 3d2798f3fb4..5207c628987 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,5 +1,5 @@ -use crate::utils::{in_macro, match_qpath, SpanlessEq}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{in_macro, match_qpath, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4c0f976f79a..94d39019608 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,8 +1,8 @@ //! lint on indexing and slicing operations use crate::consts::{constant, Constant}; -use crate::utils::higher; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::higher; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index addd95c6eae..fb35bc1e780 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{implements_trait, match_type}; +use clippy_utils::{get_trait_def_id, higher, match_qpath, paths}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{get_trait_def_id, higher, match_qpath, paths}; - declare_clippy_lint! { /// **What it does:** Checks for iteration that is guaranteed to be infinite. /// diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index f838ce190dc..5b2e70e3ce9 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,7 +1,7 @@ //! lint on inherent implementations -use crate::utils::in_macro; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_macro; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 6b29feeb4ed..b023e13e846 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; - declare_clippy_lint! { /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. /// diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 87fe5123cf6..20f00bd51ba 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -1,7 +1,7 @@ //! checks for `#[inline]` on trait methods without bodies -use crate::utils::sugg::DiagnosticBuilderExt; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::DiagnosticBuilderExt; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index a644a4113ae..717f2ea84f4 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,6 +1,6 @@ -use crate::utils::{get_item_name, get_parent_as_impl, is_allowed}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 4b7e02398fb..2d7d9c9befb 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,6 +1,6 @@ -use crate::utils::{path_to_local_id, visitors::LocalUsedVisitor}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; +use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index eee2503b481..7e3a76ded2c 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -7,8 +8,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, paths}; - declare_clippy_lint! { /// **What it does:** Checks for `let _ = ` /// where expr is #[must_use] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f479d756a33..9b2876ddc11 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -42,7 +42,7 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate rustc_typeck; -use crate::utils::parse_msrv; +use clippy_utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 0848aaed815..a2f093e6ef7 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,5 +1,5 @@ -use crate::utils::{in_macro, trait_ref_of_method}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{in_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::{ walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index dd303bc0a01..7fd55151226 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -1,12 +1,12 @@ //! Lints concerned with the grouping of digits with underscores in integral or //! floating-point literal expressions. -use crate::utils::{ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{ in_macro, numeric_literal::{NumericLiteral, Radix}, }; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs index d52a061fc8f..dda09fecdf9 100644 --- a/clippy_lints/src/loops/empty_loop.rs +++ b/clippy_lints/src/loops/empty_loop.rs @@ -1,6 +1,6 @@ use super::EMPTY_LOOP; -use crate::utils::{is_in_panic_handler, is_no_std_crate}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_in_panic_handler, is_no_std_crate}; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 87ce4e4c20c..f14dbb1d642 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -1,9 +1,9 @@ use super::{ get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP, }; -use crate::utils::{get_enclosing_block, is_integer_const}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_enclosing_block, is_integer_const}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 259e95e5ef4..92aa2beb66d 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,8 +1,8 @@ use super::EXPLICIT_ITER_LOOP; -use crate::utils::{match_trait_method, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{match_trait_method, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index bf037f392e0..8f18f54119b 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,9 +1,9 @@ use super::FOR_KV_MAP; -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{paths, sugg}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{paths, sugg}; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index 27a03562800..9148fbfd497 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -1,11 +1,10 @@ use super::ITER_NEXT_LOOP; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_trait_method; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -use crate::utils::is_trait_method; - pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool { if is_trait_method(cx, arg, sym::Iterator) { span_lint( diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 43580d6071a..be87f67d300 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,7 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; -use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index ff68d136999..af6d56a89af 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,9 +1,9 @@ use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; -use crate::utils::sugg::Sugg; -use crate::utils::{get_enclosing_block, higher, path_to_local, sugg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 2a372c6307e..20291491998 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -18,7 +18,7 @@ mod while_immutable_condition; mod while_let_loop; mod while_let_on_iterator; -use crate::utils::higher; +use clippy_utils::higher; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 175aee53a9a..f4e4064ba1d 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,6 +1,6 @@ use super::MUT_RANGE_BOUND; -use crate::utils::{higher, path_to_local}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{higher, path_to_local}; use if_chain::if_chain; use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index a3b731eefa0..5594fc7b046 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,9 +1,9 @@ use super::NEEDLESS_COLLECT; -use crate::utils::sugg::Sugg; -use crate::utils::{is_trait_method, path_to_local_id, paths}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index ca679d7ebe1..3c40d54cb42 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -1,11 +1,11 @@ use super::NEEDLESS_RANGE_LOOP; -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{ - contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq, -}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{ + contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq, +}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 172eb963ae3..8451c1c6130 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,6 +1,6 @@ use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP}; -use crate::utils::single_segment_path; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::single_segment_path; use clippy_utils::source::{indent_of, snippet}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index b85676570b4..bb409c48532 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,5 +1,5 @@ -use crate::utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use clippy_utils::ty::{has_iter_method, implements_trait}; +use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index a037a06de81..cad9ff8489a 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -1,7 +1,7 @@ use super::WHILE_IMMUTABLE_CONDITION; use crate::consts::constant; -use crate::utils::usage::mutated_variables; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::usage::mutated_variables; use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 29ad9e735cc..57fc6250a9a 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,12 +1,12 @@ use super::utils::{LoopNestVisitor, Nesting}; use super::WHILE_LET_ON_ITERATOR; -use crate::utils::usage::mutated_variables; -use crate::utils::{ - get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, -}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{ + get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 92f95645734..d573c297838 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,5 +1,5 @@ -use crate::utils::in_macro; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index 6c0308cbd92..07d8a440aea 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; +use clippy_utils::{is_entrypoint_fn, is_no_std_crate}; use if_chain::if_chain; use rustc_hir::{Crate, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{is_entrypoint_fn, is_no_std_crate}; - declare_clippy_lint! { /// **What it does:** Checks for recursion using the entrypoint. /// diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 1db5cf56962..5d88ff3b99f 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,6 @@ -use crate::utils::match_function_call; -use crate::utils::paths::FUTURE_FROM_GENERATOR; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::match_function_call; +use clippy_utils::paths::FUTURE_FROM_GENERATOR; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index fd563bec769..cd9594737bf 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,11 +1,8 @@ -use crate::{ - map_unit_fn::OPTION_MAP_UNIT_FN, - matches::MATCH_AS_REF, - utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs}, -}; +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 705c1ec1859..a9a89ae5659 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,5 +1,5 @@ -use crate::utils::meets_msrv; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::meets_msrv; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index a1e5c752f83..9bfae602c40 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,7 +1,7 @@ -use crate::utils::{match_qpath, path_to_local_id, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_qpath, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind}; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index bc4e255b03d..9da37efddac 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,8 +1,8 @@ use crate::consts::{constant, Constant}; -use crate::utils::usage::mutated_variables; -use crate::utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 615e2d5c2af..4f139f8d39a 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,9 +1,9 @@ use crate::consts::constant_simple; -use crate::utils; -use crate::utils::{in_constant, path_to_local_id, sugg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{in_constant, match_qpath, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; @@ -75,19 +75,19 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { 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), + match_qpath(some_qpath, &clippy_utils::paths::OPTION_NONE), PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => - utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR), + match_qpath(err_qpath, &clippy_utils::paths::RESULT_ERR), _ => false, } ); 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 match_qpath(unwrap_qpath, &clippy_utils::paths::OPTION_SOME) + || match_qpath(unwrap_qpath, &clippy_utils::paths::RESULT_OK); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); - if !utils::usage::contains_return_break_continue_macro(or_arm.body); + if !contains_return_break_continue_macro(or_arm.body); then { Some(or_arm) } else { diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index fd39052871b..cfcc705eabc 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,6 +1,6 @@ -use crate::utils::is_trait_method; -use crate::utils::remove_blocks; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::remove_blocks; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use if_chain::if_chain; diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 84ec23c4e2f..75191e1f9ee 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,6 +1,6 @@ -use crate::utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 3bd4a965765..d4764b5ccff 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,7 +1,7 @@ -use crate::utils::{iter_input_pats, method_chain_args}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{iter_input_pats, method_chain_args}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 1aa09b82822..e94c7094cac 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1,16 +1,16 @@ use crate::consts::{constant, miri_to_const, Constant}; -use crate::utils::sugg::Sugg; -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, strip_pat_refs, -}; -use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{ + get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, + path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, strip_pat_refs, +}; +use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -1616,9 +1616,9 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; - use crate::utils::{is_trait_method, match_qpath, paths}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; + use clippy_utils::{is_trait_method, match_qpath, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index 85353d4cdde..7895ba9f1e0 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -1,7 +1,7 @@ -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::walk_ptrs_ty_depth; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs index 1a8cb82514b..c13802e3953 100644 --- a/clippy_lints/src/mem_forget.rs +++ b/clippy_lints/src/mem_forget.rs @@ -1,5 +1,5 @@ -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, paths}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 87cb66a6770..426c108d89f 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ -use crate::utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diagnostic_assoc_item; use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 7d6104ebb7f..0ba8a98a018 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,8 +1,8 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; -use crate::utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::match_type; +use clippy_utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index d1c7789a241..949152cf789 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -1,5 +1,5 @@ -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index b49ae42b12f..03c949d5c31 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,5 +1,5 @@ -use crate::utils::paths; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 1c673c11d65..e7bffa66b3f 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,5 +1,5 @@ -use crate::utils::is_expn_of; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_expn_of; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 005c94a6018..39d2f15dbc8 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,6 +1,6 @@ -use crate::utils::{get_parent_expr, paths}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::match_type; +use clippy_utils::{get_parent_expr, paths}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs index 4820bb137c1..2f3a3c55ab5 100644 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ b/clippy_lints/src/methods/filter_flat_map.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index af91370df20..2cb476acb2b 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,6 +1,6 @@ -use crate::utils::{is_trait_method, path_to_local_id, SpanlessEq}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::{is_trait_method, path_to_local_id, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs index 5294ef97528..b1a4dc98eb8 100644 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ b/clippy_lints/src/methods/filter_map_flat_map.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 4461baf4f7c..80598d88508 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs index 8f17350054b..0b0a8b1dd3b 100644 --- a/clippy_lints/src/methods/filter_map_map.rs +++ b/clippy_lints/src/methods/filter_map_map.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 8fbadd1d457..ba57abd16c9 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ -use crate::utils::{is_trait_method, meets_msrv}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet; +use clippy_utils::{is_trait_method, meets_msrv}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index af16ea19007..6cd24334414 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 669ac1f743f..034ea6c6562 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_trait_method, match_qpath, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_trait_method, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 5d0a07992c8..2095a60e44b 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,6 +1,6 @@ -use crate::utils::{get_trait_def_id, paths, sugg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index ac855419d00..b122a0a0b89 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -1,8 +1,8 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{get_parent_expr, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 56de7a8bc5e..d10a540c24e 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,8 +1,8 @@ use super::INEFFICIENT_TO_STRING; -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index c9bbfbc37eb..7fd3ef1a622 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{source_map::Span, sym}; -use crate::utils::is_trait_method; - use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index a862b55e139..cfe749cf361 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,6 +1,6 @@ -use crate::utils::{match_trait_method, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::has_iter_method; +use clippy_utils::{match_trait_method, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index bfd62ff1f0d..fd3d53816a1 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,6 +1,6 @@ use crate::methods::derefs_to_slice; -use crate::utils::paths; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index ce599682bbe..feebf8b8209 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,8 +1,8 @@ use crate::methods::derefs_to_slice; -use crate::utils::{get_parent_expr, higher}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, higher}; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index f2b2dd8c097..a12f672739c 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index 6100613bcac..ea01860b456 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 51c1ab0484e..3baa580314f 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 0c8a002e359..f16699322d1 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,5 +1,5 @@ -use crate::utils::match_qpath; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_qpath; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index b59998fc8b4..e4402b2da21 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 6f5e723fbfb..4bc52b036a8 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 8ef02b4a641..deb4b4492b5 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,8 +1,8 @@ -use crate::utils::meets_msrv; -use crate::utils::usage::mutated_variables; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::meets_msrv; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::mutated_variables; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index af8fe7abd96..cbfb350ebb1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -55,6 +55,10 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{ + contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls, + method_chain_args, paths, return_ty, single_segment_path, SpanlessEq, +}; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -68,11 +72,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{sym, SymbolStr}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{ - contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls, - method_chain_args, paths, return_ty, single_segment_path, SpanlessEq, -}; - declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index f921d7b16c0..d11ede080dc 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,7 @@ -use crate::utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index e8b057c2d74..d93db2c22e4 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ -use crate::utils::{match_qpath, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_qpath, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index c6459648cd5..e252abc177a 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,5 +1,5 @@ -use crate::utils::differing_macro_contexts; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::differing_macro_contexts; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 1b43802a08e..4880d13f39a 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,8 +1,8 @@ -use crate::utils::eager_or_lazy::is_lazyness_candidate; -use crate::utils::{contains_return, get_trait_def_id, last_path_segment, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::is_lazyness_candidate; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; +use clippy_utils::{contains_return, get_trait_def_id, last_path_segment, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 18e1064b018..13546dc1779 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ -use crate::utils::{is_trait_method, strip_pat_refs}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index 8995226191b..3db83785b59 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -1,5 +1,5 @@ -use crate::utils::is_trait_method; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index d9b97168490..52b26a36fe3 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,5 +1,5 @@ -use crate::utils::method_chain_args; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index fd4651dd182..7015bd54c35 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,6 +1,6 @@ -use crate::utils::usage::mutated_variables; -use crate::utils::{expr_or_init, is_trait_method}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{expr_or_init, is_trait_method}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 071856c3ba6..f2f6ef4be6c 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ -use crate::utils::{match_def_path, match_qpath, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, match_qpath, paths}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 670134c68f2..48d905ab833 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,14 +1,13 @@ -use crate::utils::usage::mutated_variables; -use crate::utils::{is_trait_method, match_qpath, path_to_local_id, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::sym; -use if_chain::if_chain; - use super::UNNECESSARY_FILTER_MAP; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 3ccde57de3f..1268fd4bda9 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,6 @@ -use crate::utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs}; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 3874673bf9f..a86185bf0a6 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,7 +1,7 @@ -use crate::utils::{eager_or_lazy, usage}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{eager_or_lazy, usage}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 13c95e33fef..b5505af0f7e 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,7 @@ -use crate::utils::{get_parent_expr, match_trait_method, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; +use clippy_utils::{get_parent_expr, match_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 6a4e70e50af..776f4c7b741 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, match_trait_method, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, match_trait_method, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 03d07287005..026ea50936a 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -18,8 +18,8 @@ use rustc_span::source_map::{ExpnKind, Span}; use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; -use crate::utils::sugg::Sugg; -use crate::utils::{ +use clippy_utils::sugg::Sugg; +use clippy_utils::{ get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, last_path_segment, match_qpath, unsext, SpanlessEq, }; diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index aba4accccb1..23554669d97 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,7 @@ -use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 1b00cf2c75d..6a52de4f713 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::sext; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sext; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index 6eaeaebe781..584daa5e119 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -1,7 +1,7 @@ //! lint on multiple versions of a crate being used -use crate::utils::run_lints; use clippy_utils::diagnostics::span_lint; +use clippy_utils::run_lints; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{Crate, CRATE_HIR_ID}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 39aec79734c..41bd07bcf1e 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,5 +1,5 @@ -use crate::utils::{match_def_path, paths, trait_ref_of_method}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, paths, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeFoldable; diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index c11ccc94890..ef33e41a5fa 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,5 +1,5 @@ -use crate::utils::higher; use clippy_utils::diagnostics::span_lint; +use clippy_utils::higher; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index fb8895e08d0..533c5a22db0 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,5 @@ -use crate::utils::{higher, is_direct_expn_of}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{higher, is_direct_expn_of}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index c62ff7323f3..3e2b2782ed5 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,5 +1,5 @@ -use crate::utils::in_macro; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 4c67304d23e..db7b3423ad9 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -2,10 +2,10 @@ //! //! This lint is **warn** by default -use crate::utils::sugg::Sugg; -use crate::utils::{is_expn_of, parent_node_is_if_expr}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_expn_of, parent_node_is_if_expr}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 72436b51dcb..79d84da2dfc 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -2,8 +2,8 @@ //! //! This lint is **warn** by default -use crate::utils::is_automatically_derived; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_automatically_derived; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index fc8ee6cb8ef..075bbf9a426 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,8 +1,8 @@ -use crate::utils::ptr::get_spans; -use crate::utils::{get_trait_def_id, is_self, paths}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index ecf48d082af..99e85e6683c 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_ok_ctor, is_some_ctor, meets_msrv}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -8,9 +10,6 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; -use crate::utils; -use if_chain::if_chain; - declare_clippy_lint! { /// **What it does:** /// Suggests alternatives for useless applications of `?` in terminating expressions @@ -161,7 +160,7 @@ fn is_some_or_ok_call<'a>( // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res); + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; @@ -182,7 +181,7 @@ fn is_some_or_ok_call<'a>( let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); // Check for Option MSRV - let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); + let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); if outer_is_some && inner_is_some && meets_option_msrv { return Some(SomeOkCall::SomeCall(expr, inner_expr)); } @@ -196,7 +195,7 @@ fn is_some_or_ok_call<'a>( let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); // Must meet Result MSRV - let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); + let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { return Some(SomeOkCall::OkCall(expr, inner_expr)); } 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 e291885d34d..4b935c7b906 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; +use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; 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::{self, paths}; - declare_clippy_lint! { /// **What it does:** /// Checks for the usage of negated comparison operators on types which only implement @@ -61,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) { + if let Some(id) = get_trait_def_id(cx, &paths::ORD) { implements_trait(cx, ty, id, &[]) } else { return; diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 9fbb6b02742..502e5e4bf37 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,7 +1,7 @@ -use crate::utils::paths; -use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, return_ty}; use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::paths; +use clippy_utils::sugg::DiagnosticBuilderExt; +use clippy_utils::{get_trait_def_id, return_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 5db614497e3..b86ae1426ef 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -5,6 +5,8 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_constant; +use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -19,9 +21,6 @@ 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; -use if_chain::if_chain; - // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. declare_clippy_lint! { diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 8e8aaa67afa..c61dff4b8e0 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -1,5 +1,5 @@ -use crate::utils::paths; use clippy_utils::diagnostics::span_lint; +use clippy_utils::paths; use clippy_utils::ty::match_type; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index 343159f9ae2..a0bc324e026 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -1,5 +1,5 @@ -use crate::utils::is_direct_expn_of; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_direct_expn_of; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index eab08a58320..a76a4a33f1f 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,10 @@ -use crate::utils; -use crate::utils::eager_or_lazy; -use crate::utils::paths; -use crate::utils::sugg::Sugg; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath}; use if_chain::if_chain; - use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -110,7 +109,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 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| { + get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { let mut should_wrap = false; if let Some(Expr { @@ -160,15 +159,15 @@ fn detect_option_if_let_else<'tcx>( expr: &'_ Expr<'tcx>, ) -> Option { if_chain! { - if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly + if !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; if arms.len() == 2; 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 match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !utils::usage::contains_return_break_continue_macro(arms[0].body); - if !utils::usage::contains_return_break_continue_macro(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])?; diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 524b96921fe..cf667c6e805 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -1,5 +1,5 @@ -use crate::utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; +use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 880951f3e3e..d32b937b209 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ -use crate::utils::{find_macro_calls, return_ty}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{find_macro_calls, return_ty}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index ad4ed831941..d06e7f8fe1e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_expn_of, match_panic_call}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_expn_of, match_panic_call}; use if_chain::if_chain; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 06985cef4ff..1251ddd9a02 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -1,5 +1,5 @@ -use crate::utils::is_automatically_derived; use clippy_utils::diagnostics::span_lint_hir; +use clippy_utils::is_automatically_derived; use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index c14273840c4..9a5b1c3b944 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,7 +1,7 @@ use std::cmp; -use crate::utils::is_self_ty; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_self_ty; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use if_chain::if_chain; diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index dd63cf99e42..288e0e4586e 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,5 +1,5 @@ -use crate::utils::last_path_segment; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::last_path_segment; use rustc_hir::{ intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, QPath, Stmt, StmtKind, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 779eddcc170..be686b1b0cd 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,10 +1,10 @@ //! Checks for usage of `&Vec[_]` and `&String`. -use crate::utils::ptr::get_spans; -use crate::utils::{is_allowed, match_qpath, paths}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; +use clippy_utils::{is_allowed, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 82408c639b1..5796c59c8b3 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -1,5 +1,5 @@ -use crate::utils; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; @@ -42,7 +42,7 @@ 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) { + if in_macro(expr.span) { return; } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index c4686623487..2054255a7c9 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,5 +1,8 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -8,10 +11,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::sugg::Sugg; -use crate::utils::{eq_expr_value, match_def_path, match_qpath, paths}; -use clippy_utils::diagnostics::span_lint_and_sugg; - declare_clippy_lint! { /// **What it does:** Checks for expressions that could be replaced by the question mark operator. /// diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index d476d95256f..95b21489eb5 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,6 +1,9 @@ use crate::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path}; +use clippy_utils::{higher, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -14,10 +17,6 @@ use rustc_span::sym; use rustc_span::symbol::Ident; use std::cmp::Ordering; -use crate::utils::sugg::Sugg; -use crate::utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path}; -use crate::utils::{higher, SpanlessEq}; - declare_clippy_lint! { /// **What it does:** Checks for zipping a collection with the range of /// `0.._.len()`. diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 3d799328bb5..60da2bcb04a 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ -use crate::utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index fd11150bce2..f77be6fdf04 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,5 +1,5 @@ -use crate::utils::meets_msrv; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::meets_msrv; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index da8339a795a..32b57698ec5 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,5 +1,5 @@ -use crate::utils::meets_msrv; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::meets_msrv; use clippy_utils::source::snippet; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 452fef0694f..0922cfa494e 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,5 +1,5 @@ -use crate::utils::last_path_segment; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::last_path_segment; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index ec2acb6a0ab..d6336389b0a 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,7 +1,7 @@ -use crate::utils::in_macro; -use crate::utils::sugg::Sugg; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index bfa21ba9c97..1cc332de894 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index b39e9d5a78c..63e5ec69e66 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::in_macro; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 59d3c0ca5f0..8995ae431ad 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; +use clippy_utils::{fn_def_id, in_macro, match_qpath}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; @@ -13,8 +14,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; -use crate::utils::{fn_def_id, in_macro, match_qpath}; - declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently /// returned. diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs index 320d1a8dbe3..e7925c4fbde 100644 --- a/clippy_lints/src/self_assignment.rs +++ b/clippy_lints/src/self_assignment.rs @@ -1,5 +1,5 @@ -use crate::utils::eq_expr_value; use clippy_utils::diagnostics::span_lint; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index e43947025b9..f61af15fbed 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,6 +1,6 @@ -use crate::utils::{in_macro, sugg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{in_macro, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 632715852c5..169f7d26285 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ -use crate::utils::{get_trait_def_id, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{get_trait_def_id, paths}; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 8ef58b6d563..612d2fd84cb 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,6 +1,6 @@ -use crate::utils::{contains_name, higher, iter_input_pats}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; +use clippy_utils::{contains_name, higher, iter_input_pats}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind, diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 6d15a6c444e..c9d72aabb6a 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,5 +1,5 @@ -use crate::utils::in_macro; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use if_chain::if_chain; use rustc_ast::{Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 8920d446b9c..09e00866815 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -1,8 +1,8 @@ //! Lint on use of `size_of` or `size_of_val` of T in an expression //! expecting a count of T -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_hir::BinOpKind; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 7f0f21084af..d55a83f1613 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,6 +1,6 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{get_enclosing_block, match_qpath, SpanlessEq}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 85e4bb806e7..65790375c73 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -1,8 +1,6 @@ -use crate::utils::{is_slice_of_primitives, sugg::Sugg}; use clippy_utils::diagnostics::span_lint_and_then; - +use clippy_utils::{is_slice_of_primitives, sugg::Sugg}; use if_chain::if_chain; - use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 626166f9008..760f5e3b432 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,8 +1,8 @@ -use crate::utils::SpanlessEq; -use crate::utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; +use clippy_utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 364c99adc69..1e5d3c17e3b 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,4 +1,4 @@ -use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index ef14b6c5136..0f024c9a11e 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,5 +1,5 @@ -use crate::utils::{get_trait_def_id, trait_ref_of_method}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{get_trait_def_id, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -159,7 +159,7 @@ fn check_binop( expected_ops: &[hir::BinOpKind], ) -> Option<&'static str> { let mut trait_ids = vec![]; - let [krate, module] = crate::utils::paths::OPS_MODULE; + let [krate, module] = clippy_utils::paths::OPS_MODULE; for &t in traits { let path = [krate, module, t]; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 83fe3c49959..14519eaa962 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,8 +1,8 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{differing_macro_contexts, eq_expr_value}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, eq_expr_value}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 75a13cb9187..8ef25dc816c 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -1,5 +1,5 @@ -use crate::utils::is_adjusted; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_adjusted; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index abc9b2fe88e..c66a596c784 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,5 +1,5 @@ -use crate::utils::match_def_path; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 42605c01089..42ec14c31b5 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index cfcc8da9910..3ff27c3bcf4 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,6 +1,6 @@ -use crate::utils::{in_macro, SpanlessHash}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::{in_macro, SpanlessHash}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index c1870f5208b..47d58bd30db 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -12,7 +12,7 @@ mod useless_transmute; mod utils; mod wrong_transmute; -use crate::utils::{in_constant, match_def_path, paths}; +use clippy_utils::{in_constant, match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index f61b5362b5a..72489f27cd3 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_FLOAT_TO_INT; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use if_chain::if_chain; use rustc_ast as ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/transmute/transmute_int_to_bool.rs b/clippy_lints/src/transmute/transmute_int_to_bool.rs index ebdd1eba744..cc0a5643e2a 100644 --- a/clippy_lints/src/transmute/transmute_int_to_bool.rs +++ b/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_BOOL; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index afecfc3d701..8f884e6a4a1 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_CHAR; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::Expr; diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index c762a7ab885..2b6a4cff81e 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_FLOAT; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 820c56a215e..7b646bfc0c6 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_PTR_TO_PTR; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 049210e555c..f14eef93645 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,7 +1,7 @@ use super::utils::get_type_snippet; use super::TRANSMUTE_PTR_TO_REF; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, QPath}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 993ef8698f8..d105e37abf9 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -1,7 +1,7 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; -use crate::utils::sugg; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; +use clippy_utils::sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index da7d1016d97..e2c6d130f3c 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,7 +1,7 @@ use super::utils::can_be_expressed_as_pointer_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; -use crate::utils::sugg; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index 9bea88be7da..de9277e016e 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -1,7 +1,7 @@ use super::utils::is_layout_incompatible; use super::UNSOUND_COLLECTION_TRANSMUTE; -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, paths}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 29ffe03b673..445bcf60fa7 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -1,6 +1,6 @@ use super::USELESS_TRANSMUTE; -use crate::utils::sugg; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 0633697687a..c6d0d63b0b5 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,4 +1,4 @@ -use crate::utils::last_path_segment; +use clippy_utils::last_path_segment; use clippy_utils::source::snippet; use clippy_utils::ty::is_normalizable; use if_chain::if_chain; diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 3a2b1359a54..d42cdde110e 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{match_qpath, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_qpath, paths}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 1fce03b4f47..e61058c2749 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,7 +1,7 @@ -use crate::utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index eab81b1e246..1721fcfdcf4 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,6 +1,6 @@ -use crate::utils::{match_path, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::{match_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/clippy_lints/src/types/box_vec.rs b/clippy_lints/src/types/box_vec.rs index 6d04a47a5c8..d8b1953457c 100644 --- a/clippy_lints/src/types/box_vec.rs +++ b/clippy_lints/src/types/box_vec.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_ty_param_diagnostic_item; use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::is_ty_param_diagnostic_item; - use super::BOX_VEC; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/types/linked_list.rs b/clippy_lints/src/types/linked_list.rs index e9e1995f6a5..a9fbe7aa315 100644 --- a/clippy_lints/src/types/linked_list.rs +++ b/clippy_lints/src/types/linked_list.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{match_def_path, paths}; use rustc_hir::{self as hir, def_id::DefId}; use rustc_lint::LateContext; -use crate::utils::{match_def_path, paths}; - use super::LINKEDLIST; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 5103a259559..c0cd48e94b2 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -36,8 +36,8 @@ use rustc_target::spec::abi::Abi; use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; -use crate::utils::paths; -use crate::utils::{clip, comparisons, differing_macro_contexts, int_bits, match_path, sext, unsext}; +use clippy_utils::paths; +use clippy_utils::{clip, comparisons, differing_macro_contexts, int_bits, match_path, sext, unsext}; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. @@ -618,7 +618,7 @@ fn detect_absurd_comparison<'tcx>( ) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; use crate::types::ExtremeType::{Maximum, Minimum}; - use crate::utils::comparisons::{normalize_comparison, Rel}; + use clippy_utils::comparisons::{normalize_comparison, Rel}; // absurd comparison only makes sense on primitive types // primitive types don't implement comparison operators with each other @@ -860,7 +860,7 @@ fn upcast_comparison_bounds_err<'tcx>( rhs: &'tcx Expr<'_>, invert: bool, ) { - use crate::utils::comparisons::Rel; + use clippy_utils::comparisons::Rel; if let Some((lb, ub)) = lhs_bounds { if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) { diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index 79c5a32de2c..b2692c48076 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_ty_param_diagnostic_item; use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::is_ty_param_diagnostic_item; - use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index d5fc23f2e39..ef629a35d10 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; - use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 71c014f96f7..c0c1f340583 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item}; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use crate::utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item}; - use super::{utils, REDUNDANT_ALLOCATION}; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/types/utils.rs b/clippy_lints/src/types/utils.rs index 4d64748f998..45f891ed718 100644 --- a/clippy_lints/src/types/utils.rs +++ b/clippy_lints/src/types/utils.rs @@ -1,11 +1,9 @@ +use clippy_utils::last_path_segment; +use if_chain::if_chain; use rustc_hir::{GenericArg, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::source_map::Span; -use crate::utils::last_path_segment; - -use if_chain::if_chain; - pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); if_chain! { diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 8cedb0ede2b..d2c373db261 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::last_path_segment; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; @@ -9,8 +10,6 @@ use rustc_span::symbol::sym; use rustc_target::abi::LayoutOf; use rustc_typeck::hir_ty_to_ty; -use crate::utils::last_path_segment; - use super::VEC_BOX; pub(super) fn check( diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 943573a2b53..b6749069176 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -1,6 +1,6 @@ -use crate::utils::{match_function_call, paths}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{match_function_call, paths}; use rustc_hir::{lang_items, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index c37bbf297f8..d81e31f5a21 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -1,5 +1,5 @@ -use crate::utils::is_allowed; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_allowed; use clippy_utils::source::snippet; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index d0456347f8a..bad3e488bb6 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,5 @@ -use crate::utils::{get_trait_def_id, paths}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, StmtKind}; diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 55715cc6bf3..8698a718bbd 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,12 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; +use clippy_utils::source::snippet_with_macro_callsite; use rustc_errors::Applicability; use rustc_hir::{Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use crate::utils::diagnostics::span_lint_and_then; -use crate::utils::higher; -use crate::utils::source::snippet_with_macro_callsite; - use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index d4ef645fc8d..925ab577099 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -1,12 +1,10 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; -use if_chain::if_chain; - -use crate::utils::diagnostics::span_lint_and_then; -use crate::utils::source::{indent_of, reindent_multiline, snippet_opt}; - use super::{utils, UNIT_ARG}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index f9c0374c86d..b3077dec5d8 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,9 +1,8 @@ +use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use crate::utils::diagnostics::span_lint; - use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index 2c51a8556d9..d5bc3de6698 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -1,5 +1,5 @@ -use crate::utils::{match_def_path, paths}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index aa83f6a74be..e23bab5eba0 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,5 @@ -use crate::utils::sugg::Sugg; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index cb20192b683..9e227164695 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,6 @@ -use crate::utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; +use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 42ff1809ff4..512d505dfb5 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,8 +1,8 @@ #![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; +use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::over; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 31fd61f99f2..9990052e114 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_try, match_trait_method, paths}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 4642bf07c45..aef4ce75915 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::visitors::LocalUsedVisitor; use if_chain::if_chain; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::visitors::LocalUsedVisitor; - declare_clippy_lint! { /// **What it does:** Checks methods that contain a `self` argument but don't use it /// diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index c45b851211c..329ea49024b 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::position_before_rarrow; use if_chain::if_chain; use rustc_ast::ast; @@ -8,8 +9,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use clippy_utils::diagnostics::span_lint_and_sugg; - declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. /// diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 75b2f2da602..fb29acca18a 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,6 +1,6 @@ -use crate::utils::{differing_macro_contexts, usage::is_potentially_mutated}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, 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}; diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index a85f3ce5c9c..0d745813beb 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,6 +1,6 @@ -use crate::utils::{method_chain_args, return_ty}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{method_chain_args, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 9a42c833470..116cb8b1e1c 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,6 @@ -use crate::utils::{in_macro, meets_msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; +use clippy_utils::{in_macro, meets_msrv}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1eb152daac8..3e1b69e676b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,8 +1,8 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{get_parent_expr, match_def_path, match_trait_method, paths}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 3dd190ba440..2de5b1a628e 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,7 +1,7 @@ //! A group of attributes that can be attached to Rust code in order //! to generate a clippy lint detecting said code automatically. -use crate::utils::get_attr; +use clippy_utils::get_attr; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::walk_list; use rustc_data_structures::fx::FxHashMap; diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 9e3973e1d51..191ff6e4f16 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -1,6 +1,6 @@ //! checks for attributes -use crate::utils::get_attr; +use clippy_utils::get_attr; use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index a566362ae78..c496ff1fb24 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,9 +1,8 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq}; - use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; +use clippy_utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; use rustc_ast::visit::FnKind; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index be9a07f8d7c..d8b31344e6d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -3,5 +3,3 @@ pub mod conf; pub mod inspector; #[cfg(feature = "internal-lints")] pub mod internal_lints; - -pub use clippy_utils::*; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 9e142413365..1af9583887f 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,7 +1,7 @@ use crate::consts::{constant, Constant}; use crate::rustc_target::abi::LayoutOf; -use crate::utils::higher; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use if_chain::if_chain; diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 3ef55a82a17..8b696ed1c84 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,7 +1,7 @@ -use crate::utils::{match_def_path, path_to_local, path_to_local_id, paths}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs index 9a2fb1414a1..e035d3c5cad 100644 --- a/clippy_lints/src/vec_resize_to_zero.rs +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -1,15 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; +use rustc_ast::LitKind; use rustc_errors::Applicability; +use rustc_hir as hir; 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::LitKind; -use rustc_hir as hir; - declare_clippy_lint! { /// **What it does:** Finds occurrences of `Vec::resize(0, an_int)` /// diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs index 01dc54dc5fd..ec209b30951 100644 --- a/clippy_lints/src/verbose_file_reads.rs +++ b/clippy_lints/src/verbose_file_reads.rs @@ -1,5 +1,5 @@ -use crate::utils::paths; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths; use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; diff --git a/clippy_lints/src/wildcard_dependencies.rs b/clippy_lints/src/wildcard_dependencies.rs index 8f96b962279..60c3489a449 100644 --- a/clippy_lints/src/wildcard_dependencies.rs +++ b/clippy_lints/src/wildcard_dependencies.rs @@ -1,5 +1,5 @@ -use crate::utils::run_lints; use clippy_utils::diagnostics::span_lint; +use clippy_utils::run_lints; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 424ca2a4c2f..51c1117d206 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,5 +1,5 @@ -use crate::utils::in_macro; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 82466da6862..2abd033e2a0 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; @@ -9,8 +10,6 @@ use rustc_span::sym; use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; -use crate::utils::paths; - declare_clippy_lint! { /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. /// -- cgit 1.4.1-3-g733a5 From 565400d1f990399726498b66e87d6866533d1b1d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 17 Mar 2021 09:31:20 -0500 Subject: Add clippy_utils::paths imports --- clippy_lints/src/assign_ops.rs | 4 ++-- clippy_lints/src/manual_unwrap_or.rs | 10 +++++----- clippy_lints/src/suspicious_trait_impl.rs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 5b8fb9b436a..bc6eec0051a 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method}; -use clippy_utils::{higher, sugg}; +use clippy_utils::{higher, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { $($trait_name:ident),+) => { match $op { $(hir::BinOpKind::$trait_name => { - let [krate, module] = clippy_utils::paths::OPS_MODULE; + let [krate, module] = paths::OPS_MODULE; let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")]; let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) { trait_id diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 4f139f8d39a..f296d6a1a15 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, match_qpath, path_to_local_id, sugg}; +use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; @@ -75,16 +75,16 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { PatKind::Path(ref some_qpath) => - match_qpath(some_qpath, &clippy_utils::paths::OPTION_NONE), + match_qpath(some_qpath, &paths::OPTION_NONE), PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => - match_qpath(err_qpath, &clippy_utils::paths::RESULT_ERR), + match_qpath(err_qpath, &paths::RESULT_ERR), _ => false, } ); let unwrap_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; - if match_qpath(unwrap_qpath, &clippy_utils::paths::OPTION_SOME) - || match_qpath(unwrap_qpath, &clippy_utils::paths::RESULT_OK); + if match_qpath(unwrap_qpath, &paths::OPTION_SOME) + || match_qpath(unwrap_qpath, &paths::RESULT_OK); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if !contains_return_break_continue_macro(or_arm.body); diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 0f024c9a11e..99e3d818b79 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{get_trait_def_id, trait_ref_of_method}; +use clippy_utils::{get_trait_def_id, paths, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -159,7 +159,7 @@ fn check_binop( expected_ops: &[hir::BinOpKind], ) -> Option<&'static str> { let mut trait_ids = vec![]; - let [krate, module] = clippy_utils::paths::OPS_MODULE; + let [krate, module] = paths::OPS_MODULE; for &t in traits { let path = [krate, module, t]; -- cgit 1.4.1-3-g733a5 From 94fb2b58a30f46cd462bea088ea523ed8f4d9580 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 13 Mar 2021 17:17:10 +0900 Subject: move chars_cmp and chars_next_cmp to its own modules --- clippy_lints/src/methods/chars_cmp.rs | 54 +++++++++++++++++++++++++++ clippy_lints/src/methods/chars_next_cmp.rs | 8 ++++ clippy_lints/src/methods/mod.rs | 59 +++--------------------------- 3 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 clippy_lints/src/methods/chars_cmp.rs create mode 100644 clippy_lints/src/methods/chars_next_cmp.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs new file mode 100644 index 00000000000..f9af06d200c --- /dev/null +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -0,0 +1,54 @@ +use crate::utils::{method_chain_args, single_segment_path}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_lint::Lint; +use rustc_middle::ty; +use rustc_span::sym; + +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. +pub(super) fn check( + cx: &LateContext<'_>, + info: &crate::methods::BinaryExprInfo<'_>, + chain_methods: &[&str], + lint: &'static Lint, + suggest: &str, +) -> bool { + if_chain! { + if let Some(args) = method_chain_args(info.chain, chain_methods); + if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind; + if arg_char.len() == 1; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + if let Some(segment) = single_segment_path(qpath); + if segment.ident.name == sym::Some; + then { + let mut applicability = Applicability::MachineApplicable; + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); + + if *self_ty.kind() != ty::Str { + return false; + } + + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{}` method", suggest), + "like this", + format!("{}{}.{}({})", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), + suggest, + snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), + applicability, + ); + + return true; + } + } + + false +} diff --git a/clippy_lints/src/methods/chars_next_cmp.rs b/clippy_lints/src/methods/chars_next_cmp.rs new file mode 100644 index 00000000000..a6701d8830e --- /dev/null +++ b/clippy_lints/src/methods/chars_next_cmp.rs @@ -0,0 +1,8 @@ +use rustc_lint::LateContext; + +use super::CHARS_NEXT_CMP; + +/// Checks for the `CHARS_NEXT_CMP` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cbfb350ebb1..d288c51907e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,5 +1,7 @@ mod bind_instead_of_map; mod bytes_nth; +mod chars_cmp; +mod chars_next_cmp; mod clone_on_copy; mod clone_on_ref_ptr; mod expect_fun_call; @@ -2024,7 +2026,7 @@ struct BinaryExprInfo<'a> { /// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) { macro_rules! lint_with_both_lhs_and_rhs { - ($func:ident, $cx:expr, $info:ident) => { + ($func:expr, $cx:expr, $info:ident) => { if !$func($cx, $info) { ::std::mem::swap(&mut $info.chain, &mut $info.other); if $func($cx, $info) { @@ -2034,67 +2036,18 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr }; } - lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info); + lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info); lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info); lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info); lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info); } -/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. -fn lint_chars_cmp( - cx: &LateContext<'_>, - info: &BinaryExprInfo<'_>, - chain_methods: &[&str], - lint: &'static Lint, - suggest: &str, -) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind; - if arg_char.len() == 1; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - if let Some(segment) = single_segment_path(qpath); - if segment.ident.name == sym::Some; - then { - let mut applicability = Applicability::MachineApplicable; - let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); - - if *self_ty.kind() != ty::Str { - return false; - } - - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{}` method", suggest), - "like this", - format!("{}{}.{}({})", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), - suggest, - snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), - applicability, - ); - - return true; - } - } - - false -} - -/// Checks for the `CHARS_NEXT_CMP` lint. -fn lint_chars_next_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") -} - /// Checks for the `CHARS_LAST_CMP` lint. fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { true } else { - lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") } } -- cgit 1.4.1-3-g733a5 From 058d8c878ab51ccd65e636456207b3d63ee532b5 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 13 Mar 2021 17:28:22 +0900 Subject: move chars_cmp_with_unwrap, chars_last_cmp and chars_next_cmp_with_unwrap to their own modules --- clippy_lints/src/methods/chars_cmp_with_unwrap.rs | 44 +++++++++++++++ clippy_lints/src/methods/chars_last_cmp.rs | 13 +++++ .../src/methods/chars_next_cmp_with_unwrap.rs | 8 +++ clippy_lints/src/methods/mod.rs | 64 +++------------------- 4 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 clippy_lints/src/methods/chars_cmp_with_unwrap.rs create mode 100644 clippy_lints/src/methods/chars_last_cmp.rs create mode 100644 clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs new file mode 100644 index 00000000000..3ba59171f77 --- /dev/null +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -0,0 +1,44 @@ +use crate::utils::method_chain_args; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_lint::Lint; + +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + info: &crate::methods::BinaryExprInfo<'_>, + chain_methods: &[&str], + lint: &'static Lint, + suggest: &str, +) -> bool { + if_chain! { + if let Some(args) = method_chain_args(info.chain, chain_methods); + if let hir::ExprKind::Lit(ref lit) = info.other.kind; + if let ast::LitKind::Char(c) = lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{}` method", suggest), + "like this", + format!("{}{}.{}('{}')", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), + suggest, + c), + applicability, + ); + + true + } else { + false + } + } +} diff --git a/clippy_lints/src/methods/chars_last_cmp.rs b/clippy_lints/src/methods/chars_last_cmp.rs new file mode 100644 index 00000000000..07bbc5ca1bf --- /dev/null +++ b/clippy_lints/src/methods/chars_last_cmp.rs @@ -0,0 +1,13 @@ +use crate::methods::chars_cmp; +use rustc_lint::LateContext; + +use super::CHARS_LAST_CMP; + +/// Checks for the `CHARS_LAST_CMP` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + true + } else { + chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + } +} diff --git a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs new file mode 100644 index 00000000000..28ede28e935 --- /dev/null +++ b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -0,0 +1,8 @@ +use rustc_lint::LateContext; + +use super::CHARS_NEXT_CMP; + +/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d288c51907e..b4c32e45da9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,7 +1,10 @@ mod bind_instead_of_map; mod bytes_nth; mod chars_cmp; +mod chars_cmp_with_unwrap; +mod chars_last_cmp; mod chars_next_cmp; +mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; mod expect_fun_call; @@ -54,7 +57,7 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{ @@ -66,7 +69,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{TraitItem, TraitItemKind}; -use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; @@ -2037,66 +2040,17 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr } lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info); - lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info); - lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info); + lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info); + lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info); } -/// Checks for the `CHARS_LAST_CMP` lint. -fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { - true - } else { - chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") - } -} - -/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. -fn lint_chars_cmp_with_unwrap<'tcx>( - cx: &LateContext<'tcx>, - info: &BinaryExprInfo<'_>, - chain_methods: &[&str], - lint: &'static Lint, - suggest: &str, -) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Lit(ref lit) = info.other.kind; - if let ast::LitKind::Char(c) = lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{}` method", suggest), - "like this", - format!("{}{}.{}('{}')", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), - suggest, - c), - applicability, - ); - - true - } else { - false - } - } -} - -/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. -fn lint_chars_next_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") -} - /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { true } else { - lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") } } -- cgit 1.4.1-3-g733a5 From c07c046b317bf9a75b04e97c37396436351d80ab Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 13 Mar 2021 17:36:04 +0900 Subject: refactor string_extend_chars: return when obj type is not string --- clippy_lints/src/methods/string_extend_chars.rs | 57 +++++++++++++------------ 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 52b26a36fe3..5c688ac5621 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -12,34 +12,35 @@ use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - if is_type_diagnostic_item(cx, obj_ty, sym::string_type) { - let arg = &args[1]; - if let Some(arglists) = method_chain_args(arg, &["chars"]) { - let target = &arglists[0][0]; - let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); - let ref_str = if *self_ty.kind() == ty::Str { - "" - } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { - "&" - } else { - return; - }; + if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) { + return; + } + let arg = &args[1]; + if let Some(arglists) = method_chain_args(arg, &["chars"]) { + let target = &arglists[0][0]; + let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); + let ref_str = if *self_ty.kind() == ty::Str { + "" + } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { + "&" + } else { + return; + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - STRING_EXTEND_CHARS, - expr.span, - "calling `.extend(_.chars())`", - "try this", - format!( - "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "..", &mut applicability), - ref_str, - snippet_with_applicability(cx, target.span, "..", &mut applicability) - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + STRING_EXTEND_CHARS, + expr.span, + "calling `.extend(_.chars())`", + "try this", + format!( + "{}.push_str({}{})", + snippet_with_applicability(cx, args[0].span, "..", &mut applicability), + ref_str, + snippet_with_applicability(cx, target.span, "..", &mut applicability) + ), + applicability, + ); } } -- cgit 1.4.1-3-g733a5 From e578a536c703cd799b74f261e345121bd889be45 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 13 Mar 2021 18:13:31 +0900 Subject: move derefs_to_slice to methods/utils.rs --- clippy_lints/src/methods/utils.rs | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 clippy_lints/src/methods/utils.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs new file mode 100644 index 00000000000..3596aaa089b --- /dev/null +++ b/clippy_lints/src/methods/utils.rs @@ -0,0 +1,46 @@ +use crate::utils::is_type_diagnostic_item; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +pub fn derefs_to_slice<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ty: Ty<'tcx>, +) -> Option<&'tcx hir::Expr<'tcx>> { + fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { + match ty.kind() { + ty::Slice(_) => true, + ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), + ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), + ty::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, + } + } + + if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { + if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { + Some(&args[0]) + } else { + None + } + } else { + match ty.kind() { + ty::Slice(_) => Some(expr), + ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), + ty::Ref(_, inner, _) => { + if may_slice(cx, inner) { + Some(expr) + } else { + None + } + }, + _ => None, + } + } +} -- cgit 1.4.1-3-g733a5 From 1bec8b606540813cc068c5b564aa2dbd3d212823 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 13 Mar 2021 18:16:38 +0900 Subject: use derefs_to_slice in methods/utils.rs --- clippy_lints/src/methods/get_unwrap.rs | 2 +- clippy_lints/src/methods/iter_cloned_collect.rs | 2 +- clippy_lints/src/methods/iter_count.rs | 2 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/iter_nth.rs | 2 +- clippy_lints/src/methods/mod.rs | 42 +------------------------ 6 files changed, 6 insertions(+), 46 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index b122a0a0b89..b3a9743c614 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -1,4 +1,4 @@ -use crate::methods::derefs_to_slice; +use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index cd575c9cb02..848f47e39f6 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,4 +1,4 @@ -use crate::methods::derefs_to_slice; +use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index fd3d53816a1..e394a8fe819 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,4 +1,4 @@ -use crate::methods::derefs_to_slice; +use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths; use clippy_utils::source::snippet_with_applicability; diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index feebf8b8209..869b2cdd1a6 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,4 +1,4 @@ -use crate::methods::derefs_to_slice; +use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 2619793e1e2..2e7220025f8 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,7 +1,7 @@ -use crate::methods::derefs_to_slice; use crate::methods::iter_nth_zero; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; +use crate::methods::utils::derefs_to_slice; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b4c32e45da9..e16ab4bc20b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -53,6 +53,7 @@ mod unnecessary_fold; mod unnecessary_lazy_eval; mod unwrap_used; mod useless_asref; +mod utils; mod wrong_self_convention; mod zst_offset; @@ -1976,47 +1977,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } - -fn derefs_to_slice<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ty: Ty<'tcx>, -) -> Option<&'tcx hir::Expr<'tcx>> { - fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { - match ty.kind() { - ty::Slice(_) => true, - ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), - ty::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, - } - } - - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { - Some(&args[0]) - } else { - None - } - } else { - match ty.kind() { - ty::Slice(_) => Some(expr), - ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), - ty::Ref(_, inner, _) => { - if may_slice(cx, inner) { - Some(expr) - } else { - None - } - }, - _ => None, - } - } -} - /// Used for `lint_binary_expr_with_method_call`. #[derive(Copy, Clone)] struct BinaryExprInfo<'a> { -- cgit 1.4.1-3-g733a5 From f0a101d81dc25fae998d0670348d928f1789439b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 14 Mar 2021 01:05:37 +0900 Subject: remove a needless variable --- clippy_lints/src/methods/iter_skip_next.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index ea01860b456..b1d398876d3 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -12,14 +12,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir // lint if caller of skip is an Iterator if is_trait_method(cx, expr, sym::Iterator) { 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(..).next()` on an iterator", "use `nth` instead", - hint, + format!(".nth({})", snippet(cx, n.span, "..")), Applicability::MachineApplicable, ); } -- cgit 1.4.1-3-g733a5 From 0c81311bf0093e991c0b5ca8b69096a5c5cd161c Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 14 Mar 2021 01:18:04 +0900 Subject: extract a condition into a function. --- clippy_lints/src/methods/iter_next_slice.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index 869b2cdd1a6..2155159bf7c 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -47,12 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite ); } } - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type) - || matches!( - &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), - ty::Array(_, _) - ) - { + } else if is_vec_or_array(cx, caller_expr) { // caller is a Vec or an Array let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( @@ -69,3 +64,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite ); } } + +fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) + || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) +} -- cgit 1.4.1-3-g733a5 From b6597eec0b3fcd9598fd4f90daa2d4d781e0aabc Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 14 Mar 2021 01:37:33 +0900 Subject: remove unused arguments --- clippy_lints/src/methods/filter_flat_map.rs | 7 +------ clippy_lints/src/methods/filter_map_flat_map.rs | 7 +------ clippy_lints/src/methods/filter_map_map.rs | 7 +------ clippy_lints/src/methods/mod.rs | 6 +++--- 4 files changed, 6 insertions(+), 21 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs index 2f3a3c55ab5..1588eec8882 100644 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ b/clippy_lints/src/methods/filter_flat_map.rs @@ -7,12 +7,7 @@ use rustc_span::sym; use super::FILTER_MAP; /// lint use of `filter().flat_map()` for `Iterators` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.filter().flat_map()` is an Iterator if is_trait_method(cx, expr, sym::Iterator) { let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs index b1a4dc98eb8..741b1e7e361 100644 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ b/clippy_lints/src/methods/filter_map_flat_map.rs @@ -7,12 +7,7 @@ use rustc_span::sym; use super::FILTER_MAP; /// lint use of `filter_map().flat_map()` for `Iterators` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.filter_map().flat_map()` is an Iterator if is_trait_method(cx, expr, sym::Iterator) { let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs index 0b0a8b1dd3b..713bbf25837 100644 --- a/clippy_lints/src/methods/filter_map_map.rs +++ b/clippy_lints/src/methods/filter_map_map.rs @@ -7,12 +7,7 @@ use rustc_span::sym; use super::FILTER_MAP; /// lint use of `filter_map().map()` for `Iterators` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.filter_map().map()` is an Iterator if is_trait_method(cx, expr, sym::Iterator) { let msg = "called `filter_map(..).map(..)` on an `Iterator`"; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e16ab4bc20b..044348a2c34 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1715,11 +1715,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), ["map", "filter"] => filter_map::check(cx, expr, false), - ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]), + ["map", "filter_map"] => filter_map_map::check(cx, expr), ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => filter_map::check(cx, expr, true), - ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), - ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), + ["flat_map", "filter"] => filter_flat_map::check(cx, expr), + ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr), ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]), ["is_some", "find"] => search_is_some::check(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), -- cgit 1.4.1-3-g733a5 From f0f787192006a7b0a508b90a03dcfdd37b3c31d2 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 11:15:25 +0900 Subject: fmt --- clippy_lints/src/methods/iter_nth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 2e7220025f8..bf1575660d1 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,7 +1,7 @@ use crate::methods::iter_nth_zero; +use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use crate::methods::utils::derefs_to_slice; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; -- cgit 1.4.1-3-g733a5 From 48430849465ae94255e78f895197b09c2ce3b03e Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 11:18:02 +0900 Subject: use clippy_utils::ty::is_type_diagnostic_item --- clippy_lints/src/methods/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 3596aaa089b..4d801148cbc 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,4 +1,4 @@ -use crate::utils::is_type_diagnostic_item; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -- cgit 1.4.1-3-g733a5 From 3d9b45df0fa6bc04d864aa4a67fdb631b9bf0a99 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 12:33:09 +0900 Subject: move single_char_add_str to its own module --- clippy_lints/src/methods/single_char_add_str.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 clippy_lints/src/methods/single_char_add_str.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs new file mode 100644 index 00000000000..3df88e5b69c --- /dev/null +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -0,0 +1,15 @@ +use crate::methods::{single_char_insert_string, single_char_push_string}; +use crate::utils::match_def_path; +use crate::utils::paths; +use rustc_hir as hir; +use rustc_lint::LateContext; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + 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) { + single_char_push_string::check(cx, expr, args); + } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { + single_char_insert_string::check(cx, expr, args); + } + } +} -- cgit 1.4.1-3-g733a5 From 62490c41af1a5dab806e45dd2bc0b8d56a3c3c23 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 12:34:37 +0900 Subject: extract conditions into modules --- clippy_lints/src/methods/clone_on_copy.rs | 10 ++++++++-- clippy_lints/src/methods/clone_on_ref_ptr.rs | 8 ++++++-- clippy_lints/src/methods/inefficient_to_string.rs | 11 +++++++---- clippy_lints/src/methods/mod.rs | 20 +++++--------------- 4 files changed, 26 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 949152cf789..edb6649b87b 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -4,14 +4,20 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; +use rustc_span::symbol::{sym, Symbol}; use std::iter; use super::CLONE_DOUBLE_REF; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { + if !(args.len() == 1 && method_name == sym::clone) { + return; + } + let arg = &args[0]; + let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() { if let ty::Ref(_, innermost, _) = inner.kind() { diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 03c949d5c31..6417bc81304 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -6,11 +6,15 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use super::CLONE_ON_REF_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { + if !(args.len() == 1 && method_name == sym::clone) { + return; + } + let arg = &args[0]; let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs(); if let ty::Adt(_, subst) = obj_ty.kind() { diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index d10a540c24e..950ec62c9fe 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,4 +1,3 @@ -use super::INEFFICIENT_TO_STRING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; @@ -8,14 +7,18 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; +use rustc_span::symbol::{sym, Symbol}; + +use super::INEFFICIENT_TO_STRING; /// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { if_chain! { + if args.len() == 1 && method_name == sym!(to_string); if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id); + let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); let self_ty = substs.type_at(0); let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); if deref_count >= 1; @@ -32,7 +35,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr self_ty, deref_self_ty )); let mut applicability = Applicability::MachineApplicable; - let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); diag.span_suggestion( expr.span, "try dereferencing the receiver", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 044348a2c34..bbf7f1d312b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -41,6 +41,7 @@ mod option_map_or_none; mod option_map_unwrap_or; mod or_fun_call; mod search_is_some; +mod single_char_add_str; mod single_char_insert_string; mod single_char_pattern; mod single_char_push_string; @@ -1785,23 +1786,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); + clone_on_copy::check(cx, expr, method_call.ident.name, args); + clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); - if args.len() == 1 && method_call.ident.name == sym::clone { - clone_on_copy::check(cx, expr, &args[0], self_ty); - clone_on_ref_ptr::check(cx, expr, &args[0]); - } - if args.len() == 1 && method_call.ident.name == sym!(to_string) { - inefficient_to_string::check(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) { - single_char_push_string::check(cx, expr, args); - } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { - single_char_insert_string::check(cx, expr, args); - } - } + inefficient_to_string::check(cx, expr, method_call.ident.name, args); + single_char_add_str::check(cx, expr, args); match self_ty.kind() { ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { -- cgit 1.4.1-3-g733a5 From d38076995258ed7f1ebd270f2f32b1744c688e71 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 13:22:25 +0900 Subject: extract conditions for `from_iter_instead_of_collect` into its own module --- clippy_lints/src/methods/chars_cmp.rs | 2 +- clippy_lints/src/methods/chars_cmp_with_unwrap.rs | 2 +- clippy_lints/src/methods/from_iter_instead_of_collect.rs | 12 +++++++----- clippy_lints/src/methods/mod.rs | 13 ++++--------- clippy_lints/src/methods/single_char_add_str.rs | 3 +-- 5 files changed, 14 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index f9af06d200c..c668fe52781 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -1,6 +1,6 @@ -use crate::utils::{method_chain_args, single_segment_path}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{method_chain_args, single_segment_path}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 3ba59171f77..4275857757f 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -1,5 +1,5 @@ -use crate::utils::method_chain_args; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 2095a60e44b..15cf5674313 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,20 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, paths, sugg}; +use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::ExprKind; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let ty = cx.typeck_results().expr_ty(expr); - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) { if_chain! { + if let hir::ExprKind::Path(path) = func_kind; + if match_qpath(path, &["from_iter"]); + let ty = cx.typeck_results().expr_ty(expr); + let arg_ty = cx.typeck_results().expr_ty(&args[0]); if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bbf7f1d312b..808b64d9493 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -63,8 +63,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{ - contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls, - method_chain_args, paths, return_ty, single_segment_path, SpanlessEq, + contains_return, get_trait_def_id, in_macro, iter_input_pats, match_qpath, method_calls, paths, return_ty, + SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -1777,22 +1777,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { match expr.kind { hir::ExprKind::Call(ref func, ref args) => { - if let hir::ExprKind::Path(path) = &func.kind { - if match_qpath(path, &["from_iter"]) { - from_iter_instead_of_collect::check(cx, expr, args); - } - } + from_iter_instead_of_collect::check(cx, expr, args, &func.kind); }, hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); clone_on_copy::check(cx, expr, method_call.ident.name, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args); - - let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); inefficient_to_string::check(cx, expr, method_call.ident.name, args); single_char_add_str::check(cx, expr, args); + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); match self_ty.kind() { ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { for &(method, pos) in &PATTERN_METHODS { diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index 3df88e5b69c..9a5fabcf7cd 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -1,6 +1,5 @@ use crate::methods::{single_char_insert_string, single_char_push_string}; -use crate::utils::match_def_path; -use crate::utils::paths; +use clippy_utils::{match_def_path, paths}; use rustc_hir as hir; use rustc_lint::LateContext; -- cgit 1.4.1-3-g733a5 From 7a7fcc0eda1d62f44ec374bad3d14226888238ad Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 14:14:06 +0900 Subject: extract condition for into_iter_on_ref to its own module --- clippy_lints/src/methods/into_iter_on_ref.rs | 47 +++++++++++++++++----------- clippy_lints/src/methods/mod.rs | 7 ++--- 2 files changed, 31 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index cfe749cf361..5d59fa86c9e 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,32 +1,43 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::has_iter_method; use clippy_utils::{match_trait_method, paths}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{sym, Symbol}; use super::INTO_ITER_ON_REF; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) { - if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) { - return; - } - if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) { - span_lint_and_sugg( - cx, - INTO_ITER_ON_REF, - method_span, - &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", - method_name, kind, - ), - "call directly", - method_name.to_string(), - Applicability::MachineApplicable, - ); +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + method_span: Span, + method_name: Symbol, + args: &[hir::Expr<'_>], +) { + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); + if_chain! { + if let ty::Ref(..) = self_ty.kind(); + if method_name == sym::into_iter; + if match_trait_method(cx, expr, &paths::INTO_ITERATOR); + if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty); + then { + span_lint_and_sugg( + cx, + INTO_ITER_ON_REF, + method_span, + &format!( + "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", + method_name, kind, + ), + "call directly", + method_name.to_string(), + Applicability::MachineApplicable, + ); + } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 808b64d9493..e8cefbd7422 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1786,9 +1786,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args); inefficient_to_string::check(cx, expr, method_call.ident.name, args); single_char_add_str::check(cx, expr, args); + into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); - let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); - match self_ty.kind() { + match cx.typeck_results().expr_ty_adjusted(&args[0]).kind() { ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { for &(method, pos) in &PATTERN_METHODS { if method_call.ident.name.as_str() == method && args.len() > pos { @@ -1796,9 +1796,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } }, - ty::Ref(..) if method_call.ident.name == sym::into_iter => { - into_iter_on_ref::check(cx, expr, self_ty, *method_span); - }, _ => (), } }, -- cgit 1.4.1-3-g733a5 From 4d1f2bc56519a9b37daaec84273f97e3aba4318b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 14:26:27 +0900 Subject: extract conditions for single_char_pattern into its own module --- clippy_lints/src/methods/mod.rs | 12 +-------- clippy_lints/src/methods/single_char_pattern.rs | 36 ++++++++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e8cefbd7422..b97dee27c52 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1787,17 +1787,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::check(cx, expr, method_call.ident.name, args); single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); - - match cx.typeck_results().expr_ty_adjusted(&args[0]).kind() { - ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { - for &(method, pos) in &PATTERN_METHODS { - if method_call.ident.name.as_str() == method && args.len() > pos { - single_char_pattern::check(cx, expr, &args[pos]); - } - } - }, - _ => (), - } + single_char_pattern::check(cx, expr, method_call.ident.name, args); }, hir::ExprKind::Binary(op, ref lhs, ref rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index 12d6418d700..ae998468c2d 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -1,23 +1,35 @@ use crate::methods::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::Symbol; use super::SINGLE_CHAR_PATTERN; /// lint for length-1 `str`s for methods in `PATTERN_METHODS` -pub(super) fn check(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( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); +pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { + for &(method, pos) in &crate::methods::PATTERN_METHODS { + if_chain! { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind(); + if *ty.kind() == ty::Str; + if method_name.as_str() == method && args.len() > pos; + let arg = &args[pos]; + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability); + then { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } + } } } -- cgit 1.4.1-3-g733a5 From 27963c8dce5b2db8c6c308681682bbb33938e125 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 14:34:20 +0900 Subject: move chars_last_cmp_with_unwrap to its own module --- clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs | 13 +++++++++++++ clippy_lints/src/methods/mod.rs | 12 ++---------- 2 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs new file mode 100644 index 00000000000..c29ee0ec8c8 --- /dev/null +++ b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -0,0 +1,13 @@ +use crate::methods::chars_cmp_with_unwrap; +use rustc_lint::LateContext; + +use super::CHARS_LAST_CMP; + +/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + true + } else { + chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b97dee27c52..f124d0b12f5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod bytes_nth; mod chars_cmp; mod chars_cmp_with_unwrap; mod chars_last_cmp; +mod chars_last_cmp_with_unwrap; mod chars_next_cmp; mod chars_next_cmp_with_unwrap; mod clone_on_copy; @@ -1974,16 +1975,7 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info); lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info); lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); - lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info); -} - -/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. -fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { - true - } else { - chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") - } + lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); } fn get_hint_if_single_char_arg( -- cgit 1.4.1-3-g733a5 From 602bcf3e4f5f540e2f81d9e807e28b35c7bee0ed Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Mar 2021 14:45:20 +0900 Subject: move get_hint_if_single_char_arg to methods/utils.rs --- clippy_lints/src/methods/mod.rs | 31 -------------------- .../src/methods/single_char_insert_string.rs | 2 +- clippy_lints/src/methods/single_char_pattern.rs | 2 +- .../src/methods/single_char_push_string.rs | 2 +- clippy_lints/src/methods/utils.rs | 34 +++++++++++++++++++++- 5 files changed, 36 insertions(+), 35 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f124d0b12f5..a80162015dc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -61,15 +61,12 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{ contains_return, get_trait_def_id, in_macro, iter_input_pats, match_qpath, method_calls, paths, return_ty, SpanlessEq, }; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1978,34 +1975,6 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); } -fn get_hint_if_single_char_arg( - cx: &LateContext<'_>, - arg: &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; - let string = r.as_str(); - if string.chars().count() == 1; - then { - let snip = snippet_with_applicability(cx, arg.span, &string, applicability); - let ch = if let ast::StrStyle::Raw(nhash) = style { - let nhash = nhash as usize; - // for raw string: r##"a"## - &snip[(nhash + 2)..(snip.len() - 1 - nhash)] - } else { - // for regular string: "a" - &snip[1..(snip.len() - 1)] - }; - let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - Some(hint) - } else { - None - } - } -} - const FN_HEADER: hir::FnHeader = hir::FnHeader { unsafety: hir::Unsafety::Normal, constness: hir::Constness::NotConst, diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index cd87d2c56b7..3f3e3bd7dfd 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -1,4 +1,4 @@ -use crate::methods::get_hint_if_single_char_arg; +use crate::methods::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index ae998468c2d..abead199098 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -1,4 +1,4 @@ -use crate::methods::get_hint_if_single_char_arg; +use crate::methods::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 882703a987d..189157f819e 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -1,4 +1,4 @@ -use crate::methods::get_hint_if_single_char_arg; +use crate::methods::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 4d801148cbc..8de23e1bc6e 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,11 +1,15 @@ +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; use rustc_span::symbol::sym; -pub fn derefs_to_slice<'tcx>( +pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, @@ -44,3 +48,31 @@ pub fn derefs_to_slice<'tcx>( } } } + +pub(super) fn get_hint_if_single_char_arg( + cx: &LateContext<'_>, + arg: &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; + let string = r.as_str(); + if string.chars().count() == 1; + then { + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); + let ch = if let ast::StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; + let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); + Some(hint) + } else { + None + } + } +} -- cgit 1.4.1-3-g733a5 From 0b7ab90ecae3c5ab55aa053ea9477cab88ab7f0e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 6 Mar 2021 19:25:09 -0500 Subject: Improvements to `match_wildcard_for_single_variants` and `wildcard_enum_match_arm` lints Don't lint on `Result` and `Option` types. Considers `or` patterns. Considers variants prefixed with `Self` Suggestions will try to find a common prefix rather than just using the full path --- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/matches.rs | 245 +++++++++++++-------- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/types/mod.rs | 6 +- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_utils/src/lib.rs | 10 + tests/ui/match_wildcard_for_single_variants.fixed | 40 ++++ tests/ui/match_wildcard_for_single_variants.rs | 40 ++++ tests/ui/match_wildcard_for_single_variants.stderr | 44 +++- tests/ui/wildcard_enum_match_arm.fixed | 2 +- tests/ui/wildcard_enum_match_arm.stderr | 2 +- 14 files changed, 293 insertions(+), 112 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index f805f716e6b..a4ddef666c0 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -430,7 +430,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), - _ => false, + StmtKind::Item(_) => false, }, ) } @@ -613,7 +613,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } } }, - _ => {}, + MetaItemKind::NameValue(..) => {}, } } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 5fa278b2897..14338ac8faf 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -583,7 +583,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - _ => false, + FnRetTy::Ty(_) => false, }; if returns_nothing && !is_async && !block.stmts.is_empty() { diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 5d5b67de843..ea33a4d98fd 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -273,7 +273,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) - .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), - _ => StopEarly::KeepGoing, + StmtKind::Item(..) => StopEarly::KeepGoing, } } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 82ec2635aeb..f63a3761a0d 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -78,7 +78,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e), StmtKind::Local(ref local) => local.init.as_deref(), - _ => None, + StmtKind::Item(..) => None, } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e94c7094cac..e9926e0bca5 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -8,21 +8,21 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, pe use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, strip_pat_refs, + path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, - PatKind, QPath, RangeEnd, + self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, + Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, Ty, TyS, VariantDef}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; @@ -956,114 +956,181 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } } -fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - let ty = cx.typeck_results().expr_ty(ex); - if !ty.is_enum() { - // If there isn't a nice closed set of possible values that can be conveniently enumerated, - // don't complain about not enumerating the mall. - return; +enum CommonPrefixSearcher<'a> { + None, + Path(&'a [PathSegment<'a>]), + Mixed, +} +impl CommonPrefixSearcher<'a> { + fn with_path(&mut self, path: &'a [PathSegment<'a>]) { + match path { + [path @ .., _] => self.with_prefix(path), + [] => (), + } } + fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) { + match self { + Self::None => *self = Self::Path(path), + Self::Path(self_path) + if path + .iter() + .map(|p| p.ident.name) + .eq(self_path.iter().map(|p| p.ident.name)) => {}, + Self::Path(_) => *self = Self::Mixed, + Self::Mixed => (), + } + } +} + +#[allow(clippy::too_many_lines)] +fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { + let ty = cx.typeck_results().expr_ty(ex).peel_refs(); + let adt_def = match ty.kind() { + ty::Adt(adt_def, _) + if adt_def.is_enum() + && !(is_type_diagnostic_item(cx, ty, sym::option_type) + || is_type_diagnostic_item(cx, ty, sym::result_type)) => + { + adt_def + }, + _ => return, + }; + // First pass - check for violation, but don't do much book-keeping because this is hopefully // the uncommon case, and the book-keeping is slightly expensive. let mut wildcard_span = None; let mut wildcard_ident = None; + let mut has_non_wild = false; for arm in arms { - if let PatKind::Wild = arm.pat.kind { - wildcard_span = Some(arm.pat.span); - } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind { - wildcard_span = Some(arm.pat.span); - wildcard_ident = Some(ident); + match peel_hir_pat_refs(arm.pat).0.kind { + PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Binding(_, _, ident, None) => { + wildcard_span = Some(arm.pat.span); + wildcard_ident = Some(ident); + }, + _ => has_non_wild = true, } } + let wildcard_span = match wildcard_span { + Some(x) if has_non_wild => x, + _ => return, + }; - if let Some(wildcard_span) = wildcard_span { - // Accumulate the variants which should be put in place of the wildcard because they're not - // already covered. + // Accumulate the variants which should be put in place of the wildcard because they're not + // already covered. + let mut missing_variants: Vec<_> = adt_def.variants.iter().collect(); - let mut missing_variants = vec![]; - if let ty::Adt(def, _) = ty.kind() { - for variant in &def.variants { - missing_variants.push(variant); + let mut path_prefix = CommonPrefixSearcher::None; + for arm in arms { + // Guards mean that this case probably isn't exhaustively covered. Technically + // this is incorrect, as we should really check whether each variant is exhaustively + // covered by the set of guards that cover it, but that's really hard to do. + recurse_or_patterns(arm.pat, |pat| { + let path = match &peel_hir_pat_refs(pat).0.kind { + PatKind::Path(path) => { + #[allow(clippy::match_same_arms)] + let id = match cx.qpath_res(path, pat.hir_id) { + Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return, + Res::Def(_, id) => id, + _ => return, + }; + if arm.guard.is_none() { + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } + path + }, + PatKind::TupleStruct(path, patterns, ..) => { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { + let id = cx.qpath_res(path, pat.hir_id).def_id(); + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } + path + }, + PatKind::Struct(path, patterns, ..) => { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { + let id = cx.qpath_res(path, pat.hir_id).def_id(); + missing_variants.retain(|e| e.def_id != id); + } + path + }, + _ => return, + }; + match path { + QPath::Resolved(_, path) => path_prefix.with_path(path.segments), + QPath::TypeRelative( + hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }, + _, + ) => path_prefix.with_prefix(path.segments), + _ => (), } - } + }); + } - for arm in arms { - if arm.guard.is_some() { - // Guards mean that this case probably isn't exhaustively covered. Technically - // this is incorrect, as we should really check whether each variant is exhaustively - // covered by the set of guards that cover it, but that's really hard to do. - continue; - } - if let PatKind::Path(ref path) = arm.pat.kind { - if let QPath::Resolved(_, p) = path { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); - } - } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind { - // Some simple checks for exhaustive patterns. - // There is a room for improvements to detect more cases, - // but it can be more expensive to do so. - let is_pattern_exhaustive = - |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); - if patterns.iter().all(is_pattern_exhaustive) { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + let format_suggestion = |variant: &VariantDef| { + format!( + "{}{}{}{}", + if let Some(ident) = wildcard_ident { + format!("{} @ ", ident.name) + } else { + String::new() + }, + if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { + let mut s = String::new(); + for seg in path_prefix { + s.push_str(&seg.ident.as_str()); + s.push_str("::"); } + s + } else { + let mut s = cx.tcx.def_path_str(adt_def.did); + s.push_str("::"); + s + }, + variant.ident.name, + match variant.ctor_kind { + CtorKind::Fn if variant.fields.len() == 1 => "(_)", + CtorKind::Fn => "(..)", + CtorKind::Const => "", + CtorKind::Fictive => "{ .. }", } - } - - let mut suggestion: Vec = missing_variants - .iter() - .map(|v| { - let suffix = match v.ctor_kind { - CtorKind::Fn => "(..)", - CtorKind::Const | CtorKind::Fictive => "", - }; - let ident_str = if let Some(ident) = wildcard_ident { - format!("{} @ ", ident.name) - } else { - String::new() - }; - // This path assumes that the enum type is imported into scope. - format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix) - }) - .collect(); - - if suggestion.is_empty() { - return; - } - - let mut message = "wildcard match will miss any future added variants"; + ) + }; - if let ty::Adt(def, _) = ty.kind() { - if def.is_variant_list_non_exhaustive() { - message = "match on non-exhaustive enum doesn't explicitly match all known variants"; - suggestion.push(String::from("_")); - } - } + match missing_variants.as_slice() { + [] => (), + [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + "match on non-exhaustive enum doesn't explicitly match all known variants", + "try this", + format_suggestion(x), + Applicability::MaybeIncorrect, + ), + variants => { + let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); + let message = if adt_def.is_variant_list_non_exhaustive() { + suggestions.push("_".into()); + "match on non-exhaustive enum doesn't explicitly match all known variants" + } else { + "wildcard match will miss any future added variants" + }; - 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_ENUM_MATCH_ARM, wildcard_span, message, "try this", - suggestion[0].clone(), + suggestions.join(" | "), Applicability::MaybeIncorrect, ) - }; - - span_lint_and_sugg( - cx, - WILDCARD_ENUM_MATCH_ARM, - wildcard_span, - message, - "try this", - suggestion.join(" | "), - Applicability::MaybeIncorrect, - ) - } + }, + }; } // If the block contains only a `panic!` macro (as expression or statement) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 60da2bcb04a..37678fac1d2 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -556,7 +556,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { mir::Operand::Copy(p) | mir::Operand::Move(p) => { self.possible_borrower.add(p.local, *dest); }, - _ => (), + mir::Operand::Constant(..) => (), } } } @@ -578,7 +578,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { let mut visit_op = |op: &mir::Operand<'_>| match op { mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - _ => (), + mir::Operand::Constant(..) => (), }; match rvalue { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index c0cd48e94b2..10c93c6ad06 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -276,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { match item.kind { TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false), TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl), - _ => (), + TraitItemKind::Type(..) => (), } } @@ -452,7 +452,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeComplexity { TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty), TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl), // methods with default impl are covered by check_fn - _ => (), + TraitItemKind::Type(..) | TraitItemKind::Fn(_, TraitFn::Provided(_)) => (), } } @@ -460,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeComplexity { match item.kind { ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty), // methods are covered by check_fn - _ => (), + ImplItemKind::Fn(..) => (), } } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 9e227164695..c2be457e9dc 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } }, FnKind::Closure => return, - _ => (), + FnKind::Method(..) => (), } // Abort if the method is implementing a trait or of it a trait method. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3efa84f6b86..7c8ad4fea9a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -957,6 +957,16 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } } +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call +/// the function once on the given pattern. +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) { + if let PatKind::Or(pats) = pat.kind { + pats.iter().cloned().for_each(f) + } else { + f(pat) + } +} + /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index 519200977a7..f101e144a13 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -15,6 +15,16 @@ enum Color { Blue, Rgb(u8, u8, u8), } +impl Color { + fn f(self) { + match self { + Self::Red => (), + Self::Green => (), + Self::Blue => (), + Self::Rgb(..) => (), + }; + } +} fn main() { let f = Foo::A; @@ -56,4 +66,34 @@ fn main() { Color::Rgb(255, _, _) => {}, _ => {}, } + + // References shouldn't change anything + match &color { + &Color::Red => (), + Color::Green => (), + &Color::Rgb(..) => (), + Color::Blue => (), + } + + use self::Color as C; + + match color { + C::Red => (), + C::Green => (), + C::Rgb(..) => (), + C::Blue => (), + } + + match color { + C::Red => (), + Color::Green => (), + Color::Rgb(..) => (), + Color::Blue => (), + } + + match Some(0) { + Some(0) => 0, + Some(_) => 1, + _ => 2, + }; } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1df917e085c..1ddba87e78f 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -15,6 +15,16 @@ enum Color { Blue, Rgb(u8, u8, u8), } +impl Color { + fn f(self) { + match self { + Self::Red => (), + Self::Green => (), + Self::Blue => (), + _ => (), + }; + } +} fn main() { let f = Foo::A; @@ -56,4 +66,34 @@ fn main() { Color::Rgb(255, _, _) => {}, _ => {}, } + + // References shouldn't change anything + match &color { + &Color::Red => (), + Color::Green => (), + &Color::Rgb(..) => (), + &_ => (), + } + + use self::Color as C; + + match color { + C::Red => (), + C::Green => (), + C::Rgb(..) => (), + _ => (), + } + + match color { + C::Red => (), + Color::Green => (), + Color::Rgb(..) => (), + _ => (), + } + + match Some(0) { + Some(0) => 0, + Some(_) => 1, + _ => 2, + }; } diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 82790aa9e80..e0900c970c3 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,28 +1,52 @@ -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:24:9 +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:24:13 | -LL | _ => {}, - | ^ help: try this: `Foo::C` +LL | _ => (), + | ^ help: try this: `Self::Rgb(..)` | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: wildcard match will miss any future added variants +error: match on non-exhaustive enum doesn't explicitly match all known variants --> $DIR/match_wildcard_for_single_variants.rs:34:9 | +LL | _ => {}, + | ^ help: try this: `Foo::C` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:44: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 +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:52: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 +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:58:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: aborting due to 4 previous errors +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:75:9 + | +LL | &_ => (), + | ^^ help: try this: `Color::Blue` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:84:9 + | +LL | _ => (), + | ^ help: try this: `C::Blue` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:91:9 + | +LL | _ => (), + | ^ help: try this: `Color::Blue` + +error: aborting due to 8 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index c266f684a36..fd754e4c794 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -77,7 +77,7 @@ fn main() { let error_kind = ErrorKind::NotFound; match error_kind { ErrorKind::NotFound => {}, - std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {}, + ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {}, } match error_kind { ErrorKind::NotFound => {}, diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 0da2b68ba0b..8d880b7ce0a 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -32,7 +32,7 @@ error: match on non-exhaustive enum doesn't explicitly match all known variants --> $DIR/wildcard_enum_match_arm.rs:80:9 | LL | _ => {}, - | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _` + | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _` error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 0edd5f881d287dcff1b7401c44c8482cbd627c6b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 18 Mar 2021 01:06:24 +0900 Subject: remove the use of paths --- clippy_lints/src/methods/into_iter_on_ref.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 5d59fa86c9e..da13b4ba37a 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; use clippy_utils::ty::has_iter_method; -use clippy_utils::{match_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,7 +22,7 @@ pub(super) fn check( if_chain! { if let ty::Ref(..) = self_ty.kind(); if method_name == sym::into_iter; - if match_trait_method(cx, expr, &paths::INTO_ITERATOR); + if is_trait_method(cx, expr, sym::IntoIterator); if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty); then { span_lint_and_sugg( -- cgit 1.4.1-3-g733a5 From b6a2757561167f831939f874735e259e655031a1 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 18 Mar 2021 01:23:37 +0900 Subject: replace crate::methods::utils with super::utils --- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/iter_nth.rs | 2 +- clippy_lints/src/methods/single_char_insert_string.rs | 2 +- clippy_lints/src/methods/single_char_pattern.rs | 2 +- clippy_lints/src/methods/single_char_push_string.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index 2155159bf7c..e9b37b6f2bd 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,4 +1,4 @@ -use crate::methods::utils::derefs_to_slice; +use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index bf1575660d1..c46af427b3c 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,5 +1,5 @@ +use super::utils::derefs_to_slice; use crate::methods::iter_nth_zero; -use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index 3f3e3bd7dfd..6cdc954c03b 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -1,4 +1,4 @@ -use crate::methods::utils::get_hint_if_single_char_arg; +use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index abead199098..f4090c7c617 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -1,4 +1,4 @@ -use crate::methods::utils::get_hint_if_single_char_arg; +use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 189157f819e..0237d39cbdb 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -1,4 +1,4 @@ -use crate::methods::utils::get_hint_if_single_char_arg; +use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; -- cgit 1.4.1-3-g733a5 From d5a7941ead06e1cb89a0a2cc6ea5c19810daea03 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 16 Mar 2021 12:56:08 -0400 Subject: Fix message for `match_wildcard_for_single_variant` --- clippy_lints/src/matches.rs | 6 +++--- tests/ui/match_wildcard_for_single_variants.stderr | 16 ++++++++-------- tests/ui/wildcard_enum_match_arm.stderr | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e9926e0bca5..d43cb32ee51 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1106,7 +1106,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, - "match on non-exhaustive enum doesn't explicitly match all known variants", + "wildcard matches only a single variant and will also match any future added variants", "try this", format_suggestion(x), Applicability::MaybeIncorrect, @@ -1115,9 +1115,9 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); let message = if adt_def.is_variant_list_non_exhaustive() { suggestions.push("_".into()); - "match on non-exhaustive enum doesn't explicitly match all known variants" + "wildcard matches known variants and will also match future added variants" } else { - "wildcard match will miss any future added variants" + "wildcard match will also match any future added variants" }; span_lint_and_sugg( diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index e0900c970c3..34538dea8e5 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,4 +1,4 @@ -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:24:13 | LL | _ => (), @@ -6,43 +6,43 @@ LL | _ => (), | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:34:9 | LL | _ => {}, | ^ help: try this: `Foo::C` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:44:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:52:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:58:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:75:9 | LL | &_ => (), | ^^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:84:9 | LL | _ => (), | ^ help: try this: `C::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:91:9 | LL | _ => (), diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 8d880b7ce0a..a513a62c748 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -1,4 +1,4 @@ -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:39:9 | LL | _ => eprintln!("Not red"), @@ -10,25 +10,25 @@ note: the lint level is defined here LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:43:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:47:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:63:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches known variants and will also match future added variants --> $DIR/wildcard_enum_match_arm.rs:80:9 | LL | _ => {}, -- cgit 1.4.1-3-g733a5 From f468d822830b36bb61d2ecd90fe3f1e9e1b90cf4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 5 Mar 2021 15:57:37 -0500 Subject: Fix `manual_map` suggestion for `if let.. else ... if let.. else` chain --- clippy_lints/src/manual_map.rs | 18 +++++++++++++----- clippy_utils/src/lib.rs | 20 ++++++++++++++++++++ tests/ui/manual_map_option.fixed | 5 +++++ tests/ui/manual_map_option.rs | 9 +++++++++ tests/ui/manual_map_option.stderr | 21 ++++++++++++++++++++- 5 files changed, 67 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index cd9594737bf..ed157783b72 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,13 +2,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{is_allowed, is_else_clause_of_if_let_else, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ def::Res, intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath, + Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -51,8 +51,11 @@ impl LateLintPass<'_> for ManualMap { return; } - if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) = - expr.kind + if let ExprKind::Match( + scrutinee, + [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], + match_kind, + ) = expr.kind { let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); @@ -178,7 +181,12 @@ impl LateLintPass<'_> for ManualMap { expr.span, "manual implementation of `Option::map`", "try this", - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str), + if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause_of_if_let_else(cx.tcx, expr) + { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, app, ); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3efa84f6b86..d5a5430546d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -798,6 +798,26 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { } } +/// Checks if the given expression is the else clause in the expression `if let .. {} else {}` +pub fn is_else_clause_of_if_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + let map = tcx.hir(); + let mut iter = map.parent_iter(expr.hir_id); + let arm_id = match iter.next() { + Some((id, Node::Arm(..))) => id, + _ => return false, + }; + match iter.next() { + Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Match(_, [_, else_arm], kind), + .. + }), + )) => else_arm.hir_id == arm_id && matches!(kind, MatchSource::IfLetDesugar { .. }), + _ => false, + } +} + /// Checks whether the given expression is a constant integer of the given value. /// unlike `is_integer_literal`, this version does const folding pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool { diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 9222aaf6c78..acb6a580ceb 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -128,4 +128,9 @@ fn main() { None => None, }; } + + // #6847 + if Some(0).is_some() { + Some(0) + } else { Some(0).map(|x| x + 1) }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 1ccb450619c..3299e617707 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -186,4 +186,13 @@ fn main() { None => None, }; } + + // #6847 + if let Some(_) = Some(0) { + Some(0) + } else if let Some(x) = Some(0) { + Some(x + 1) + } else { + None + }; } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index d9f86eecd93..048ccfb9582 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -172,5 +172,24 @@ LL | | None => None, LL | | }; | |_____^ help: try this: `option_env!("").map(String::from)` -error: aborting due to 19 previous errors +error: redundant pattern matching, consider using `is_some()` + --> $DIR/manual_map_option.rs:191:12 + | +LL | if let Some(_) = Some(0) { + | -------^^^^^^^---------- help: try this: `if Some(0).is_some()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:193:12 + | +LL | } else if let Some(x) = Some(0) { + | ____________^ +LL | | Some(x + 1) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` + +error: aborting due to 21 previous errors -- cgit 1.4.1-3-g733a5 From ea15fb2177b0c33352363c130bc296a0dd1a794a Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 17 Mar 2021 15:52:14 +0100 Subject: wrong_self_convention: `to_` respects `Copy` types More details here: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv --- clippy_lints/src/methods/mod.rs | 39 +++++++----- clippy_lints/src/methods/wrong_self_convention.rs | 74 +++++++++++++---------- tests/ui/use_self.fixed | 1 + tests/ui/use_self.rs | 1 + tests/ui/use_self.stderr | 46 +++++++------- tests/ui/wrong_self_convention.rs | 32 ++++++++++ tests/ui/wrong_self_convention.stderr | 36 +++++------ tests/ui/wrong_self_conventions_mut.stderr | 4 +- 8 files changed, 143 insertions(+), 90 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index af8fe7abd96..722effc29b2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -193,14 +193,18 @@ declare_clippy_lint! { /// **What it does:** Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |Postfix |`self` taken | - /// |-------|------------|----------------------| - /// |`as_` | none |`&self` or `&mut self`| - /// |`from_`| none | none | - /// |`into_`| none |`self` | - /// |`is_` | none |`&self` or none | - /// |`to_` | `_mut` |`&mut &self` | - /// |`to_` | not `_mut` |`&self` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-----------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// + /// Please find more info here: + /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv /// /// **Why is this bad?** Consistency breeds readability. If you follow the /// conventions, your users won't be surprised that they, e.g., need to supply a @@ -1837,10 +1841,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let self_ty = cx.tcx.type_of(item.def_id); - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return; - } + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; @@ -1855,7 +1856,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(first_arg_ty) = first_arg_ty; then { - if cx.access_levels.is_exported(impl_item.hir_id()) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) { // check missing trait implementations for method_config in &TRAIT_METHODS { if name == method_config.method_name && @@ -1891,11 +1893,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { item.vis.node.is_pub(), self_ty, first_arg_ty, - first_arg.pat.span + first_arg.pat.span, + false ); } } + // if this impl block implements a trait, lint in trait definition instead + if implements_trait { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id()); @@ -1947,7 +1955,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { false, self_ty, first_arg_ty, - first_arg_span + first_arg_span, + true ); } } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index b728d7d8d08..bece8f251da 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,5 +1,6 @@ use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_copy; use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::source_map::Span; @@ -9,7 +10,7 @@ use super::WRONG_PUB_SELF_CONVENTION; use super::WRONG_SELF_CONVENTION; #[rustfmt::skip] -const CONVENTIONS: [(&[Convention], &[SelfKind]); 8] = [ +const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ (&[Convention::Eq("new")], &[SelfKind::No]), (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), (&[Convention::StartsWith("from_")], &[SelfKind::No]), @@ -17,7 +18,11 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 8] = [ (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut")], &[SelfKind::Ref]), + + // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). + // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; enum Convention { @@ -25,16 +30,20 @@ enum Convention { StartsWith(&'static str), EndsWith(&'static str), NotEndsWith(&'static str), + IsSelfTypeCopy(bool), + ImplementsTrait(bool), } impl Convention { #[must_use] - fn check(&self, other: &str) -> bool { + fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool { match *self { Self::Eq(this) => this == other, Self::StartsWith(this) => other.starts_with(this) && this != other, Self::EndsWith(this) => other.ends_with(this) && this != other, - Self::NotEndsWith(this) => !Self::EndsWith(this).check(other), + Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def), + Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty), + Self::ImplementsTrait(is_true) => is_true == is_trait_def, } } } @@ -46,6 +55,10 @@ impl fmt::Display for Convention { Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), Self::EndsWith(this) => '*'.fmt(f).and_then(|_| this.fmt(f)), Self::NotEndsWith(this) => '~'.fmt(f).and_then(|_| this.fmt(f)), + Self::IsSelfTypeCopy(is_true) => format!("self type is {} Copy", if is_true { "" } else { "not" }).fmt(f), + Self::ImplementsTrait(is_true) => { + format!("Method {} implement a trait", if is_true { "" } else { "do not" }).fmt(f) + }, } } } @@ -57,45 +70,42 @@ pub(super) fn check<'tcx>( self_ty: &'tcx TyS<'tcx>, first_arg_ty: &'tcx TyS<'tcx>, first_arg_span: Span, + is_trait_item: bool, ) { let lint = if is_pub { WRONG_PUB_SELF_CONVENTION } else { WRONG_SELF_CONVENTION }; - if let Some((conventions, self_kinds)) = &CONVENTIONS - .iter() - .find(|(convs, _)| convs.iter().all(|conv| conv.check(item_name))) - { + if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { + convs + .iter() + .all(|conv| conv.check(cx, self_ty, item_name, is_trait_item)) + }) { if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { if conventions.len() > 1 { - let special_case = { - // Don't mention `NotEndsWith` when there is also `StartsWith` convention present - if conventions.len() == 2 { - match conventions { - [Convention::StartsWith(starts_with), Convention::NotEndsWith(_)] - | [Convention::NotEndsWith(_), Convention::StartsWith(starts_with)] => { - Some(format!("methods called `{}`", Convention::StartsWith(starts_with))) - }, - _ => None, - } - } else { - None - } - }; - - if let Some(suggestion) = special_case { - suggestion - } else { - let s = conventions + // Don't mention `NotEndsWith` when there is also `StartsWith` convention present + let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_))) + && conventions .iter() - .map(|c| format!("`{}`", &c.to_string())) - .collect::>() - .join(" and "); + .any(|conv| matches!(conv, Convention::NotEndsWith(_))); + + let s = conventions + .iter() + .filter_map(|conv| { + if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) + || matches!(conv, Convention::ImplementsTrait(_)) + { + None + } else { + Some(format!("`{}`", &conv.to_string())) + } + }) + .collect::>() + .join(" and "); - format!("methods called like this: ({})", &s) - } + format!("methods with the following characteristics: ({})", &s) } else { format!("methods called `{}`", &conventions[0]) } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index a630936e3b1..1986b54e66c 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -79,6 +79,7 @@ mod issue2894 { } // This should not be linted + #[allow(clippy::wrong_self_convention)] impl IntoBytes for u8 { fn to_bytes(&self) -> Vec { vec![*self] diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index f3e081dd203..acc18280f1a 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -79,6 +79,7 @@ mod issue2894 { } // This should not be linted + #[allow(clippy::wrong_self_convention)] impl IntoBytes for u8 { fn to_bytes(&self) -> Vec { vec![*self] diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index e1410d2e652..a2c95963487 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -37,139 +37,139 @@ LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:93:24 + --> $DIR/use_self.rs:94:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:93:55 + --> $DIR/use_self.rs:94:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:108:13 + --> $DIR/use_self.rs:109:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:143:29 + --> $DIR/use_self.rs:144:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:144:21 + --> $DIR/use_self.rs:145:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:155:21 + --> $DIR/use_self.rs:156:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:156:13 + --> $DIR/use_self.rs:157:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:173:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:174:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:175:21 + --> $DIR/use_self.rs:176:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:217:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:218:13 + --> $DIR/use_self.rs:219:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:220:13 + --> $DIR/use_self.rs:221:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:239:13 + --> $DIR/use_self.rs:240:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:253:25 + --> $DIR/use_self.rs:254:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:254:13 + --> $DIR/use_self.rs:255:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:16 + --> $DIR/use_self.rs:259:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:22 + --> $DIR/use_self.rs:259:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:281:29 + --> $DIR/use_self.rs:282:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:282:13 + --> $DIR/use_self.rs:283:13 | LL | Foo { value } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:319:21 + --> $DIR/use_self.rs:320:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:320:19 + --> $DIR/use_self.rs:321:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:453:13 + --> $DIR/use_self.rs:454:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 6cfc0fcb4ca..ba9e19a1722 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -163,3 +163,35 @@ mod issue6307 { fn to_mut(&mut self); } } + +mod issue6727 { + trait ToU64 { + fn to_u64(self) -> u64; + fn to_u64_v2(&self) -> u64; + } + + #[derive(Clone, Copy)] + struct FooCopy; + + impl ToU64 for FooCopy { + fn to_u64(self) -> u64 { + 1 + } + // trigger lint + fn to_u64_v2(&self) -> u64 { + 1 + } + } + + struct FooNoCopy; + + impl ToU64 for FooNoCopy { + // trigger lint + fn to_u64(self) -> u64 { + 2 + } + fn to_u64_v2(&self) -> u64 { + 2 + } + } +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index f43fea0d513..6daa334ed48 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -39,7 +39,7 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} @@ -79,7 +79,7 @@ LL | pub fn is_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} @@ -119,14 +119,6 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference - --> $DIR/wrong_self_convention.rs:102:19 - | -LL | fn to_i32(self) {} - | ^^^^ - | - = help: consider choosing a less ambiguous name - error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:104:21 | @@ -159,14 +151,6 @@ LL | fn is_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference - --> $DIR/wrong_self_convention.rs:126:19 - | -LL | fn to_i32(self); - | ^^^^ - | - = help: consider choosing a less ambiguous name - error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:128:21 | @@ -191,5 +175,21 @@ LL | fn from_i32(self); | = help: consider choosing a less ambiguous name +error: methods with the following characteristics: (`to_*` and `self type is Copy`) usually take self by value + --> $DIR/wrong_self_convention.rs:181:22 + | +LL | fn to_u64_v2(&self) -> u64 { + | ^^^^^ + | + = help: consider choosing a less ambiguous name + +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference + --> $DIR/wrong_self_convention.rs:190:19 + | +LL | fn to_u64(self) -> u64 { + | ^^^^ + | + = help: consider choosing a less ambiguous name + error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr index 7662b38e67d..8095188758c 100644 --- a/tests/ui/wrong_self_conventions_mut.stderr +++ b/tests/ui/wrong_self_conventions_mut.stderr @@ -1,4 +1,4 @@ -error: methods called `to_*` usually take self by reference +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference --> $DIR/wrong_self_conventions_mut.rs:15:24 | LL | pub fn to_many(&mut self) -> Option<&mut [T]> { @@ -7,7 +7,7 @@ LL | pub fn to_many(&mut self) -> Option<&mut [T]> { = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: methods called like this: (`to_*` and `*_mut`) usually take self by mutable reference +error: methods with the following characteristics: (`to_*` and `*_mut`) usually take self by mutable reference --> $DIR/wrong_self_conventions_mut.rs:23:28 | LL | pub fn to_many_mut(&self) -> Option<&[T]> { -- cgit 1.4.1-3-g733a5 From b1f89ee02ff05d559a27949601bef628e498b128 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 17 Mar 2021 20:29:31 +0100 Subject: or_fun_call: trigger on unsafe blocks --- clippy_lints/src/methods/or_fun_call.rs | 13 +++++++++++-- tests/ui/or_fun_call.fixed | 14 ++++++++++++++ tests/ui/or_fun_call.rs | 14 ++++++++++++++ tests/ui/or_fun_call.stderr | 20 +++++++++++++++++++- 4 files changed, 58 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 1b43802a08e..22df3e028f9 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -6,6 +6,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::{BlockCheckMode, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -154,7 +155,6 @@ pub(super) fn check<'tcx>( } } } - if args.len() == 2 { match args[1].kind { hir::ExprKind::Call(ref fun, ref or_args) => { @@ -167,7 +167,16 @@ pub(super) fn check<'tcx>( hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); }, - _ => {}, + hir::ExprKind::Block(block, _) => { + if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules { + if let Some(block_expr) = block.expr { + if let hir::ExprKind::MethodCall(..) = block_expr.kind { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + } + } + } + }, + _ => (), } } } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 64347cae5da..660245f10c3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -132,4 +132,18 @@ fn f() -> Option<()> { Some(()) } +mod issue6675 { + unsafe fn foo() { + let mut s = "test".to_owned(); + None.unwrap_or_else(|| s.as_mut_vec()); + } + + fn bar() { + let mut s = "test".to_owned(); + None.unwrap_or_else(|| unsafe { s.as_mut_vec() }); + #[rustfmt::skip] + None.unwrap_or_else(|| unsafe { s.as_mut_vec() }); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7faab0017b2..c589b170bdd 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -132,4 +132,18 @@ fn f() -> Option<()> { Some(()) } +mod issue6675 { + unsafe fn foo() { + let mut s = "test".to_owned(); + None.unwrap_or(s.as_mut_vec()); + } + + fn bar() { + let mut s = "test".to_owned(); + None.unwrap_or(unsafe { s.as_mut_vec() }); + #[rustfmt::skip] + None.unwrap_or( unsafe { s.as_mut_vec() } ); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 1e2bfd490e0..d63b3443592 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -114,5 +114,23 @@ error: use of `or` followed by a function call 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 19 previous errors +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:138:14 + | +LL | None.unwrap_or(s.as_mut_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:143:14 + | +LL | None.unwrap_or(unsafe { s.as_mut_vec() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:145:14 + | +LL | None.unwrap_or( unsafe { s.as_mut_vec() } ); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` + +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From b42ec5e04d51e2fa16a689ce61128533559f09ff Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 18 Mar 2021 23:01:42 +0100 Subject: needless_question_mark: don't lint if Some(..) is inside a macro def and the ? is not. The suggestion would fail to apply. Fixes #6921 changelog: needless_question_mark: don't lint if Some(..) is inside a macro def and the ? is not. --- clippy_lints/src/needless_question_mark.rs | 7 ++++++- tests/ui/needless_question_mark.fixed | 25 +++++++++++++++++++++++++ tests/ui/needless_question_mark.rs | 25 +++++++++++++++++++++++++ tests/ui/needless_question_mark.stderr | 13 ++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 99e85e6683c..9852633b734 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_ok_ctor, is_some_ctor, meets_msrv}; +use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -173,6 +173,11 @@ fn is_some_or_ok_call<'a>( // question mark operator let inner_expr = &args[0]; + // if the inner expr is inside macro but the outer one is not, do not lint (#6921) + if differing_macro_contexts(expr.span, inner_expr.span) { + return None; + } + let inner_ty = cx.typeck_results().expr_ty(inner_expr); let outer_ty = cx.typeck_results().expr_ty(expr); diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index 71fb3565224..fd8433870bb 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -167,3 +167,28 @@ mod question_mark_both { needless_question_mark_result(); } } + +// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, +// the suggestion fails to apply; do not lint +macro_rules! some_in_macro { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} + +pub fn test1() { + let x = Some(3); + let _x = some_in_macro!(x?); +} + +// this one is ok because both the ? and the Some are both inside the macro def +macro_rules! some_and_qmark_in_macro { + ($expr:expr) => { + || -> Option<_> { Some($expr) }() + }; +} + +pub fn test2() { + let x = Some(3); + let _x = some_and_qmark_in_macro!(x?); +} diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index e31f6f48fa7..36d45ac7e03 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -167,3 +167,28 @@ mod question_mark_both { needless_question_mark_result(); } } + +// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, +// the suggestion fails to apply; do not lint +macro_rules! some_in_macro { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} + +pub fn test1() { + let x = Some(3); + let _x = some_in_macro!(x?); +} + +// this one is ok because both the ? and the Some are both inside the macro def +macro_rules! some_and_qmark_in_macro { + ($expr:expr) => { + || -> Option<_> { Some(Some($expr)?) }() + }; +} + +pub fn test2() { + let x = Some(3); + let _x = some_and_qmark_in_macro!(x?); +} diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 983c56031d8..7cbf1e505ad 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -84,5 +84,16 @@ error: question mark operator is useless here LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` -error: aborting due to 14 previous errors +error: question mark operator is useless here + --> $DIR/needless_question_mark.rs:187:27 + | +LL | || -> Option<_> { Some(Some($expr)?) }() + | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` +... +LL | let _x = some_and_qmark_in_macro!(x?); + | ---------------------------- 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 15 previous errors -- cgit 1.4.1-3-g733a5 From 1f2d01641d6ef2f283265eb603c7d231692b6a89 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 18 Mar 2021 19:45:13 +0100 Subject: wrong_self_convention: Enhance lint message --- clippy_lints/src/methods/mod.rs | 8 ++-- clippy_lints/src/methods/wrong_self_convention.rs | 19 +++++---- tests/ui/def_id_nocore.stderr | 2 +- tests/ui/wrong_self_convention.stderr | 48 +++++++++++------------ tests/ui/wrong_self_conventions_mut.stderr | 4 +- 5 files changed, 42 insertions(+), 39 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 722effc29b2..a3652c18a78 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2348,10 +2348,10 @@ impl SelfKind { #[must_use] fn description(self) -> &'static str { match self { - Self::Value => "self by value", - Self::Ref => "self by reference", - Self::RefMut => "self by mutable reference", - Self::No => "no self", + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", } } } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index bece8f251da..59e683aa9a7 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -51,13 +51,16 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => this.fmt(f), - Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), - Self::EndsWith(this) => '*'.fmt(f).and_then(|_| this.fmt(f)), - Self::NotEndsWith(this) => '~'.fmt(f).and_then(|_| this.fmt(f)), - Self::IsSelfTypeCopy(is_true) => format!("self type is {} Copy", if is_true { "" } else { "not" }).fmt(f), + Self::Eq(this) => format!("`{}`", this).fmt(f), + Self::StartsWith(this) => format!("`{}*`", this).fmt(f), + Self::EndsWith(this) => format!("`*{}`", this).fmt(f), + Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f), + Self::IsSelfTypeCopy(is_true) => { + format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + }, Self::ImplementsTrait(is_true) => { - format!("Method {} implement a trait", if is_true { "" } else { "do not" }).fmt(f) + let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; + format!("Method{} implement{} a trait", negation, s_suffix).fmt(f) }, } } @@ -99,7 +102,7 @@ pub(super) fn check<'tcx>( { None } else { - Some(format!("`{}`", &conv.to_string())) + Some(conv.to_string()) } }) .collect::>() @@ -107,7 +110,7 @@ pub(super) fn check<'tcx>( format!("methods with the following characteristics: ({})", &s) } else { - format!("methods called `{}`", &conventions[0]) + format!("methods called {}", &conventions[0]) } }; diff --git a/tests/ui/def_id_nocore.stderr b/tests/ui/def_id_nocore.stderr index a3e9cc75b08..702684f6b43 100644 --- a/tests/ui/def_id_nocore.stderr +++ b/tests/ui/def_id_nocore.stderr @@ -1,4 +1,4 @@ -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/def_id_nocore.rs:26:19 | LL | pub fn as_ref(self) -> &'static str { diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 6daa334ed48..1d58a12ac79 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,4 +1,4 @@ -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} @@ -7,7 +7,7 @@ LL | fn from_i32(self) {} = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} @@ -15,7 +15,7 @@ LL | pub fn from_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} @@ -23,7 +23,7 @@ LL | fn as_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} @@ -31,7 +31,7 @@ LL | fn into_i32(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} @@ -39,7 +39,7 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} @@ -47,7 +47,7 @@ LL | fn to_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} @@ -55,7 +55,7 @@ LL | fn from_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} @@ -63,7 +63,7 @@ LL | pub fn as_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} @@ -71,7 +71,7 @@ LL | pub fn into_i64(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} @@ -79,7 +79,7 @@ LL | pub fn is_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} @@ -87,7 +87,7 @@ LL | pub fn to_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} @@ -95,7 +95,7 @@ LL | pub fn from_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:95:19 | LL | fn as_i32(self) {} @@ -103,7 +103,7 @@ LL | fn as_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:98:25 | LL | fn into_i32_ref(&self) {} @@ -111,7 +111,7 @@ LL | fn into_i32_ref(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} @@ -119,7 +119,7 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} @@ -127,7 +127,7 @@ LL | fn from_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); @@ -135,7 +135,7 @@ LL | fn as_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:122:25 | LL | fn into_i32_ref(&self); @@ -143,7 +143,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); @@ -151,7 +151,7 @@ LL | fn is_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:128:21 | LL | fn from_i32(self); @@ -159,7 +159,7 @@ LL | fn from_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:146:25 | LL | fn into_i32_ref(&self); @@ -167,7 +167,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); @@ -175,7 +175,7 @@ LL | fn from_i32(self); | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is Copy`) usually take self by value +error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value --> $DIR/wrong_self_convention.rs:181:22 | LL | fn to_u64_v2(&self) -> u64 { @@ -183,7 +183,7 @@ LL | fn to_u64_v2(&self) -> u64 { | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:190:19 | LL | fn to_u64(self) -> u64 { diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr index 8095188758c..6ce37c59491 100644 --- a/tests/ui/wrong_self_conventions_mut.stderr +++ b/tests/ui/wrong_self_conventions_mut.stderr @@ -1,4 +1,4 @@ -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_conventions_mut.rs:15:24 | LL | pub fn to_many(&mut self) -> Option<&mut [T]> { @@ -7,7 +7,7 @@ LL | pub fn to_many(&mut self) -> Option<&mut [T]> { = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `*_mut`) usually take self by mutable reference +error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference --> $DIR/wrong_self_conventions_mut.rs:23:28 | LL | pub fn to_many_mut(&self) -> Option<&[T]> { -- cgit 1.4.1-3-g733a5 From 296751f643952f02f6fa721c90bbb81f4183e3f6 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Mar 2021 10:24:10 +0100 Subject: Fix bad suggestion for generics in `new_without_default` lint --- clippy_lints/src/new_without_default.rs | 15 ++++++++----- tests/ui/new_without_default.rs | 15 +++++++++++++ tests/ui/new_without_default.stderr | 38 +++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 502e5e4bf37..3789572ad43 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::paths; +use clippy_utils::source::snippet; use clippy_utils::sugg::DiagnosticBuilderExt; use clippy_utils::{get_trait_def_id, return_ty}; use if_chain::if_chain; @@ -62,7 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { #[allow(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, items, .. + of_trait: None, + ref generics, + items, + .. }) = item.kind { for assoc_item in items { @@ -126,6 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } } + let generics_sugg = snippet(cx, generics.span, ""); span_lint_hir_and_then( cx, NEW_WITHOUT_DEFAULT, @@ -140,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { cx, item.span, "try this", - &create_new_without_default_suggest_msg(self_ty), + &create_new_without_default_suggest_msg(self_ty, &generics_sugg), Applicability::MaybeIncorrect, ); }, @@ -155,12 +160,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } } -fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { +fn create_new_without_default_suggest_msg(ty: Ty<'_>, generics_sugg: &str) -> String { #[rustfmt::skip] format!( -"impl Default for {} {{ +"impl{} Default for {} {{ fn default() -> Self {{ Self::new() }} -}}", ty) +}}", generics_sugg, ty) } diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 3b6041823d8..64659b63f46 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -159,4 +159,19 @@ impl NewNotEqualToDerive { } } +// see #6933 +pub struct FooGenerics(std::marker::PhantomData); +impl FooGenerics { + pub fn new() -> Self { + Self(Default::default()) + } +} + +pub struct BarGenerics(std::marker::PhantomData); +impl BarGenerics { + pub fn new() -> Self { + Self(Default::default()) + } +} + fn main() {} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index e529e441eb7..973836f75a9 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -43,7 +43,7 @@ LL | | } | help: try this | -LL | impl Default for LtKo<'c> { +LL | impl<'c> Default for LtKo<'c> { LL | fn default() -> Self { LL | Self::new() LL | } @@ -67,5 +67,39 @@ LL | } LL | } | -error: aborting due to 4 previous errors +error: you should consider adding a `Default` implementation for `FooGenerics` + --> $DIR/new_without_default.rs:165:5 + | +LL | / pub fn new() -> Self { +LL | | Self(Default::default()) +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for FooGenerics { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: you should consider adding a `Default` implementation for `BarGenerics` + --> $DIR/new_without_default.rs:172:5 + | +LL | / pub fn new() -> Self { +LL | | Self(Default::default()) +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for BarGenerics { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 00a2d7ad7e1a041e47f618a019702bfb37eca680 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Mar 2021 16:11:19 +0100 Subject: Fix bad suggestion that needs curly braces for `match_single_binding` lint --- clippy_lints/src/matches.rs | 14 +++++++++++++- tests/ui/match_single_binding.fixed | 29 +++++++++++++++++++++++++++++ tests/ui/match_single_binding.rs | 29 +++++++++++++++++++++++++++++ tests/ui/match_single_binding.stderr | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d43cb32ee51..1a8ec9c6ec3 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1353,6 +1353,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { } } +#[allow(clippy::too_many_lines)] 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; @@ -1427,7 +1428,18 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); cbrace_start = format!("{{\n{}", indent); } - }; + } + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } ( expr.span, format!( diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 526e94b10bd..4709b5b0157 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -115,4 +115,33 @@ fn main() { // => _ => println!("Not an array index start"), } + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => { + let (min, max) = iter.size_hint(); + (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) + }, + None => (0, Some(0)), + } + } + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + let (a, b) = get_tup(); + println!("a {:?} and b {:?}", a, b); + }, + None => println!("nothing"), + } } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 6a2ca7c5e93..6a6b3e8e8a9 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -132,4 +132,33 @@ fn main() { // => _ => println!("Not an array index start"), } + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => match iter.size_hint() { + (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), + }, + None => (0, Some(0)), + } + } + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + match get_tup() { + (a, b) => println!("a {:?} and b {:?}", a, b), + } + }, + None => println!("nothing"), + } } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index cbbf5d29c02..73cc867dd9f 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -178,5 +178,36 @@ LL | | _ => println!("Single branch"), LL | | } | |_____^ help: consider using the match body instead: `println!("Single branch");` -error: aborting due to 12 previous errors +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:146:36 + | +LL | Some((iter, _item)) => match iter.size_hint() { + | ____________________________________^ +LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), +LL | | }, + | |_____________^ + | +help: consider using `let` statement + | +LL | Some((iter, _item)) => { +LL | let (min, max) = iter.size_hint(); +LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) +LL | }, + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:158:13 + | +LL | / match get_tup() { +LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | } + | |_____________^ + | +help: consider using `let` statement + | +LL | let (a, b) = get_tup(); +LL | println!("a {:?} and b {:?}", a, b); + | + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 3ddaabcbc9cf223f6a2f8f645b2092166f911eab Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 21 Mar 2021 09:50:35 +0100 Subject: Fix suggestion with generics for `field_reassign_with_default` lint --- clippy_lints/src/default.rs | 19 +++++++++++++++++++ tests/ui/field_reassign_with_default.rs | 18 ++++++++++++++++++ tests/ui/field_reassign_with_default.stderr | 26 +++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index d046e894792..568a174445c 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -104,6 +104,7 @@ impl LateLintPass<'_> for Default { } } + #[allow(clippy::too_many_lines)] fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding @@ -197,6 +198,24 @@ impl LateLintPass<'_> for Default { .collect::>() .join(", "); + // give correct suggestion if generics are involved (see #6944) + let binding_type = if_chain! { + if let ty::Adt(adt_def, substs) = binding_type.kind(); + if !substs.is_empty(); + let adt_def_ty_name = cx.tcx.item_name(adt_def.did); + let generic_args = substs.iter().collect::>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); + then { + format!("{}::<{}>", adt_def_ty_name, &tys_str) + } else { + binding_type.to_string() + } + }; + let sugg = if ext_with_default { if field_list.is_empty() { format!("{}::default()", binding_type) diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 9fc208f5332..1368c5d7984 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -136,6 +136,13 @@ fn main() { // Don't lint in external macros field_reassign_with_default!(); + + // be sure suggestion is correct with generics + let mut a: Wrapper = Default::default(); + a.i = true; + + let mut a: WrapperMulti = Default::default(); + a.i = 42; } mod m { @@ -145,3 +152,14 @@ mod m { b: u64, } } + +#[derive(Default)] +struct Wrapper { + i: T, +} + +#[derive(Default)] +struct WrapperMulti { + i: T, + j: U, +} diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 2f0f28f7bb7..dd7c0360bb1 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -83,5 +83,29 @@ note: consider initializing the variable with `C { i: vec![1], ..Default::defaul LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:142:5 + | +LL | a.i = true; + | ^^^^^^^^^^^ + | +note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:141:5 + | +LL | let mut a: Wrapper = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:145:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:144:5 + | +LL | let mut a: WrapperMulti = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 2ffee89b752212d4e7d72ece16636d7c43b11c63 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Sat, 20 Mar 2021 14:30:45 +0100 Subject: search_is_some: check also when search is none --- clippy_lints/src/methods/mod.rs | 57 ++++++++++++--- clippy_lints/src/methods/search_is_some.rs | 107 +++++++++++++++++++++-------- tests/ui/search_is_some.rs | 37 +++++++++- tests/ui/search_is_some.stderr | 44 ++++++++++-- tests/ui/search_is_some_fixable.fixed | 35 +++++++++- tests/ui/search_is_some_fixable.rs | 35 +++++++++- tests/ui/search_is_some_fixable.stderr | 92 ++++++++++++++++++++++++- 7 files changed, 359 insertions(+), 48 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 46deb20f97d..5be28354769 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -588,26 +588,31 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for an iterator or string search (such as `find()`, - /// `position()`, or `rposition()`) followed by a call to `is_some()`. + /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.any(_)` or `_.contains(_)`. + /// **Why is this bad?** Readability, this can be written more concisely as: + /// * `_.any(_)`, or `_.contains(_)` for `is_some()`, + /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`. /// /// **Known problems:** None. /// /// **Example:** /// ```rust - /// # let vec = vec![1]; + /// let vec = vec![1]; /// vec.iter().find(|x| **x == 0).is_some(); + /// + /// let _ = "hello world".find("world").is_none(); /// ``` /// Could be written as /// ```rust - /// # let vec = vec![1]; + /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); + /// + /// let _ = !"hello world".contains("world"); /// ``` pub SEARCH_IS_SOME, complexity, - "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`" + "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)" } declare_clippy_lint! { @@ -1720,12 +1725,42 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]), - ["is_some", "find"] => search_is_some::check(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), - ["is_some", "position"] => { - search_is_some::check(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1]) + [option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => { + search_is_some::check( + cx, + expr, + "find", + option_check_method, + arg_lists[1], + arg_lists[0], + method_spans[1], + ) }, - ["is_some", "rposition"] => { - search_is_some::check(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) + [option_check_method, "position"] + if "is_some" == *option_check_method || "is_none" == *option_check_method => + { + search_is_some::check( + cx, + expr, + "position", + option_check_method, + arg_lists[1], + arg_lists[0], + method_spans[1], + ) + }, + [option_check_method, "rposition"] + if "is_some" == *option_check_method || "is_none" == *option_check_method => + { + search_is_some::check( + cx, + expr, + "rposition", + option_check_method, + arg_lists[1], + arg_lists[0], + method_spans[1], + ) }, ["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]), ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 13546dc1779..de7d168295f 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -14,11 +14,13 @@ use rustc_span::symbol::sym; use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` -/// or calling `find()` on a string followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` or `is_none()` +#[allow(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, search_method: &str, + option_check_method: &str, search_args: &'tcx [hir::Expr<'_>], is_some_args: &'tcx [hir::Expr<'_>], method_span: Span, @@ -26,10 +28,9 @@ pub(super) fn check<'tcx>( // lint if caller of search is an Iterator if is_trait_method(cx, &is_some_args[0], sym::Iterator) { let msg = format!( - "called `is_some()` after searching an `Iterator` with `{}`", - search_method + "called `{}()` after searching an `Iterator` with `{}`", + option_check_method, search_method ); - let hint = "this is more succinctly expressed by calling `any()`"; let search_snippet = snippet(cx, search_args[1].span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -53,20 +54,49 @@ pub(super) fn check<'tcx>( } }; // add note if not multi-line - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `any()` instead", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - Applicability::MachineApplicable, - ); + match option_check_method { + "is_some" => { + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `any()` instead", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + }, + "is_none" => { + let iter = snippet(cx, search_args[0].span, ".."); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.any()` instead", + format!( + "!{}.any({})", + iter, + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + }, + _ => (), + } } else { - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); + let hint = format!( + "this is more succinctly expressed by calling `any()`{}", + if option_check_method == "is_none" { + " with negation" + } else { + "" + } + ); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint); } } // lint if `find()` is called by `String` or `&str` @@ -83,18 +113,37 @@ pub(super) fn check<'tcx>( if is_string_or_str_slice(&search_args[0]); if is_string_or_str_slice(&search_args[1]); then { - let msg = "called `is_some()` after calling `find()` on a string"; - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - msg, - "use `contains()` instead", - format!("contains({})", find_arg), - applicability, - ); + let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); + match option_check_method { + "is_some" => { + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `contains()` instead", + format!("contains({})", find_arg), + applicability, + ); + }, + "is_none" => { + let string = snippet(cx, search_args[0].span, ".."); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.contains()` instead", + format!("!{}.contains({})", string, find_arg), + applicability, + ); + }, + _ => (), + } } } } diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index f0dc3b3d06b..72bc6ef35d3 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -1,8 +1,9 @@ // aux-build:option_helpers.rs +#![warn(clippy::search_is_some)] +#![allow(dead_code)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; -#[warn(clippy::search_is_some)] #[rustfmt::skip] fn main() { let v = vec![3, 2, 1, 0, -1, -2, -3]; @@ -36,3 +37,37 @@ fn main() { // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); } + +#[rustfmt::skip] +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + + // Check `find().is_none()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_none(); + + // Check `position().is_none()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_none(); + + // Check `rposition().is_none()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_none(); + + // Check that we don't lint if the caller is not an `Iterator` or string + let falsepos = IteratorFalsePositives { foo: 0 }; + let _ = falsepos.find().is_none(); + let _ = falsepos.position().is_none(); + let _ = falsepos.rposition().is_none(); + // check that we don't lint if `find()` is called with + // `Pattern` that is not a string + let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none(); +} diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index c601f568c60..f3c758e451e 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,5 +1,5 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> $DIR/search_is_some.rs:13:13 + --> $DIR/search_is_some.rs:14:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -12,7 +12,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `position` - --> $DIR/search_is_some.rs:19:13 + --> $DIR/search_is_some.rs:20:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -24,7 +24,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `rposition` - --> $DIR/search_is_some.rs:25:13 + --> $DIR/search_is_some.rs:26:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -35,5 +35,41 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: aborting due to 3 previous errors +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some.rs:48:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: called `is_none()` after searching an `Iterator` with `position` + --> $DIR/search_is_some.rs:54:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: called `is_none()` after searching an `Iterator` with `rposition` + --> $DIR/search_is_some.rs:60:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: aborting due to 6 previous errors diff --git a/tests/ui/search_is_some_fixable.fixed b/tests/ui/search_is_some_fixable.fixed index dc3f290e562..62ff16f67f4 100644 --- a/tests/ui/search_is_some_fixable.fixed +++ b/tests/ui/search_is_some_fixable.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(dead_code)] #![warn(clippy::search_is_some)] fn main() { @@ -33,3 +33,36 @@ fn main() { let _ = s1[2..].contains(&s2); let _ = s1[2..].contains(&s2[2..]); } + +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_none()`, single-line case. + let _ = !v.iter().any(|x| *x < 0); + let _ = !(0..1).any(|x| **y == x); // one dereference less + let _ = !(0..1).any(|x| x == 0); + let _ = !v.iter().any(|x| *x == 0); + + // Check `position().is_none()`, single-line case. + let _ = !v.iter().any(|&x| x < 0); + + // Check `rposition().is_none()`, single-line case. + let _ = !v.iter().any(|&x| x < 0); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + + // caller of `find()` is a `&`static str` + let _ = !"hello world".contains("world"); + let _ = !"hello world".contains(&s2); + let _ = !"hello world".contains(&s2[2..]); + // caller of `find()` is a `String` + let _ = !s1.contains("world"); + let _ = !s1.contains(&s2); + let _ = !s1.contains(&s2[2..]); + // caller of `find()` is slice of `String` + let _ = !s1[2..].contains("world"); + let _ = !s1[2..].contains(&s2); + let _ = !s1[2..].contains(&s2[2..]); +} diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs index 146cf5adf1b..8407f716647 100644 --- a/tests/ui/search_is_some_fixable.rs +++ b/tests/ui/search_is_some_fixable.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(dead_code)] #![warn(clippy::search_is_some)] fn main() { @@ -33,3 +33,36 @@ fn main() { let _ = s1[2..].find(&s2).is_some(); let _ = s1[2..].find(&s2[2..]).is_some(); } + +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_none()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_none(); + let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_none(); + let _ = v.iter().find(|x| **x == 0).is_none(); + + // Check `position().is_none()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_none(); + + // Check `rposition().is_none()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_none(); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + + // caller of `find()` is a `&`static str` + let _ = "hello world".find("world").is_none(); + let _ = "hello world".find(&s2).is_none(); + let _ = "hello world".find(&s2[2..]).is_none(); + // caller of `find()` is a `String` + let _ = s1.find("world").is_none(); + let _ = s1.find(&s2).is_none(); + let _ = s1.find(&s2[2..]).is_none(); + // caller of `find()` is slice of `String` + let _ = s1[2..].find("world").is_none(); + let _ = s1[2..].find(&s2).is_none(); + let _ = s1[2..].find(&s2[2..]).is_none(); +} diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index 23c1d9a901b..bd1b6955a97 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -90,5 +90,95 @@ error: called `is_some()` after calling `find()` on a string LL | let _ = s1[2..].find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: aborting due to 15 previous errors +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:42:13 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:43:13 + | +LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:44:13 + | +LL | let _ = (0..1).find(|x| *x == 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:45:13 + | +LL | let _ = v.iter().find(|x| **x == 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)` + +error: called `is_none()` after searching an `Iterator` with `position` + --> $DIR/search_is_some_fixable.rs:48:13 + | +LL | let _ = v.iter().position(|&x| x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + +error: called `is_none()` after searching an `Iterator` with `rposition` + --> $DIR/search_is_some_fixable.rs:51:13 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:57:13 + | +LL | let _ = "hello world".find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:58:13 + | +LL | let _ = "hello world".find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:59:13 + | +LL | let _ = "hello world".find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:61:13 + | +LL | let _ = s1.find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:62:13 + | +LL | let _ = s1.find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:63:13 + | +LL | let _ = s1.find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:65:13 + | +LL | let _ = s1[2..].find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:66:13 + | +LL | let _ = s1[2..].find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:67:13 + | +LL | let _ = s1[2..].find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])` + +error: aborting due to 30 previous errors -- cgit 1.4.1-3-g733a5 From 45e775697ea4376224275e0c568fbefed6645fee Mon Sep 17 00:00:00 2001 From: Jamie Quigley Date: Mon, 22 Mar 2021 19:34:20 +0000 Subject: Ignore str::len() in or_fun_call lint. --- clippy_lints/src/methods/or_fun_call.rs | 2 +- tests/ui/or_fun_call.fixed | 3 +++ tests/ui/or_fun_call.rs | 3 +++ tests/ui/or_fun_call.stderr | 6 +++--- 4 files changed, 10 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index e6b473dbafa..20861ff8686 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); match ty.kind() { - ty::Slice(_) | ty::Array(_, _) => return, + ty::Slice(_) | ty::Array(_, _) | ty::Str => return, _ => (), } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 660245f10c3..4390ff7dc30 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -120,6 +120,9 @@ fn test_or_with_ctors() { let slice = &["foo"][..]; let _ = opt.ok_or(slice.len()); + + let string = "foo"; + let _ = opt.ok_or(string.len()); } // Issue 4514 - early return diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index c589b170bdd..75908c974cc 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -120,6 +120,9 @@ fn test_or_with_ctors() { let slice = &["foo"][..]; let _ = opt.ok_or(slice.len()); + + let string = "foo"; + let _ = opt.ok_or(string.len()); } // Issue 4514 - early return diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index d63b3443592..9905029ce91 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -115,19 +115,19 @@ LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:138:14 + --> $DIR/or_fun_call.rs:141:14 | LL | None.unwrap_or(s.as_mut_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:143:14 + --> $DIR/or_fun_call.rs:146:14 | LL | None.unwrap_or(unsafe { s.as_mut_vec() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:145:14 + --> $DIR/or_fun_call.rs:148:14 | LL | None.unwrap_or( unsafe { s.as_mut_vec() } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` -- cgit 1.4.1-3-g733a5 From 9132dbdf310bbffbf5a8efd3b52f68189e3ee15e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 23 Mar 2021 10:40:27 -0500 Subject: Factor out eq_ty_kind --- clippy_lints/src/methods/mod.rs | 18 ++++++++---------- clippy_utils/src/hir_utils.rs | 12 ++---------- 2 files changed, 10 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b37d744269e..d77f9c03cae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -62,13 +62,11 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{ - contains_return, get_trait_def_id, in_macro, iter_input_pats, match_qpath, method_calls, paths, return_ty, - SpanlessEq, -}; +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::{TraitItem, TraitItemKind}; +use rustc_hir::def::Res; +use rustc_hir::{PrimTy, QPath, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; @@ -1872,7 +1870,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { 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.output_type.matches(&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) @@ -2199,8 +2197,8 @@ enum OutType { } impl OutType { - fn matches(self, cx: &LateContext<'_>, ty: &hir::FnRetTy<'_>) -> bool { - let is_unit = |ty: &hir::Ty<'_>| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(&[])); + fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); match (self, ty) { (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true, @@ -2213,8 +2211,8 @@ impl OutType { } fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(ref p) = ty.kind { - match_qpath(p, &["bool"]) + if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + matches!(path.res, Res::PrimTy(PrimTy::Bool)) } else { false } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 4c13a185d5d..5cfeaee28cf 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -80,10 +80,6 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { self.inter_expr().eq_path_segments(left, right) } - - pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { - self.inter_expr().eq_ty_kind(left, right) - } } struct HirEqInterExpr<'a, 'b, 'tcx> { @@ -378,13 +374,9 @@ impl HirEqInterExpr<'_, '_, '_> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { - self.eq_ty_kind(&left.kind, &right.kind) - } - #[allow(clippy::similar_names)] - fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { - match (left, right) { + fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + match (&left.kind, &right.kind) { (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { let cx = self.inner.cx; -- cgit 1.4.1-3-g733a5 From 3b8e85a5dccd47c7284b30ad29d3144d5c152235 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 25 Mar 2021 00:15:15 +0900 Subject: fix false positive in manual_flatten --- clippy_lints/src/loops/manual_flatten.rs | 10 +++- tests/ui/manual_flatten.rs | 22 +++++++++ tests/ui/manual_flatten.stderr | 85 +++++++++++++++++++++++++++----- 3 files changed, 105 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index be87f67d300..8d2b9cccba4 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -6,6 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::source_map::Span; /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the @@ -54,6 +55,13 @@ pub(super) fn check<'tcx>( // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); + let copied = match cx.typeck_results().expr_ty(match_expr).kind() { + ty::Ref(_, inner, _) => match inner.kind() { + ty::Ref(..) => ".copied()", + _ => "" + } + _ => "" + }; span_lint_and_then( cx, @@ -61,7 +69,7 @@ pub(super) fn check<'tcx>( span, &msg, |diag| { - let sugg = format!("{}.flatten()", arg_snippet); + let sugg = format!("{}{}.flatten()", arg_snippet, copied); diag.span_suggestion( arg.span, "try", diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index cff68eca933..b5bd35a6878 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_flatten)] +#![allow(clippy::useless_vec)] fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression @@ -69,6 +70,27 @@ fn main() { } } + let vec_of_ref = vec![&Some(1)]; + for n in &vec_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + + let vec_of_ref = &vec_of_ref; + for n in vec_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + + let slice_of_ref = &[&Some(1)]; + for n in slice_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + // Using manual flatten should not trigger the lint for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { println!("{}", n); diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr index 855dd9130e2..be5f8a1d818 100644 --- a/tests/ui/manual_flatten.stderr +++ b/tests/ui/manual_flatten.stderr @@ -1,5 +1,5 @@ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:6:5 + --> $DIR/manual_flatten.rs:7:5 | LL | for n in x { | ^ - help: try: `x.into_iter().flatten()` @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::manual-flatten` implied by `-D warnings` help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:7:9 + --> $DIR/manual_flatten.rs:8:9 | LL | / if let Some(y) = n { LL | | println!("{}", y); @@ -21,7 +21,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:14:5 + --> $DIR/manual_flatten.rs:15:5 | LL | for n in y.clone() { | ^ --------- help: try: `y.clone().into_iter().flatten()` @@ -34,7 +34,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:15:9 + --> $DIR/manual_flatten.rs:16:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -42,7 +42,7 @@ LL | | }; | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:21:5 + --> $DIR/manual_flatten.rs:22:5 | LL | for n in &y { | ^ -- help: try: `y.iter().flatten()` @@ -55,7 +55,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:22:9 + --> $DIR/manual_flatten.rs:23:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -63,7 +63,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:31:5 + --> $DIR/manual_flatten.rs:32:5 | LL | for n in z { | ^ - help: try: `z.into_iter().flatten()` @@ -76,7 +76,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:32:9 + --> $DIR/manual_flatten.rs:33:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -84,7 +84,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:40:5 + --> $DIR/manual_flatten.rs:41:5 | LL | for n in z { | ^ - help: try: `z.flatten()` @@ -97,12 +97,75 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:41:9 + --> $DIR/manual_flatten.rs:42:9 | LL | / if let Some(m) = n { LL | | println!("{}", m); LL | | } | |_________^ -error: aborting due to 5 previous errors +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:74:5 + | +LL | for n in &vec_of_ref { + | ^ ----------- help: try: `vec_of_ref.iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:75:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:81:5 + | +LL | for n in vec_of_ref { + | ^ ---------- help: try: `vec_of_ref.into_iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:82:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:88:5 + | +LL | for n in slice_of_ref { + | ^ ------------ help: try: `slice_of_ref.into_iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:89:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 2f8d71b9dc5cbb2a26ecd88f943c8a225e28108f Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 25 Mar 2021 00:17:43 +0900 Subject: merge imports --- clippy_lints/src/methods/utils.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 8de23e1bc6e..ac6b55396da 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -5,8 +5,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; pub(super) fn derefs_to_slice<'tcx>( -- cgit 1.4.1-3-g733a5 From 99b8a671981eb9bf2c624027940bf9ae4cd23087 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 22 Mar 2021 21:38:14 -0400 Subject: Fix false positive with `new_ret_no_self` when returning `Self` with different generic arguments --- clippy_lints/src/methods/mod.rs | 14 +++++++++++--- clippy_utils/src/ty.rs | 11 ++++++++++- tests/ui/new_ret_no_self.rs | 10 ++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d77f9c03cae..b6be77b03e5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -61,7 +61,7 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{contains_adt, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; @@ -1916,7 +1916,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let ret_ty = return_ty(cx, impl_item.hir_id()); // walk the return type and check for Self (this does not check associated types) - if contains_ty(ret_ty, self_ty) { + if let Some(self_adt) = self_ty.ty_adt_def() { + if contains_adt(ret_ty, self_adt) { + return; + } + } else if contains_ty(ret_ty, self_ty) { return; } @@ -1926,7 +1930,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { // walk the associated type and check for Self - if contains_ty(projection_predicate.ty, self_ty) { + if let Some(self_adt) = self_ty.ty_adt_def() { + if contains_adt(projection_predicate.ty, self_adt) { + return; + } + } else if contains_ty(projection_predicate.ty, self_ty) { return; } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f4a1ae67da3..b9724efb04f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -11,7 +11,7 @@ use rustc_hir::{TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, IntTy, Ty, TypeFoldable, UintTy}; +use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -43,6 +43,15 @@ pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { }) } +/// Walks into `ty` and returns `true` if any inner type is any instance of the given abstract data +/// type.` +pub fn contains_adt(ty: Ty<'_>, adt: &AdtDef) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index e82873629a5..2f315ffe298 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -340,3 +340,13 @@ mod issue5435 { } } } + +// issue #1724 +struct RetOtherSelf(T); +struct RetOtherSelfWrapper(T); + +impl RetOtherSelf { + fn new(t: T) -> RetOtherSelf> { + RetOtherSelf(RetOtherSelfWrapper(t)) + } +} -- cgit 1.4.1-3-g733a5 From 6e88900f9ecc3b1e9ffce777d5e74e0eb3554ef4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 24 Mar 2021 16:19:27 -0400 Subject: Rename `contains_adt` to `contains_adt_constructor` --- clippy_lints/src/methods/mod.rs | 6 +++--- clippy_utils/src/ty.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6be77b03e5..fccdee07877 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -61,7 +61,7 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_adt, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; @@ -1917,7 +1917,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { // walk the return type and check for Self (this does not check associated types) if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt(ret_ty, self_adt) { + if contains_adt_constructor(ret_ty, self_adt) { return; } } else if contains_ty(ret_ty, self_ty) { @@ -1931,7 +1931,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { // walk the associated type and check for Self if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt(projection_predicate.ty, self_adt) { + if contains_adt_constructor(projection_predicate.ty, self_adt) { return; } } else if contains_ty(projection_predicate.ty, self_ty) { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index b9724efb04f..807cfbc4c7f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -43,9 +43,9 @@ pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { }) } -/// Walks into `ty` and returns `true` if any inner type is any instance of the given abstract data -/// type.` -pub fn contains_adt(ty: Ty<'_>, adt: &AdtDef) -> bool { +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt +/// constructor. +pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, -- cgit 1.4.1-3-g733a5 From e9ebc27525757f967131ecb3570c1438f8de4efb Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Tue, 23 Mar 2021 17:10:27 -0700 Subject: check for `.to_string().into_bytes()` in string_lit_to_bytes apply changes from review --- clippy_lints/src/strings.rs | 29 +++++++++++++++++++++++++++++ tests/ui/string_lit_as_bytes.fixed | 6 ++++++ tests/ui/string_lit_as_bytes.rs | 6 ++++++ tests/ui/string_lit_as_bytes.stderr | 18 +++++++++++++++--- 4 files changed, 56 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 760f5e3b432..99ca7ef77a5 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -284,6 +284,35 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } } } + + if_chain! { + if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind; + if path.ident.name == sym!(into_bytes); + if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind; + if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string"); + if let ExprKind::Lit(lit) = &recv.kind; + if let LitKind::Str(lit_content, _) = &lit.node; + + if lit_content.as_str().is_ascii(); + if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT; + if !recv.span.from_expansion(); + then { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `into_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}.to_vec()", + snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) + ), + applicability, + ); + } + } } } diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index ccf8f61c4a9..dd22bfa5c53 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -8,10 +8,16 @@ fn str_lit_as_bytes() { let bs = br###"raw string with 3# plus " ""###; + let bs = b"lit to string".to_vec(); + let bs = b"lit to owned".to_vec(); + // no warning, because these cannot be written as byte string literals: let ubs = "☃".as_bytes(); let ubs = "hello there! this is a very long string".as_bytes(); + let ubs = "☃".to_string().into_bytes(); + let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes(); + let strify = stringify!(foobar).as_bytes(); let current_version = env!("CARGO_PKG_VERSION").as_bytes(); diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index 178df08e249..d2a710ed6b8 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -8,10 +8,16 @@ fn str_lit_as_bytes() { let bs = r###"raw string with 3# plus " ""###.as_bytes(); + let bs = "lit to string".to_string().into_bytes(); + let bs = "lit to owned".to_owned().into_bytes(); + // no warning, because these cannot be written as byte string literals: let ubs = "☃".as_bytes(); let ubs = "hello there! this is a very long string".as_bytes(); + let ubs = "☃".to_string().into_bytes(); + let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes(); + let strify = stringify!(foobar).as_bytes(); let current_version = env!("CARGO_PKG_VERSION").as_bytes(); diff --git a/tests/ui/string_lit_as_bytes.stderr b/tests/ui/string_lit_as_bytes.stderr index 99c512354d5..e0ddb070b50 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -12,17 +12,29 @@ error: calling `as_bytes()` on a string literal 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 `into_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:11:14 + | +LL | let bs = "lit to string".to_string().into_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to string".to_vec()` + +error: calling `into_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:12:14 + | +LL | let bs = "lit to owned".to_owned().into_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to owned".to_vec()` + error: calling `as_bytes()` on `include_str!(..)` - --> $DIR/string_lit_as_bytes.rs:19:22 + --> $DIR/string_lit_as_bytes.rs:25: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:21:13 + --> $DIR/string_lit_as_bytes.rs:27:13 | LL | let _ = "string with newline/t/n".as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 0ff68bb151ee8e93c0a6aff9f3cda3ac3f757d2f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 22:30:14 -0400 Subject: Improve `redundant_slicing` Fix bad suggestion when a reborrow might be required Fix bad suggestion when the value being sliced is a macro call Don't lint inside of a macro due to the previous context sensitive changes --- clippy_lints/src/redundant_slicing.rs | 37 ++++++++++++++++++++++++++--------- tests/ui/redundant_slicing.rs | 29 +++++++++++++++++++++++---- tests/ui/redundant_slicing.stderr | 32 +++++++++++++++++++++++------- 3 files changed, 78 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 6da7b5fbcc8..9c6cd7b4fa6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_parent_expr, in_macro}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty::TyS}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -40,26 +41,44 @@ declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); impl LateLintPass<'_> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if in_macro(expr.span) { return; } + let ctxt = expr.span.ctxt(); if_chain! { - if let ExprKind::AddrOf(_, _, addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; + if addressee.span.ctxt() == ctxt; if let ExprKind::Index(indexed, range) = addressee.kind; if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed)); then { let mut app = Applicability::MachineApplicable; - let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned(); + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + + let (reborrow_str, help_str) = if mutability == Mutability::Mut { + // The slice was used to reborrow the mutable reference. + ("&mut *", "reborrow the original value instead") + } else if matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), + .. + }) + ) { + // The slice was used to make a temporary reference. + ("&*", "reborrow the original value instead") + } else { + ("", "use the original value instead") + }; span_lint_and_sugg( cx, REDUNDANT_SLICING, expr.span, "redundant slicing of the whole range", - "use the original slice instead", - hint, + help_str, + format!("{}{}", reborrow_str, snip), app, ); } diff --git a/tests/ui/redundant_slicing.rs b/tests/ui/redundant_slicing.rs index 922b8b4ce57..554b6ba36ae 100644 --- a/tests/ui/redundant_slicing.rs +++ b/tests/ui/redundant_slicing.rs @@ -2,10 +2,31 @@ #![warn(clippy::redundant_slicing)] fn main() { - let x: &[u32] = &[0]; - let err = &x[..]; + let slice: &[u32] = &[0]; + let _ = &slice[..]; let v = vec![0]; - let ok = &v[..]; - let err = &(&v[..])[..]; + let _ = &v[..]; // Changes the type + let _ = &(&v[..])[..]; // Outer borrow is redundant + + static S: &[u8] = &[0, 1, 2]; + let err = &mut &S[..]; // Should reborrow instead of slice + + let mut vec = vec![0]; + let mut_slice = &mut *vec; + let _ = &mut mut_slice[..]; // Should reborrow instead of slice + + macro_rules! m { + ($e:expr) => { + $e + }; + } + let _ = &m!(slice)[..]; + + macro_rules! m2 { + ($e:expr) => { + &$e[..] + }; + } + let _ = m2!(slice); // Don't lint in a macro } diff --git a/tests/ui/redundant_slicing.stderr b/tests/ui/redundant_slicing.stderr index 9efd6484ad0..bbd10eafbbe 100644 --- a/tests/ui/redundant_slicing.stderr +++ b/tests/ui/redundant_slicing.stderr @@ -1,16 +1,34 @@ error: redundant slicing of the whole range - --> $DIR/redundant_slicing.rs:6:15 + --> $DIR/redundant_slicing.rs:6:13 | -LL | let err = &x[..]; - | ^^^^^^ help: use the original slice instead: `x` +LL | let _ = &slice[..]; + | ^^^^^^^^^^ help: use the original value instead: `slice` | = note: `-D clippy::redundant-slicing` implied by `-D warnings` error: redundant slicing of the whole range - --> $DIR/redundant_slicing.rs:10:15 + --> $DIR/redundant_slicing.rs:10:13 | -LL | let err = &(&v[..])[..]; - | ^^^^^^^^^^^^^ help: use the original slice instead: `(&v[..])` +LL | let _ = &(&v[..])[..]; // Outer borrow is redundant + | ^^^^^^^^^^^^^ help: use the original value instead: `(&v[..])` -error: aborting due to 2 previous errors +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:13:20 + | +LL | let err = &mut &S[..]; // Should reborrow instead of slice + | ^^^^^^ help: reborrow the original value instead: `&*S` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:17:13 + | +LL | let _ = &mut mut_slice[..]; // Should reborrow instead of slice + | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:24:13 + | +LL | let _ = &m!(slice)[..]; + | ^^^^^^^^^^^^^^ help: use the original value instead: `slice` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From caa49c85d6c1d45678d1065c689cc4d168e18dfb Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 18 Mar 2021 16:03:56 +0900 Subject: Move absurd_extreme_comparisons to its own module --- clippy_lints/src/absurd_extreme_comparisons.rs | 173 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 9 +- clippy_lints/src/types/mod.rs | 178 +------------------------ 3 files changed, 183 insertions(+), 177 deletions(-) create mode 100644 clippy_lints/src/absurd_extreme_comparisons.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/absurd_extreme_comparisons.rs new file mode 100644 index 00000000000..33c720c666e --- /dev/null +++ b/clippy_lints/src/absurd_extreme_comparisons.rs @@ -0,0 +1,173 @@ +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 crate::consts::{constant, Constant}; + +use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_isize_or_usize; +use clippy_utils::{clip, int_bits, unsext}; + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons where one side of the relation is + /// either the minimum or maximum value for its type and warns if it involves a + /// case that is always true or always false. Only integer and boolean types are + /// checked. + /// + /// **Why is this bad?** An expression like `min <= x` may misleadingly imply + /// that it is possible for `x` to be less than the minimum. Expressions like + /// `max < x` are probably mistakes. + /// + /// **Known problems:** For `usize` the size of the current compile target will + /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such + /// a comparison to detect target pointer width will trigger this lint. One can + /// use `mem::sizeof` and compare its value or conditional compilation + /// attributes + /// like `#[cfg(target_pointer_width = "64")] ..` instead. + /// + /// **Example:** + /// + /// ```rust + /// let vec: Vec = Vec::new(); + /// if vec.len() <= 0 {} + /// if 100 > i32::MAX {} + /// ``` + pub ABSURD_EXTREME_COMPARISONS, + correctness, + "a comparison with a maximum or minimum value that is always true or false" +} + +declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); + +impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { + if !expr.span.from_expansion() { + let msg = "this comparison involving the minimum or maximum element for this \ + type contains a case that is always true or always false"; + + let conclusion = match result { + AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(), + AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(), + AbsurdComparisonResult::InequalityImpossible => format!( + "the case where the two sides are not equal never occurs, consider using `{} == {}` \ + instead", + snippet(cx, lhs.span, "lhs"), + snippet(cx, rhs.span, "rhs") + ), + }; + + let help = format!( + "because `{}` is the {} value for this type, {}", + snippet(cx, culprit.expr.span, "x"), + match culprit.which { + ExtremeType::Minimum => "minimum", + ExtremeType::Maximum => "maximum", + }, + conclusion + ); + + span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); + } + } + } + } +} + +enum ExtremeType { + Minimum, + Maximum, +} + +struct ExtremeExpr<'a> { + which: ExtremeType, + expr: &'a Expr<'a>, +} + +enum AbsurdComparisonResult { + AlwaysFalse, + AlwaysTrue, + InequalityImpossible, +} + +fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + let precast_ty = cx.typeck_results().expr_ty(cast_exp); + let cast_ty = cx.typeck_results().expr_ty(expr); + + return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty); + } + + false +} + +fn detect_absurd_comparison<'tcx>( + cx: &LateContext<'tcx>, + op: BinOpKind, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, +) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { + use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; + use ExtremeType::{Maximum, Minimum}; + // absurd comparison only makes sense on primitive types + // primitive types don't implement comparison operators with each other + if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) { + return None; + } + + // comparisons between fix sized types and target sized types are considered unanalyzable + if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) { + return None; + } + + let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?; + + let lx = detect_extreme_expr(cx, normalized_lhs); + let rx = detect_extreme_expr(cx, normalized_rhs); + + Some(match rel { + Rel::Lt => { + match (lx, rx) { + (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x + (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min + _ => return None, + } + }, + Rel::Le => { + match (lx, rx) { + (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x + (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x + (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min + (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max + _ => return None, + } + }, + Rel::Ne | Rel::Eq => return None, + }) +} + +fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + let ty = cx.typeck_results().expr_ty(expr); + + let cv = constant(cx, cx.typeck_results(), expr)?.0; + + let which = match (ty.kind(), cv) { + (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, + (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => { + ExtremeType::Minimum + }, + + (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum, + (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => { + ExtremeType::Maximum + }, + (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum, + + _ => return None, + }; + Some(ExtremeExpr { which, expr }) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3a9236d8735..7b261121f51 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -164,6 +164,7 @@ mod consts; mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` +mod absurd_extreme_comparisons; mod approx_const; mod arithmetic; mod as_conversions; @@ -558,6 +559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::PRODUCE_ICE, #[cfg(feature = "internal-lints")] &utils::internal_lints::UNNECESSARY_SYMBOL_STR, + &absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -955,7 +957,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &transmute::WRONG_TRANSMUTE, &transmuting_null::TRANSMUTING_NULL, &try_err::TRY_ERR, - &types::ABSURD_EXTREME_COMPARISONS, &types::BORROWED_BOX, &types::BOX_VEC, &types::IMPLICIT_HASHER, @@ -1112,7 +1113,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); store.register_late_pass(|| box empty_enum::EmptyEnum); - store.register_late_pass(|| box types::AbsurdExtremeComparisons); + store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons); store.register_late_pass(|| box types::InvalidUpcastComparisons); store.register_late_pass(|| box regex::Regex::default()); store.register_late_pass(|| box copies::CopyAndPaste); @@ -1442,6 +1443,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ + LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), LintId::of(&approx_const::APPROX_CONSTANT), LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assign_ops::ASSIGN_OP_PATTERN), @@ -1701,7 +1703,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(&try_err::TRY_ERR), - LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::BORROWED_BOX), LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), @@ -1941,6 +1942,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(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), LintId::of(&approx_const::APPROX_CONSTANT), LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), @@ -2002,7 +2004,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), - LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index c73c1c9d92d..0c50ed8348e 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -13,16 +13,16 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; -use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_help, span_lint_and_then}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item}; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::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, - ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, + Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, ImplItemKind, Item, + ItemKind, Local, MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -37,7 +37,7 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use clippy_utils::paths; -use clippy_utils::{clip, comparisons, differing_macro_contexts, int_bits, match_path, sext, unsext}; +use clippy_utils::{comparisons, differing_macro_contexts, match_path, sext}; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. @@ -552,174 +552,6 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { } } -declare_clippy_lint! { - /// **What it does:** Checks for comparisons where one side of the relation is - /// either the minimum or maximum value for its type and warns if it involves a - /// case that is always true or always false. Only integer and boolean types are - /// checked. - /// - /// **Why is this bad?** An expression like `min <= x` may misleadingly imply - /// that it is possible for `x` to be less than the minimum. Expressions like - /// `max < x` are probably mistakes. - /// - /// **Known problems:** For `usize` the size of the current compile target will - /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such - /// a comparison to detect target pointer width will trigger this lint. One can - /// use `mem::sizeof` and compare its value or conditional compilation - /// attributes - /// like `#[cfg(target_pointer_width = "64")] ..` instead. - /// - /// **Example:** - /// - /// ```rust - /// let vec: Vec = Vec::new(); - /// if vec.len() <= 0 {} - /// if 100 > i32::MAX {} - /// ``` - pub ABSURD_EXTREME_COMPARISONS, - correctness, - "a comparison with a maximum or minimum value that is always true or false" -} - -declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); - -enum ExtremeType { - Minimum, - Maximum, -} - -struct ExtremeExpr<'a> { - which: ExtremeType, - expr: &'a Expr<'a>, -} - -enum AbsurdComparisonResult { - AlwaysFalse, - AlwaysTrue, - InequalityImpossible, -} - -fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { - let precast_ty = cx.typeck_results().expr_ty(cast_exp); - let cast_ty = cx.typeck_results().expr_ty(expr); - - return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty); - } - - false -} - -fn detect_absurd_comparison<'tcx>( - cx: &LateContext<'tcx>, - op: BinOpKind, - lhs: &'tcx Expr<'_>, - rhs: &'tcx Expr<'_>, -) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { - use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; - use crate::types::ExtremeType::{Maximum, Minimum}; - use clippy_utils::comparisons::{normalize_comparison, Rel}; - - // absurd comparison only makes sense on primitive types - // primitive types don't implement comparison operators with each other - if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) { - return None; - } - - // comparisons between fix sized types and target sized types are considered unanalyzable - if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) { - return None; - } - - let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?; - - let lx = detect_extreme_expr(cx, normalized_lhs); - let rx = detect_extreme_expr(cx, normalized_rhs); - - Some(match rel { - Rel::Lt => { - match (lx, rx) { - (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x - (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min - _ => return None, - } - }, - Rel::Le => { - match (lx, rx) { - (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x - (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x - (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min - (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max - _ => return None, - } - }, - Rel::Ne | Rel::Eq => return None, - }) -} - -fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { - use crate::types::ExtremeType::{Maximum, Minimum}; - - let ty = cx.typeck_results().expr_ty(expr); - - let cv = constant(cx, cx.typeck_results(), expr)?.0; - - 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) => { - 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) => { - Maximum - }, - (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => Maximum, - - _ => return None, - }; - Some(ExtremeExpr { which, expr }) -} - -impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; - use crate::types::ExtremeType::{Maximum, Minimum}; - - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { - if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { - if !expr.span.from_expansion() { - let msg = "this comparison involving the minimum or maximum element for this \ - type contains a case that is always true or always false"; - - let conclusion = match result { - AlwaysFalse => "this comparison is always false".to_owned(), - AlwaysTrue => "this comparison is always true".to_owned(), - InequalityImpossible => format!( - "the case where the two sides are not equal never occurs, consider using `{} == {}` \ - instead", - snippet(cx, lhs.span, "lhs"), - snippet(cx, rhs.span, "rhs") - ), - }; - - let help = format!( - "because `{}` is the {} value for this type, {}", - snippet(cx, culprit.expr.span, "x"), - match culprit.which { - Minimum => "minimum", - Maximum => "maximum", - }, - conclusion - ); - - span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); - } - } - } - } -} - declare_clippy_lint! { /// **What it does:** Checks for comparisons where the relation is always either /// true or false, but where one side has been upcast so that the comparison is -- cgit 1.4.1-3-g733a5 From f231b59b9e3fc1ea074a269b006421295d5402d0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 18 Mar 2021 16:15:19 +0900 Subject: Move invalid_upcast_comparisons to its own module --- clippy_lints/src/invalid_upcast_comparisons.rs | 221 +++++++++++++++++++++++++ clippy_lints/src/lib.rs | 7 +- clippy_lints/src/types/mod.rs | 215 +----------------------- 3 files changed, 227 insertions(+), 216 deletions(-) create mode 100644 clippy_lints/src/invalid_upcast_comparisons.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs new file mode 100644 index 00000000000..d183fc41f31 --- /dev/null +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -0,0 +1,221 @@ +use std::cmp::Ordering; + +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, IntTy, UintTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; +use rustc_target::abi::LayoutOf; + +use crate::consts::{constant, Constant}; + +use clippy_utils::comparisons::Rel; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet; +use clippy_utils::{comparisons, sext}; + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons where the relation is always either + /// true or false, but where one side has been upcast so that the comparison is + /// necessary. Only integer types are checked. + /// + /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300` + /// will mistakenly imply that it is possible for `x` to be outside the range of + /// `u8`. + /// + /// **Known problems:** + /// https://github.com/rust-lang/rust-clippy/issues/886 + /// + /// **Example:** + /// ```rust + /// let x: u8 = 1; + /// (x as u32) > 300; + /// ``` + pub INVALID_UPCAST_COMPARISONS, + pedantic, + "a comparison involving an upcast which is always true or false" +} + +declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); + +#[derive(Copy, Clone, Debug, Eq)] +enum FullInt { + S(i128), + U(u128), +} + +impl FullInt { + #[allow(clippy::cast_sign_loss)] + #[must_use] + fn cmp_s_u(s: i128, u: u128) -> Ordering { + if s < 0 { + Ordering::Less + } else if u > (i128::MAX as u128) { + Ordering::Greater + } else { + (s as u128).cmp(&u) + } + } +} + +impl PartialEq for FullInt { + #[must_use] + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal + } +} + +impl PartialOrd for FullInt { + #[must_use] + fn partial_cmp(&self, other: &Self) -> Option { + Some(match (self, other) { + (&Self::S(s), &Self::S(o)) => s.cmp(&o), + (&Self::U(s), &Self::U(o)) => s.cmp(&o), + (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o), + (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(), + }) + } +} + +impl Ord for FullInt { + #[must_use] + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other) + .expect("`partial_cmp` for FullInt can never return `None`") + } +} + +fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { + if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); + let cast_ty = cx.typeck_results().expr_ty(expr); + // if it's a cast from i32 to u32 wrapping will invalidate all these checks + if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { + return None; + } + 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::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)), + 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::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)), + UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)), + }), + _ => None, + } + } else { + None + } +} + +fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { + let val = constant(cx, cx.typeck_results(), expr)?.0; + if let Constant::Int(const_int) = val { + match *cx.typeck_results().expr_ty(expr).kind() { + ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Uint(_) => Some(FullInt::U(const_int)), + _ => None, + } + } else { + None + } +} + +fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { + if let ExprKind::Cast(ref cast_val, _) = expr.kind { + span_lint( + cx, + INVALID_UPCAST_COMPARISONS, + span, + &format!( + "because of the numeric bounds on `{}` prior to casting, this expression is always {}", + snippet(cx, cast_val.span, "the expression"), + if always { "true" } else { "false" }, + ), + ); + } +} + +fn upcast_comparison_bounds_err<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + rel: comparisons::Rel, + lhs_bounds: Option<(FullInt, FullInt)>, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, + invert: bool, +) { + if let Some((lb, ub)) = lhs_bounds { + if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) { + if rel == Rel::Eq || rel == Rel::Ne { + if norm_rhs_val < lb || norm_rhs_val > ub { + err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); + } + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val < lb + } else { + ub < norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val <= lb + } else { + ub <= norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, true) + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val >= ub + } else { + lb >= norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val > ub + } else { + lb > norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, false) + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); + let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { + val + } else { + return; + }; + + let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); + let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); + + upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); + upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7b261121f51..c16be6da11a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -242,6 +242,7 @@ mod inherent_to_string; mod inline_fn_without_body; mod int_plus_one; mod integer_division; +mod invalid_upcast_comparisons; mod items_after_statements; mod large_const_arrays; mod large_enum_variant; @@ -697,6 +698,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &inline_fn_without_body::INLINE_FN_WITHOUT_BODY, &int_plus_one::INT_PLUS_ONE, &integer_division::INTEGER_DIVISION, + &invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, &items_after_statements::ITEMS_AFTER_STATEMENTS, &large_const_arrays::LARGE_CONST_ARRAYS, &large_enum_variant::LARGE_ENUM_VARIANT, @@ -960,7 +962,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::BORROWED_BOX, &types::BOX_VEC, &types::IMPLICIT_HASHER, - &types::INVALID_UPCAST_COMPARISONS, &types::LINKEDLIST, &types::OPTION_OPTION, &types::RC_BUFFER, @@ -1114,7 +1115,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box drop_forget_ref::DropForgetRef); store.register_late_pass(|| box empty_enum::EmptyEnum); store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons); - store.register_late_pass(|| box types::InvalidUpcastComparisons); + store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons); store.register_late_pass(|| box regex::Regex::default()); store.register_late_pass(|| box copies::CopyAndPaste); store.register_late_pass(|| box copy_iterator::CopyIterator); @@ -1374,6 +1375,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&if_not_else::IF_NOT_ELSE), LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(&invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), LintId::of(&let_underscore::LET_UNDERSCORE_DROP), @@ -1413,7 +1415,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(&types::IMPLICIT_HASHER), - LintId::of(&types::INVALID_UPCAST_COMPARISONS), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), LintId::of(&unicode::NON_ASCII_LITERAL), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 0c50ed8348e..785f2c511d0 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -10,7 +10,6 @@ mod utils; mod vec_box; use std::borrow::Cow; -use std::cmp::Ordering; use std::collections::BTreeMap; use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; @@ -27,17 +26,15 @@ 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, IntTy, Ty, TyS, TypeckResults, UintTy}; +use rustc_middle::ty::{Ty, TyS, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; -use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::hir_ty_to_ty; -use crate::consts::{constant, Constant}; use clippy_utils::paths; -use clippy_utils::{comparisons, differing_macro_contexts, match_path, sext}; +use clippy_utils::{differing_macro_contexts, match_path}; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. @@ -552,214 +549,6 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { } } -declare_clippy_lint! { - /// **What it does:** Checks for comparisons where the relation is always either - /// true or false, but where one side has been upcast so that the comparison is - /// necessary. Only integer types are checked. - /// - /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300` - /// will mistakenly imply that it is possible for `x` to be outside the range of - /// `u8`. - /// - /// **Known problems:** - /// https://github.com/rust-lang/rust-clippy/issues/886 - /// - /// **Example:** - /// ```rust - /// let x: u8 = 1; - /// (x as u32) > 300; - /// ``` - pub INVALID_UPCAST_COMPARISONS, - pedantic, - "a comparison involving an upcast which is always true or false" -} - -declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); - -#[derive(Copy, Clone, Debug, Eq)] -enum FullInt { - S(i128), - U(u128), -} - -impl FullInt { - #[allow(clippy::cast_sign_loss)] - #[must_use] - fn cmp_s_u(s: i128, u: u128) -> Ordering { - if s < 0 { - Ordering::Less - } else if u > (i128::MAX as u128) { - Ordering::Greater - } else { - (s as u128).cmp(&u) - } - } -} - -impl PartialEq for FullInt { - #[must_use] - fn eq(&self, other: &Self) -> bool { - self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal - } -} - -impl PartialOrd for FullInt { - #[must_use] - fn partial_cmp(&self, other: &Self) -> Option { - Some(match (self, other) { - (&Self::S(s), &Self::S(o)) => s.cmp(&o), - (&Self::U(s), &Self::U(o)) => s.cmp(&o), - (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o), - (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(), - }) - } -} - -impl Ord for FullInt { - #[must_use] - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other) - .expect("`partial_cmp` for FullInt can never return `None`") - } -} - -fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { - let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); - let cast_ty = cx.typeck_results().expr_ty(expr); - // if it's a cast from i32 to u32 wrapping will invalidate all these checks - if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { - return None; - } - 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::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)), - 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::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)), - UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)), - }), - _ => None, - } - } else { - None - } -} - -fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - let val = constant(cx, cx.typeck_results(), expr)?.0; - if let Constant::Int(const_int) = val { - match *cx.typeck_results().expr_ty(expr).kind() { - ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), - ty::Uint(_) => Some(FullInt::U(const_int)), - _ => None, - } - } else { - None - } -} - -fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { - if let ExprKind::Cast(ref cast_val, _) = expr.kind { - span_lint( - cx, - INVALID_UPCAST_COMPARISONS, - span, - &format!( - "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - snippet(cx, cast_val.span, "the expression"), - if always { "true" } else { "false" }, - ), - ); - } -} - -fn upcast_comparison_bounds_err<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - rel: comparisons::Rel, - lhs_bounds: Option<(FullInt, FullInt)>, - lhs: &'tcx Expr<'_>, - rhs: &'tcx Expr<'_>, - invert: bool, -) { - use clippy_utils::comparisons::Rel; - - if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true) - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false) - } - } - } -} - -impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { - let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); - let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { - val - } else { - return; - }; - - let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); - let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); - - upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); - upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); - } - } -} - declare_clippy_lint! { /// **What it does:** Checks for public `impl` or `fn` missing generalization /// over different hashers and implicitly defaulting to the default hashing -- cgit 1.4.1-3-g733a5 From dad39b6613645aefd6671f1378cafa1c8f2d5f65 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 18 Mar 2021 16:34:22 +0900 Subject: Move implicit_hasher to its own module --- clippy_lints/src/implicit_hasher.rs | 377 +++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 7 +- clippy_lints/src/types/mod.rs | 380 +----------------------------------- 3 files changed, 387 insertions(+), 377 deletions(-) create mode 100644 clippy_lints/src/implicit_hasher.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs new file mode 100644 index 00000000000..0b748b4d72d --- /dev/null +++ b/clippy_lints/src/implicit_hasher.rs @@ -0,0 +1,377 @@ +#![allow(rustc::default_hash_types)] + +use std::borrow::Cow; +use std::collections::BTreeMap; + +use rustc_errors::DiagnosticBuilder; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{Ty, TyS, TypeckResults}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::paths; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, match_path}; + +declare_clippy_lint! { + /// **What it does:** Checks for public `impl` or `fn` missing generalization + /// over different hashers and implicitly defaulting to the default hashing + /// algorithm (`SipHash`). + /// + /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be + /// used with them. + /// + /// **Known problems:** Suggestions for replacing constructors can contain + /// false-positives. Also applying suggestions can require modification of other + /// pieces of code, possibly including external crates. + /// + /// **Example:** + /// ```rust + /// # use std::collections::HashMap; + /// # use std::hash::{Hash, BuildHasher}; + /// # trait Serialize {}; + /// impl Serialize for HashMap { } + /// + /// pub fn foo(map: &mut HashMap) { } + /// ``` + /// could be rewritten as + /// ```rust + /// # use std::collections::HashMap; + /// # use std::hash::{Hash, BuildHasher}; + /// # trait Serialize {}; + /// impl Serialize for HashMap { } + /// + /// pub fn foo(map: &mut HashMap) { } + /// ``` + pub IMPLICIT_HASHER, + pedantic, + "missing generalization over different hashers" +} + +declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { + #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + use rustc_span::BytePos; + + fn suggestion<'tcx>( + cx: &LateContext<'tcx>, + diag: &mut DiagnosticBuilder<'_>, + generics_span: Span, + generics_suggestion_span: Span, + target: &ImplicitHasherType<'_>, + vis: ImplicitHasherConstructorVisitor<'_, '_, '_>, + ) { + let generics_snip = snippet(cx, generics_span, ""); + // trim `<` `>` + let generics_snip = if generics_snip.is_empty() { + "" + } else { + &generics_snip[1..generics_snip.len() - 1] + }; + + multispan_sugg( + diag, + "consider adding a type parameter", + vec![ + ( + generics_suggestion_span, + format!( + "<{}{}S: ::std::hash::BuildHasher{}>", + generics_snip, + if generics_snip.is_empty() { "" } else { ", " }, + if vis.suggestions.is_empty() { + "" + } else { + // request users to add `Default` bound so that generic constructors can be used + " + Default" + }, + ), + ), + ( + target.span(), + format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + ), + ], + ); + + if !vis.suggestions.is_empty() { + multispan_sugg(diag, "...and use generic constructor", vis.suggestions); + } + } + + if !cx.access_levels.is_exported(item.hir_id()) { + return; + } + + match item.kind { + ItemKind::Impl(ref impl_) => { + let mut vis = ImplicitHasherTypeVisitor::new(cx); + vis.visit_ty(impl_.self_ty); + + for target in &vis.found { + if differing_macro_contexts(item.span, target.span()) { + return; + } + + let generics_suggestion_span = impl_.generics.span.substitute_dummy({ + let pos = snippet_opt(cx, item.span.until(target.span())) + .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4))); + if let Some(pos) = pos { + Span::new(pos, pos, item.span.data().ctxt) + } else { + return; + } + }); + + let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); + for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) { + ctr_vis.visit_impl_item(item); + } + + span_lint_and_then( + cx, + IMPLICIT_HASHER, + target.span(), + &format!( + "impl for `{}` should be generalized over different hashers", + target.type_name() + ), + move |diag| { + suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis); + }, + ); + } + }, + ItemKind::Fn(ref sig, ref generics, body_id) => { + let body = cx.tcx.hir().body(body_id); + + for ty in sig.decl.inputs { + let mut vis = ImplicitHasherTypeVisitor::new(cx); + vis.visit_ty(ty); + + for target in &vis.found { + if in_external_macro(cx.sess(), generics.span) { + continue; + } + let generics_suggestion_span = generics.span.substitute_dummy({ + let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span)) + .and_then(|snip| { + let i = snip.find("fn")?; + Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) + }) + .expect("failed to create span for type parameters"); + Span::new(pos, pos, item.span.data().ctxt) + }); + + let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); + ctr_vis.visit_body(body); + + span_lint_and_then( + cx, + IMPLICIT_HASHER, + target.span(), + &format!( + "parameter of type `{}` should be generalized over different hashers", + target.type_name() + ), + move |diag| { + suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis); + }, + ); + } + } + }, + _ => {}, + } + } +} + +enum ImplicitHasherType<'tcx> { + HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>), + HashSet(Span, Ty<'tcx>, Cow<'static, str>), +} + +impl<'tcx> ImplicitHasherType<'tcx> { + /// Checks that `ty` is a target type without a `BuildHasher`. + fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option { + if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind { + let params: Vec<_> = path + .segments + .last() + .as_ref()? + .args + .as_ref()? + .args + .iter() + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + .collect(); + let params_len = params.len(); + + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + + if is_type_diagnostic_item(cx, ty, sym::hashmap_type) && params_len == 2 { + Some(ImplicitHasherType::HashMap( + hir_ty.span, + ty, + snippet(cx, params[0].span, "K"), + snippet(cx, params[1].span, "V"), + )) + } else if is_type_diagnostic_item(cx, ty, sym::hashset_type) && params_len == 1 { + Some(ImplicitHasherType::HashSet( + hir_ty.span, + ty, + snippet(cx, params[0].span, "T"), + )) + } else { + None + } + } else { + None + } + } + + fn type_name(&self) -> &'static str { + match *self { + ImplicitHasherType::HashMap(..) => "HashMap", + ImplicitHasherType::HashSet(..) => "HashSet", + } + } + + fn type_arguments(&self) -> String { + match *self { + ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), + ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), + } + } + + fn ty(&self) -> Ty<'tcx> { + match *self { + ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty, + } + } + + fn span(&self) -> Span { + match *self { + ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span, + } + } +} + +struct ImplicitHasherTypeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: Vec>, +} + +impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { cx, found: vec![] } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { + if let Some(target) = ImplicitHasherType::new(self.cx, t) { + self.found.push(target); + } + + walk_ty(self, t); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Looks for default-hasher-dependent constructors like `HashMap::new`. +struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, + target: &'b ImplicitHasherType<'tcx>, + suggestions: BTreeMap, +} + +impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self { + Self { + cx, + maybe_typeck_results: cx.maybe_typeck_results(), + target, + suggestions: BTreeMap::new(), + } + } +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + type Map = Map<'tcx>; + + fn visit_body(&mut self, body: &'tcx Body<'_>) { + let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id())); + walk_body(self, body); + self.maybe_typeck_results = old_maybe_typeck_results; + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref fun, ref args) = e.kind; + 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 !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { + return; + } + + if match_path(ty_path, &paths::HASHMAP) { + if method.ident.name == sym::new { + self.suggestions + .insert(e.span, "HashMap::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashMap::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } else if match_path(ty_path, &paths::HASHSET) { + if method.ident.name == sym::new { + self.suggestions + .insert(e.span, "HashSet::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashSet::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } + } + } + + walk_expr(self, e); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c16be6da11a..05165ec9d66 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -232,6 +232,7 @@ mod if_let_mutex; mod if_let_some_result; mod if_not_else; mod if_then_some_else_none; +mod implicit_hasher; mod implicit_return; mod implicit_saturating_sub; mod inconsistent_struct_constructor; @@ -685,6 +686,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &if_let_some_result::IF_LET_SOME_RESULT, &if_not_else::IF_NOT_ELSE, &if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, + &implicit_hasher::IMPLICIT_HASHER, &implicit_return::IMPLICIT_RETURN, &implicit_saturating_sub::IMPLICIT_SATURATING_SUB, &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, @@ -961,7 +963,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &try_err::TRY_ERR, &types::BORROWED_BOX, &types::BOX_VEC, - &types::IMPLICIT_HASHER, &types::LINKEDLIST, &types::OPTION_OPTION, &types::RC_BUFFER, @@ -1159,7 +1160,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); store.register_late_pass(|| box useless_conversion::UselessConversion::default()); - store.register_late_pass(|| box types::ImplicitHasher); + store.register_late_pass(|| box implicit_hasher::ImplicitHasher); store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); @@ -1373,6 +1374,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::MUST_USE_CANDIDATE), LintId::of(&functions::TOO_MANY_LINES), LintId::of(&if_not_else::IF_NOT_ELSE), + LintId::of(&implicit_hasher::IMPLICIT_HASHER), LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), @@ -1414,7 +1416,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(&types::IMPLICIT_HASHER), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), LintId::of(&unicode::NON_ASCII_LITERAL), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 785f2c511d0..d5f2b3d013e 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -1,5 +1,3 @@ -#![allow(rustc::default_hash_types)] - mod borrowed_box; mod box_vec; mod linked_list; @@ -9,32 +7,18 @@ mod redundant_allocation; mod utils; mod vec_box; -use std::borrow::Cow; -use std::collections::BTreeMap; - -use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; -use rustc_errors::DiagnosticBuilder; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, ImplItemKind, Item, - ItemKind, Local, MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, + Body, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, + MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, }; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{Ty, TyS, TypeckResults}; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; -use rustc_typeck::hir_ty_to_ty; - -use clippy_utils::paths; -use clippy_utils::{differing_macro_contexts, match_path}; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. @@ -548,355 +532,3 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { NestedVisitorMap::None } } - -declare_clippy_lint! { - /// **What it does:** Checks for public `impl` or `fn` missing generalization - /// over different hashers and implicitly defaulting to the default hashing - /// algorithm (`SipHash`). - /// - /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be - /// used with them. - /// - /// **Known problems:** Suggestions for replacing constructors can contain - /// false-positives. Also applying suggestions can require modification of other - /// pieces of code, possibly including external crates. - /// - /// **Example:** - /// ```rust - /// # use std::collections::HashMap; - /// # use std::hash::{Hash, BuildHasher}; - /// # trait Serialize {}; - /// impl Serialize for HashMap { } - /// - /// pub fn foo(map: &mut HashMap) { } - /// ``` - /// could be rewritten as - /// ```rust - /// # use std::collections::HashMap; - /// # use std::hash::{Hash, BuildHasher}; - /// # trait Serialize {}; - /// impl Serialize for HashMap { } - /// - /// pub fn foo(map: &mut HashMap) { } - /// ``` - pub IMPLICIT_HASHER, - pedantic, - "missing generalization over different hashers" -} - -declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); - -impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { - #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - use rustc_span::BytePos; - - fn suggestion<'tcx>( - cx: &LateContext<'tcx>, - diag: &mut DiagnosticBuilder<'_>, - generics_span: Span, - generics_suggestion_span: Span, - target: &ImplicitHasherType<'_>, - vis: ImplicitHasherConstructorVisitor<'_, '_, '_>, - ) { - let generics_snip = snippet(cx, generics_span, ""); - // trim `<` `>` - let generics_snip = if generics_snip.is_empty() { - "" - } else { - &generics_snip[1..generics_snip.len() - 1] - }; - - multispan_sugg( - diag, - "consider adding a type parameter", - vec![ - ( - generics_suggestion_span, - format!( - "<{}{}S: ::std::hash::BuildHasher{}>", - generics_snip, - if generics_snip.is_empty() { "" } else { ", " }, - if vis.suggestions.is_empty() { - "" - } else { - // request users to add `Default` bound so that generic constructors can be used - " + Default" - }, - ), - ), - ( - target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), - ), - ], - ); - - if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor", vis.suggestions); - } - } - - if !cx.access_levels.is_exported(item.hir_id()) { - return; - } - - match item.kind { - ItemKind::Impl(ref impl_) => { - let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_ty(impl_.self_ty); - - for target in &vis.found { - if differing_macro_contexts(item.span, target.span()) { - return; - } - - let generics_suggestion_span = impl_.generics.span.substitute_dummy({ - let pos = snippet_opt(cx, item.span.until(target.span())) - .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4))); - if let Some(pos) = pos { - Span::new(pos, pos, item.span.data().ctxt) - } else { - return; - } - }); - - let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); - for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) { - ctr_vis.visit_impl_item(item); - } - - span_lint_and_then( - cx, - IMPLICIT_HASHER, - target.span(), - &format!( - "impl for `{}` should be generalized over different hashers", - target.type_name() - ), - move |diag| { - suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis); - }, - ); - } - }, - ItemKind::Fn(ref sig, ref generics, body_id) => { - let body = cx.tcx.hir().body(body_id); - - for ty in sig.decl.inputs { - let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_ty(ty); - - for target in &vis.found { - if in_external_macro(cx.sess(), generics.span) { - continue; - } - let generics_suggestion_span = generics.span.substitute_dummy({ - let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span)) - .and_then(|snip| { - let i = snip.find("fn")?; - Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) - }) - .expect("failed to create span for type parameters"); - Span::new(pos, pos, item.span.data().ctxt) - }); - - let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); - ctr_vis.visit_body(body); - - span_lint_and_then( - cx, - IMPLICIT_HASHER, - target.span(), - &format!( - "parameter of type `{}` should be generalized over different hashers", - target.type_name() - ), - move |diag| { - suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis); - }, - ); - } - } - }, - _ => {}, - } - } -} - -enum ImplicitHasherType<'tcx> { - HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>), - HashSet(Span, Ty<'tcx>, Cow<'static, str>), -} - -impl<'tcx> ImplicitHasherType<'tcx> { - /// Checks that `ty` is a target type without a `BuildHasher`. - fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option { - if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind { - let params: Vec<_> = path - .segments - .last() - .as_ref()? - .args - .as_ref()? - .args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }) - .collect(); - let params_len = params.len(); - - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - - if is_type_diagnostic_item(cx, ty, sym::hashmap_type) && params_len == 2 { - Some(ImplicitHasherType::HashMap( - hir_ty.span, - ty, - snippet(cx, params[0].span, "K"), - snippet(cx, params[1].span, "V"), - )) - } else if is_type_diagnostic_item(cx, ty, sym::hashset_type) && params_len == 1 { - Some(ImplicitHasherType::HashSet( - hir_ty.span, - ty, - snippet(cx, params[0].span, "T"), - )) - } else { - None - } - } else { - None - } - } - - fn type_name(&self) -> &'static str { - match *self { - ImplicitHasherType::HashMap(..) => "HashMap", - ImplicitHasherType::HashSet(..) => "HashSet", - } - } - - fn type_arguments(&self) -> String { - match *self { - ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), - ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), - } - } - - fn ty(&self) -> Ty<'tcx> { - match *self { - ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty, - } - } - - fn span(&self) -> Span { - match *self { - ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span, - } - } -} - -struct ImplicitHasherTypeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: Vec>, -} - -impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { - Self { cx, found: vec![] } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { - if let Some(target) = ImplicitHasherType::new(self.cx, t) { - self.found.push(target); - } - - walk_ty(self, t); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - -/// Looks for default-hasher-dependent constructors like `HashMap::new`. -struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { - cx: &'a LateContext<'tcx>, - maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, - target: &'b ImplicitHasherType<'tcx>, - suggestions: BTreeMap, -} - -impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self { - Self { - cx, - maybe_typeck_results: cx.maybe_typeck_results(), - target, - suggestions: BTreeMap::new(), - } - } -} - -impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { - type Map = Map<'tcx>; - - fn visit_body(&mut self, body: &'tcx Body<'_>) { - let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id())); - walk_body(self, body); - self.maybe_typeck_results = old_maybe_typeck_results; - } - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(ref fun, ref args) = e.kind; - 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 !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { - return; - } - - if match_path(ty_path, &paths::HASHMAP) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashMap::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } - } else if match_path(ty_path, &paths::HASHSET) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashSet::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } - } - } - } - - walk_expr(self, e); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) - } -} -- cgit 1.4.1-3-g733a5 From 494bc8a30cf154eede2f22178c9a7ebc404302e7 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 19 Mar 2021 18:14:48 +0900 Subject: Fix FN that types lints don't work with const or static --- clippy_lints/src/types/mod.rs | 8 ++++++++ tests/ui/dlist.rs | 3 +++ tests/ui/dlist.stderr | 32 ++++++++++++++++++++++++-------- tests/ui/option_option.rs | 3 +++ tests/ui/option_option.stderr | 38 +++++++++++++++++++++++++------------- tests/ui/vec_box_sized.fixed | 2 ++ tests/ui/vec_box_sized.rs | 2 ++ tests/ui/vec_box_sized.stderr | 26 +++++++++++++++++++------- 8 files changed, 86 insertions(+), 28 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index d5f2b3d013e..1df964db38f 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -249,6 +249,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl); } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + match item.kind { + ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_ty(cx, ty, false), + // functions, enums, structs, impls and traits are covered + _ => (), + } + } + fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { self.check_ty(cx, &field.ty, false); } diff --git a/tests/ui/dlist.rs b/tests/ui/dlist.rs index 2940d2d2901..2c3b25cd45e 100644 --- a/tests/ui/dlist.rs +++ b/tests/ui/dlist.rs @@ -5,6 +5,9 @@ extern crate alloc; use alloc::collections::linked_list::LinkedList; +const C: LinkedList = LinkedList::new(); +static S: LinkedList = LinkedList::new(); + trait Foo { type Baz = LinkedList; fn foo(_: LinkedList); diff --git a/tests/ui/dlist.stderr b/tests/ui/dlist.stderr index 234db33ba12..425407dc334 100644 --- a/tests/ui/dlist.stderr +++ b/tests/ui/dlist.stderr @@ -1,14 +1,30 @@ error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:9:16 + --> $DIR/dlist.rs:8:10 + | +LL | const C: LinkedList = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::linkedlist` implied by `-D warnings` + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:9:11 + | +LL | static S: LinkedList = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:12:16 | LL | type Baz = LinkedList; | ^^^^^^^^^^^^^^ | - = note: `-D clippy::linkedlist` implied by `-D warnings` = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:10:15 + --> $DIR/dlist.rs:13:15 | LL | fn foo(_: LinkedList); | ^^^^^^^^^^^^^^ @@ -16,7 +32,7 @@ LL | fn foo(_: LinkedList); = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:11:23 + --> $DIR/dlist.rs:14:23 | LL | const BAR: Option>; | ^^^^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | const BAR: Option>; = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:22:15 + --> $DIR/dlist.rs:25:15 | LL | fn foo(_: LinkedList) {} | ^^^^^^^^^^^^^^ @@ -32,7 +48,7 @@ LL | fn foo(_: LinkedList) {} = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:25:39 + --> $DIR/dlist.rs:28:39 | LL | pub fn test(my_favourite_linked_list: LinkedList) { | ^^^^^^^^^^^^^^ @@ -40,12 +56,12 @@ LL | pub fn test(my_favourite_linked_list: LinkedList) { = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:29:29 + --> $DIR/dlist.rs:32:29 | LL | pub fn test_ret() -> Option> { | ^^^^^^^^^^^^^^ | = help: a `VecDeque` might work -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 6859ba8e5bb..2faab9e035d 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,6 +1,9 @@ #![deny(clippy::option_option)] #![allow(clippy::unnecessary_wraps)] +const C: Option> = None; +static S: Option> = None; + fn input(_: Option>) {} fn output() -> Option> { diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index ad7f081c713..a925bb35b04 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,8 +1,8 @@ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:4:13 + --> $DIR/option_option.rs:4:10 | -LL | fn input(_: Option>) {} - | ^^^^^^^^^^^^^^^^^^ +LL | const C: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/option_option.rs:1:9 @@ -11,58 +11,70 @@ LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:6:16 + --> $DIR/option_option.rs:5:11 + | +LL | static S: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:7:13 + | +LL | fn input(_: Option>) {} + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:9:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:10:27 + --> $DIR/option_option.rs:13:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:15:30 + --> $DIR/option_option.rs:18:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:20:8 + --> $DIR/option_option.rs:23:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:24:23 + --> $DIR/option_option.rs:27:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:30:22 + --> $DIR/option_option.rs:33:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:34:11 + --> $DIR/option_option.rs:37:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:35:17 + --> $DIR/option_option.rs:38:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:76:14 + --> $DIR/option_option.rs:79:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index 4fa28b525c3..a40d91fdb18 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]); /// The following should trigger the lint mod should_trigger { use super::SizedStruct; + const C: Vec = Vec::new(); + static S: Vec = Vec::new(); struct StructWithVecBox { sized_type: Vec, diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index 7dc735cd90b..843bbb64e71 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]); /// The following should trigger the lint mod should_trigger { use super::SizedStruct; + const C: Vec> = Vec::new(); + static S: Vec> = Vec::new(); struct StructWithVecBox { sized_type: Vec>, diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr index 83435a40aa1..c518267f041 100644 --- a/tests/ui/vec_box_sized.stderr +++ b/tests/ui/vec_box_sized.stderr @@ -1,28 +1,40 @@ error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:14:21 + --> $DIR/vec_box_sized.rs:12:14 | -LL | sized_type: Vec>, - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` +LL | const C: Vec> = Vec::new(); + | ^^^^^^^^^^^^^ help: try: `Vec` | = note: `-D clippy::vec-box` implied by `-D warnings` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:17:14 + --> $DIR/vec_box_sized.rs:13:15 + | +LL | static S: Vec> = Vec::new(); + | ^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:16:21 + | +LL | sized_type: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:19:14 | LL | struct A(Vec>); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:18:18 + --> $DIR/vec_box_sized.rs:20:18 | LL | struct B(Vec>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:46:23 + --> $DIR/vec_box_sized.rs:48:23 | LL | pub fn f() -> Vec> { | ^^^^^^^^^^^ help: try: `Vec` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 818f8320a34c60ec72c2cce0eb24a4a26c0d2b7a Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 20 Mar 2021 15:32:19 +0900 Subject: Merge type_complexity pass into types pass --- clippy_lints/src/lib.rs | 5 +- clippy_lints/src/types/mod.rs | 308 +++++++++++------------------- clippy_lints/src/types/type_complexity.rs | 79 ++++++++ 3 files changed, 192 insertions(+), 200 deletions(-) create mode 100644 clippy_lints/src/types/type_complexity.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 05165ec9d66..402fecd478c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1031,7 +1031,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeApi); let vec_box_size_threshold = conf.vec_box_size_threshold; - store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); + let type_complexity_threshold = conf.type_complexity_threshold; + store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); @@ -1092,8 +1093,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); store.register_late_pass(|| box entry::HashMapPass); - let type_complexity_threshold = conf.type_complexity_threshold; - store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); store.register_late_pass(|| box minmax::MinMaxPass); store.register_late_pass(|| box open_options::OpenOptions); store.register_late_pass(|| box zero_div_zero::ZeroDiv); diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 1df964db38f..12e1eba2ca6 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -4,21 +4,19 @@ mod linked_list; mod option_option; mod rc_buffer; mod redundant_allocation; +mod type_complexity; mod utils; mod vec_box; -use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_ty, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Body, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, - MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, + Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, + TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. @@ -231,63 +229,121 @@ declare_clippy_lint! { "shared ownership of a buffer type" } +declare_clippy_lint! { + /// **What it does:** Checks for types used in structs, parameters and `let` + /// declarations above a certain complexity threshold. + /// + /// **Why is this bad?** Too complex types make the code less readable. Consider + /// using a `type` definition to simplify them. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::rc::Rc; + /// struct Foo { + /// inner: Rc>>>, + /// } + /// ``` + pub TYPE_COMPLEXITY, + complexity, + "usage of very complex types that might be better factored into `type` definitions" +} + pub struct Types { vec_box_size_threshold: u64, + type_complexity_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { - // Skip trait implementations; see issue #605. - if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) { - if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return; - } - } + let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) + { + matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) + } else { + false + }; - self.check_fn_decl(cx, decl); + self.check_fn_decl( + cx, + decl, + CheckTyContext { + is_in_trait_impl, + ..CheckTyContext::default() + }, + ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { match item.kind { - ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_ty(cx, ty, false), + ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => { + self.check_ty(cx, ty, CheckTyContext::default()) + }, // functions, enums, structs, impls and traits are covered _ => (), } } + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + match item.kind { + ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_ty( + cx, + ty, + CheckTyContext { + is_in_trait_impl: true, + ..CheckTyContext::default() + }, + ), + // methods are covered by check_fn + ImplItemKind::Fn(..) => (), + } + } + fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - self.check_ty(cx, &field.ty, false); + self.check_ty(cx, &field.ty, CheckTyContext::default()); } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { match item.kind { - TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false), - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl), + TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => { + self.check_ty(cx, ty, CheckTyContext::default()) + }, + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl, CheckTyContext::default()), TraitItemKind::Type(..) => (), } } fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { if let Some(ref ty) = local.ty { - self.check_ty(cx, ty, true); + self.check_ty( + cx, + ty, + CheckTyContext { + is_local: true, + ..CheckTyContext::default() + }, + ); } } } impl Types { - pub fn new(vec_box_size_threshold: u64) -> Self { - Self { vec_box_size_threshold } + pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self { + Self { + vec_box_size_threshold, + type_complexity_threshold, + } } - fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>) { + fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { for input in decl.inputs { - self.check_ty(cx, input, false); + self.check_ty(cx, input, context); } if let FnRetTy::Return(ref ty) = decl.output { - self.check_ty(cx, ty, false); + self.check_ty(cx, ty, context); } } @@ -295,12 +351,22 @@ impl Types { /// lint found. /// /// The parameter `is_local` distinguishes the context of the type. - fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: bool) { + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) { if hir_ty.span.from_expansion() { return; } + + if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) { + return; + } + + // Skip trait implementations; see issue #605. + if context.is_in_trait_impl { + return; + } + match hir_ty.kind { - TyKind::Path(ref qpath) if !is_local => { + TyKind::Path(ref qpath) if !context.is_local => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { @@ -318,7 +384,8 @@ impl Types { } match *qpath { QPath::Resolved(Some(ref ty), ref p) => { - self.check_ty(cx, ty, is_local); + context.is_nested_call = true; + self.check_ty(cx, ty, context); for ty in p.segments.iter().flat_map(|seg| { seg.args .as_ref() @@ -328,10 +395,11 @@ impl Types { _ => None, }) }) { - self.check_ty(cx, ty, is_local); + self.check_ty(cx, ty, context); } }, QPath::Resolved(None, ref p) => { + context.is_nested_call = true; for ty in p.segments.iter().flat_map(|seg| { seg.args .as_ref() @@ -341,17 +409,18 @@ impl Types { _ => None, }) }) { - self.check_ty(cx, ty, is_local); + self.check_ty(cx, ty, context); } }, QPath::TypeRelative(ref ty, ref seg) => { - self.check_ty(cx, ty, is_local); + context.is_nested_call = true; + self.check_ty(cx, ty, context); if let Some(ref params) = seg.args { for ty in params.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, }) { - self.check_ty(cx, ty, is_local); + self.check_ty(cx, ty, context); } } }, @@ -359,16 +428,19 @@ impl Types { } }, TyKind::Rptr(ref lt, ref mut_ty) => { + context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { - self.check_ty(cx, &mut_ty.ty, is_local); + self.check_ty(cx, &mut_ty.ty, context); } }, TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => { - self.check_ty(cx, ty, is_local) + context.is_nested_call = true; + self.check_ty(cx, ty, context) }, TyKind::Tup(tys) => { + context.is_nested_call = true; for ty in tys { - self.check_ty(cx, ty, is_local); + self.check_ty(cx, ty, context); } }, _ => {}, @@ -376,167 +448,9 @@ impl Types { } } -declare_clippy_lint! { - /// **What it does:** Checks for types used in structs, parameters and `let` - /// declarations above a certain complexity threshold. - /// - /// **Why is this bad?** Too complex types make the code less readable. Consider - /// using a `type` definition to simplify them. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # use std::rc::Rc; - /// struct Foo { - /// inner: Rc>>>, - /// } - /// ``` - pub TYPE_COMPLEXITY, - complexity, - "usage of very complex types that might be better factored into `type` definitions" -} - -pub struct TypeComplexity { - threshold: u64, -} - -impl TypeComplexity { - #[must_use] - pub fn new(threshold: u64) -> Self { - Self { threshold } - } -} - -impl_lint_pass!(TypeComplexity => [TYPE_COMPLEXITY]); - -impl<'tcx> LateLintPass<'tcx> for TypeComplexity { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - _: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - _: &'tcx Body<'_>, - _: Span, - _: HirId, - ) { - self.check_fndecl(cx, decl); - } - - fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) { - // enum variants are also struct fields now - self.check_type(cx, &field.ty); - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - match item.kind { - ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_type(cx, ty), - // functions, enums, structs, impls and traits are covered - _ => (), - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - match item.kind { - TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty), - TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl), - // methods with default impl are covered by check_fn - TraitItemKind::Type(..) | TraitItemKind::Fn(_, TraitFn::Provided(_)) => (), - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - match item.kind { - ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty), - // methods are covered by check_fn - ImplItemKind::Fn(..) => (), - } - } - - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - if let Some(ref ty) = local.ty { - self.check_type(cx, ty); - } - } -} - -impl<'tcx> TypeComplexity { - fn check_fndecl(&self, cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>) { - for arg in decl.inputs { - self.check_type(cx, arg); - } - if let FnRetTy::Return(ref ty) = decl.output { - self.check_type(cx, ty); - } - } - - fn check_type(&self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { - if ty.span.from_expansion() { - return; - } - let score = { - let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; - visitor.visit_ty(ty); - visitor.score - }; - - if score > self.threshold { - span_lint( - cx, - TYPE_COMPLEXITY, - ty.span, - "very complex type used. Consider factoring parts into `type` definitions", - ); - } - } -} - -/// Walks a type and assigns a complexity score to it. -struct TypeComplexityVisitor { - /// total complexity score of the type - score: u64, - /// current nesting level - nest: u64, -} - -impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { - let (add_score, sub_nest) = match ty.kind { - // _, &x and *x have only small overhead; don't mess with nesting level - TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), - - // the "normal" components of a type: named types, arrays/tuples - TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), - - // function types bring a lot of overhead - TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), - - TyKind::TraitObject(ref param_bounds, _, _) => { - let has_lifetime_parameters = param_bounds.iter().any(|bound| { - bound - .bound_generic_params - .iter() - .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) - }); - if has_lifetime_parameters { - // complex trait bounds like A<'a, 'b> - (50 * self.nest, 1) - } else { - // simple trait bounds like A + B - (20 * self.nest, 0) - } - }, - - _ => (0, 0), - }; - self.score += add_score; - self.nest += sub_nest; - walk_ty(self, ty); - self.nest -= sub_nest; - } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } +#[derive(Clone, Copy, Default)] +struct CheckTyContext { + is_in_trait_impl: bool, + is_local: bool, + is_nested_call: bool, } diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs new file mode 100644 index 00000000000..9a4e9da3e2b --- /dev/null +++ b/clippy_lints/src/types/type_complexity.rs @@ -0,0 +1,79 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_ty, NestedVisitorMap, Visitor}; +use rustc_hir::{GenericParamKind, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_target::spec::abi::Abi; + +use super::TYPE_COMPLEXITY; + +pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool { + let score = { + let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; + visitor.visit_ty(ty); + visitor.score + }; + + if score > type_complexity_threshold { + span_lint( + cx, + TYPE_COMPLEXITY, + ty.span, + "very complex type used. Consider factoring parts into `type` definitions", + ); + true + } else { + false + } +} + +/// Walks a type and assigns a complexity score to it. +struct TypeComplexityVisitor { + /// total complexity score of the type + score: u64, + /// current nesting level + nest: u64, +} + +impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + let (add_score, sub_nest) = match ty.kind { + // _, &x and *x have only small overhead; don't mess with nesting level + TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), + + // the "normal" components of a type: named types, arrays/tuples + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), + + // function types bring a lot of overhead + TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), + + TyKind::TraitObject(ref param_bounds, _, _) => { + let has_lifetime_parameters = param_bounds.iter().any(|bound| { + bound + .bound_generic_params + .iter() + .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) + }); + if has_lifetime_parameters { + // complex trait bounds like A<'a, 'b> + (50 * self.nest, 1) + } else { + // simple trait bounds like A + B + (20 * self.nest, 0) + } + }, + + _ => (0, 0), + }; + self.score += add_score; + self.nest += sub_nest; + walk_ty(self, ty); + self.nest -= sub_nest; + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} -- cgit 1.4.1-3-g733a5 From 1a1adad81d0fd7d0c64d0a72575a6b4db69c035a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 26 Mar 2021 10:56:05 +0100 Subject: Add MSRV option to unnested_or_patterns lint --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 44 ++++++++++++++++++++++++++------ clippy_lints/src/utils/conf.rs | 2 +- tests/ui/min_rust_version_attr.rs | 6 +++++ tests/ui/min_rust_version_attr.stderr | 8 +++--- 5 files changed, 48 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3a9236d8735..f013613119c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1079,6 +1079,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); store.register_late_pass(move || box casts::Casts::new(msrv)); + store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); @@ -1254,7 +1255,6 @@ 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_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 2b9479365c6..9376a2cf66a 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,15 +1,19 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] -use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::over; +use clippy_utils::{ + ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}, + meets_msrv, +}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; 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_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; use std::cell::Cell; @@ -50,26 +54,50 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); +const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0); + +#[derive(Clone, Copy)] +pub struct UnnestedOrPatterns { + msrv: Option, +} + +impl UnnestedOrPatterns { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_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); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + 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); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + 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); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &p.pat); + } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - lint_unnested_or_patterns(cx, &l.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &l.pat); + } } + + extract_msrv_attr!(EarlyContext); } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9139a0966c5..b372f5b0e72 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: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 0f47f1cbc40..49ace1ca128 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -123,6 +123,11 @@ fn missing_const_for_fn() -> i32 { 1 } +fn unnest_or_patterns() { + struct TS(u8, u8); + if let TS(0, x) | TS(1, x) = TS(0, 0) {} +} + fn main() { filter_map_next(); checked_conversion(); @@ -138,6 +143,7 @@ fn main() { replace_with_default(); map_unwrap_or(); missing_const_for_fn(); + unnest_or_patterns(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index e3e3b335cbe..8d3575c2da8 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:150:24 + --> $DIR/min_rust_version_attr.rs:156:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:149:9 + --> $DIR/min_rust_version_attr.rs:155:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:162:24 + --> $DIR/min_rust_version_attr.rs:168:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:161:9 + --> $DIR/min_rust_version_attr.rs:167:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 5351bbfbf52acea4af2a6b72685fcf2ff935b476 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 26 Mar 2021 15:39:23 +0100 Subject: Add missing lints to MSRV config doc --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b372f5b0e72..147f823491d 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: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), -- cgit 1.4.1-3-g733a5 From 4e19d406a916fc053a6e217f15efe35b20a7ddb5 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 27 Mar 2021 00:44:30 +0100 Subject: upper_case_acronyms: only lint enum variants if the enum is not public --- clippy_lints/src/upper_case_acronyms.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index a6d29d36862..f8023d07437 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -99,21 +99,21 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { impl EarlyLintPass for UpperCaseAcronyms { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) { - if_chain! { - if !in_external_macro(cx.sess(), it.span); + // do not lint public items or in macros + if !in_external_macro(cx.sess(), it.span) && !matches!(it.vis.kind, VisibilityKind::Public) { if matches!( it.kind, - ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..) - ); - // do not lint public items - if !matches!(it.vis.kind, VisibilityKind::Public); - then { + ItemKind::TyAlias(..) | ItemKind::Struct(..) | ItemKind::Trait(..) + ) { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); + } else if let ItemKind::Enum(ref enumdef, _) = it.kind { + // check enum variants seperately because again we only want to lint on private enums and + // the fn check_variant does not know about the vis of the enum of its variants + &enumdef + .variants + .iter() + .for_each(|variant| check_ident(cx, &variant.ident, self.upper_case_acronyms_aggressive)); } } } - - fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) { - check_ident(cx, &v.ident, self.upper_case_acronyms_aggressive); - } } -- cgit 1.4.1-3-g733a5 From ca7e95501c13f386956728bb66ec099a0d89e8e5 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 27 Mar 2021 00:52:49 +0100 Subject: upper_case_acronyms: add ui and ui-toml tests for private/public enums --- clippy_lints/src/upper_case_acronyms.rs | 5 ++--- .../upper_case_acronyms.rs | 16 ++++++++++++++++ .../upper_case_acronyms.stderr | 14 +++++++++++++- tests/ui/upper_case_acronyms.rs | 14 ++++++++++++++ tests/ui/upper_case_acronyms.stderr | 8 +++++++- 5 files changed, 52 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index f8023d07437..4ac2ec55b98 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{Item, ItemKind, Variant, VisibilityKind}; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -109,7 +108,7 @@ impl EarlyLintPass for UpperCaseAcronyms { } else if let ItemKind::Enum(ref enumdef, _) = it.kind { // check enum variants seperately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants - &enumdef + enumdef .variants .iter() .for_each(|variant| check_ident(cx, &variant.ident, self.upper_case_acronyms_aggressive)); diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs index c6659edacc3..1a5cf1b1947 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs @@ -25,4 +25,20 @@ pub struct MIXEDCapital; pub struct FULLCAPITAL; +// enum variants should not be linted if the num is pub +pub enum ParseError { + FULLCAPITAL(u8), + MIXEDCapital(String), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + +// private, do lint here +enum ParseErrorPrivate { + WASD(u8), + WASDMixed(String), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + fn main() {} diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr index 38e30683d57..02f29bbefe1 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr @@ -66,5 +66,17 @@ error: name `GCCLLVMSomething` contains a capitalized acronym LL | struct GCCLLVMSomething; | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` -error: aborting due to 11 previous errors +error: name `WASD` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:38:5 + | +LL | WASD(u8), + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` + +error: name `WASDMixed` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:39:5 + | +LL | WASDMixed(String), + | ^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `WasdMixed` + +error: aborting due to 13 previous errors diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index 8c09c6f5b23..48bb9e54b12 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -24,4 +24,18 @@ struct GCCLLVMSomething; pub struct NOWARNINGHERE; pub struct ALSONoWarningHERE; +// enum variants should not be linted if the num is pub +pub enum ParseError { + YDB(u8), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + +// private, do lint here +enum ParseErrorPrivate { + WASD(u8), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr index bbe38991e52..250b196a99e 100644 --- a/tests/ui/upper_case_acronyms.stderr +++ b/tests/ui/upper_case_acronyms.stderr @@ -48,5 +48,11 @@ error: name `FIN` contains a capitalized acronym LL | FIN, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` -error: aborting due to 8 previous errors +error: name `WASD` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:36:5 + | +LL | WASD(u8), + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 12985afbcad28eb67c0b5c5903ff21b6fa922b13 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 26 Mar 2021 16:27:19 -0400 Subject: `len_without_is_empty` improvements Check the return type of `len`. Only integral types, or an `Option` or `Result` wrapping one. Ensure the return type of `is_empty` matches. e.g. `Option` -> `Option` --- clippy_lints/src/len_zero.rs | 78 +++++++++++++++++++++++----- tests/ui/len_without_is_empty.rs | 99 +++++++++++++++++++++++++++++++++++- tests/ui/len_without_is_empty.stderr | 79 ++++++++++++++++++++++++---- 3 files changed, 231 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 717f2ea84f4..fb522be2f1a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -10,9 +10,12 @@ use rustc_hir::{ ItemKind, Mutability, Node, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, AssocKind, FnSig}; +use rustc_middle::ty::{self, AssocKind, FnSig, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned, Symbol}; +use rustc_span::{ + source_map::{Span, Spanned, Symbol}, + symbol::sym, +}; declare_clippy_lint! { /// **What it does:** Checks for getting the length of something via `.len()` @@ -137,6 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let Some(local_id) = ty_id.as_local(); let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); + if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder()); then { let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), @@ -148,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } _ => return, }; - check_for_is_empty(cx, sig.span, sig.decl.implicit_self, ty_id, name, kind) + check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind) } } } @@ -231,10 +235,62 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } +#[derive(Debug, Clone, Copy)] +enum LenOutput<'tcx> { + Integral, + Option(DefId), + Result(DefId, Ty<'tcx>), +} +fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option> { + match *sig.output().kind() { + ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => { + subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did)) + }, + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::result_type, adt.did) => subs + .type_at(0) + .is_integral() + .then(|| LenOutput::Result(adt.did, subs.type_at(1))), + _ => None, + } +} + +impl LenOutput<'_> { + fn matches_is_empty_output(self, ty: Ty<'_>) -> bool { + match (self, ty.kind()) { + (_, &ty::Bool) => true, + (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did => subs.type_at(0).is_bool(), + (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did => { + subs.type_at(0).is_bool() && TyS::same_type(subs.type_at(1), err_ty) + }, + _ => false, + } + } + + fn expected_sig(self, self_kind: ImplicitSelfKind) -> String { + let self_ref = match self_kind { + ImplicitSelfKind::ImmRef => "&", + ImplicitSelfKind::MutRef => "&mut ", + _ => "", + }; + match self { + Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), + Self::Option(_) => format!( + "expected signature: `({}self) -> bool` or `({}self) -> Option", + self_ref, self_ref + ), + Self::Result(..) => format!( + "expected signature: `({}self) -> bool` or `({}self) -> Result", + self_ref, self_ref + ), + } + } +} + /// Checks if the given signature matches the expectations for `is_empty` -fn check_is_empty_sig(cx: &LateContext<'_>, sig: FnSig<'_>, self_kind: ImplicitSelfKind) -> bool { +fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool { match &**sig.inputs_and_output { - [arg, res] if *res == cx.tcx.types.bool => { + [arg, res] if len_output.matches_is_empty_output(res) => { matches!( (arg.kind(), self_kind), (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) @@ -250,6 +306,7 @@ fn check_for_is_empty( cx: &LateContext<'_>, span: Span, self_kind: ImplicitSelfKind, + output: LenOutput<'_>, impl_ty: DefId, item_name: Symbol, item_kind: &str, @@ -289,7 +346,7 @@ fn check_for_is_empty( }, Some(is_empty) if !(is_empty.fn_has_self_parameter - && check_is_empty_sig(cx, cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind)) => + && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) => { ( format!( @@ -309,14 +366,7 @@ fn check_for_is_empty( db.span_note(span, "`is_empty` defined here"); } if let Some(self_kind) = self_kind { - db.note(&format!( - "expected signature: `({}self) -> bool`", - match self_kind { - ImplicitSelfKind::ImmRef => "&", - ImplicitSelfKind::MutRef => "&mut ", - _ => "", - } - )); + db.note(&output.expected_sig(self_kind)); } }); } diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 6b3636a482e..b9d66347c27 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -1,3 +1,5 @@ +// edition:2018 + #![warn(clippy::len_without_is_empty)] #![allow(dead_code, unused)] @@ -172,9 +174,9 @@ pub trait DependsOnFoo: Foo { fn len(&mut self) -> usize; } +// issue #1562 pub struct MultipleImpls; -// issue #1562 impl MultipleImpls { pub fn len(&self) -> usize { 1 @@ -187,4 +189,99 @@ impl MultipleImpls { } } +// issue #6958 +pub struct OptionalLen; + +impl OptionalLen { + pub fn len(&self) -> Option { + Some(0) + } + + pub fn is_empty(&self) -> Option { + Some(true) + } +} + +pub struct OptionalLen2; +impl OptionalLen2 { + pub fn len(&self) -> Option { + Some(0) + } + + pub fn is_empty(&self) -> bool { + true + } +} + +pub struct OptionalLen3; +impl OptionalLen3 { + pub fn len(&self) -> usize { + 0 + } + + // should lint, len is not an option + pub fn is_empty(&self) -> Option { + None + } +} + +pub struct ResultLen; +impl ResultLen { + pub fn len(&self) -> Result { + Ok(0) + } + + // Differing result types + pub fn is_empty(&self) -> Option { + Some(true) + } +} + +pub struct ResultLen2; +impl ResultLen2 { + pub fn len(&self) -> Result { + Ok(0) + } + + pub fn is_empty(&self) -> Result { + Ok(true) + } +} + +pub struct ResultLen3; +impl ResultLen3 { + pub fn len(&self) -> Result { + Ok(0) + } + + // Non-fallible result is ok. + pub fn is_empty(&self) -> bool { + true + } +} + +pub struct OddLenSig; +impl OddLenSig { + // don't lint + pub fn len(&self) -> bool { + true + } +} + +// issue #6958 +pub struct AsyncLen; +impl AsyncLen { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> usize { + if self.async_task().await { 0 } else { 1 } + } + + pub async fn is_empty(&self) -> bool { + self.len().await == 0 + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index f106506faf4..ff7f5562308 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -1,5 +1,5 @@ error: struct `PubOne` has a public `len` method, but no `is_empty` method - --> $DIR/len_without_is_empty.rs:7:5 + --> $DIR/len_without_is_empty.rs:9:5 | LL | pub fn len(&self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | pub fn len(&self) -> isize { = note: `-D clippy::len-without-is-empty` implied by `-D warnings` error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:55:1 + --> $DIR/len_without_is_empty.rs:57:1 | LL | / pub trait PubTraitsToo { LL | | fn len(&self) -> isize; @@ -15,50 +15,109 @@ LL | | } | |_^ error: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` method - --> $DIR/len_without_is_empty.rs:68:5 + --> $DIR/len_without_is_empty.rs:70:5 | LL | pub fn len(&self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `is_empty` defined here - --> $DIR/len_without_is_empty.rs:72:5 + --> $DIR/len_without_is_empty.rs:74:5 | LL | fn is_empty(&self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` method has an unexpected signature - --> $DIR/len_without_is_empty.rs:80:5 + --> $DIR/len_without_is_empty.rs:82:5 | LL | pub fn len(&self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `is_empty` defined here - --> $DIR/len_without_is_empty.rs:84:5 + --> $DIR/len_without_is_empty.rs:86:5 | LL | pub fn is_empty(&self, x: u32) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature: `(&self) -> bool` error: struct `MismatchedSelf` has a public `len` method, but the `is_empty` method has an unexpected signature - --> $DIR/len_without_is_empty.rs:92:5 + --> $DIR/len_without_is_empty.rs:94:5 | LL | pub fn len(self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `is_empty` defined here - --> $DIR/len_without_is_empty.rs:96:5 + --> $DIR/len_without_is_empty.rs:98:5 | LL | pub fn is_empty(&self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature: `(self) -> bool` error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:171:1 + --> $DIR/len_without_is_empty.rs:173:1 | LL | / pub trait DependsOnFoo: Foo { LL | | fn len(&mut self) -> usize; LL | | } | |_^ -error: aborting due to 6 previous errors +error: struct `OptionalLen3` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:218:5 + | +LL | pub fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:223:5 + | +LL | pub fn is_empty(&self) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` + +error: struct `ResultLen` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:230:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:235:5 + | +LL | pub fn is_empty(&self) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` or `(&self) -> Result + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:230:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: use a custom Error type instead + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:242:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:246:5 + | +LL | pub fn is_empty(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:253:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From febf34e2b4ed9b66fc3095d55ae13cef9b6b154b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 24 Mar 2021 23:12:08 +0900 Subject: Move too_many_arguments to its own module --- clippy_lints/src/functions.rs | 738 ----------------------- clippy_lints/src/functions/mod.rs | 713 ++++++++++++++++++++++ clippy_lints/src/functions/too_many_arguments.rs | 73 +++ clippy_lints/src/lib.rs | 6 +- 4 files changed, 789 insertions(+), 741 deletions(-) delete mode 100644 clippy_lints/src/functions.rs create mode 100644 clippy_lints/src/functions/mod.rs create mode 100644 clippy_lints/src/functions/too_many_arguments.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs deleted file mode 100644 index 730492fc7e3..00000000000 --- a/clippy_lints/src/functions.rs +++ /dev/null @@ -1,738 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; -use clippy_utils::{ - attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, - path_to_local, return_ty, trait_ref_of_method, -}; -use if_chain::if_chain; -use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::intravisit; -use rustc_hir::{def::Res, def_id::DefId, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; -use rustc_span::sym; -use rustc_target::spec::abi::Abi; -use rustc_typeck::hir_ty_to_ty; - -declare_clippy_lint! { - /// **What it does:** Checks for functions with too many parameters. - /// - /// **Why is this bad?** Functions with lots of parameters are considered bad - /// style and reduce readability (“what does the 5th parameter mean?”). Consider - /// grouping some parameters into a new type. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # struct Color; - /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { - /// // .. - /// } - /// ``` - pub TOO_MANY_ARGUMENTS, - complexity, - "functions with too many arguments" -} - -declare_clippy_lint! { - /// **What it does:** Checks for functions with a large amount of lines. - /// - /// **Why is this bad?** Functions with a lot of lines are harder to understand - /// due to having to look at a larger amount of code to understand what the - /// function is doing. Consider splitting the body of the function into - /// multiple functions. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn im_too_long() { - /// println!(""); - /// // ... 100 more LoC - /// println!(""); - /// } - /// ``` - pub TOO_MANY_LINES, - pedantic, - "functions with too many lines" -} - -declare_clippy_lint! { - /// **What it does:** Checks for public functions that dereference raw pointer - /// arguments but are not marked unsafe. - /// - /// **Why is this bad?** The function should probably be marked `unsafe`, since - /// for an arbitrary raw pointer, there is no way of telling for sure if it is - /// valid. - /// - /// **Known problems:** - /// - /// * It does not check functions recursively so if the pointer is passed to a - /// private non-`unsafe` function which does the dereferencing, the lint won't - /// trigger. - /// * It only checks for arguments whose type are raw pointers, not raw pointers - /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or - /// `some_argument.get_raw_ptr()`). - /// - /// **Example:** - /// ```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, - "public functions dereferencing raw pointer arguments but not marked `unsafe`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for a [`#[must_use]`] attribute on - /// unit-returning functions and methods. - /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// - /// **Why is this bad?** Unit values are useless. The attribute is likely - /// a remnant of a refactoring that removed the return type. - /// - /// **Known problems:** None. - /// - /// **Examples:** - /// ```rust - /// #[must_use] - /// fn useless() { } - /// ``` - pub MUST_USE_UNIT, - style, - "`#[must_use]` attribute on a unit-returning function / method" -} - -declare_clippy_lint! { - /// **What it does:** Checks for a [`#[must_use]`] attribute without - /// further information on functions and methods that return a type already - /// marked as `#[must_use]`. - /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// - /// **Why is this bad?** The attribute isn't needed. Not using the result - /// will already be reported. Alternatively, one can add some text to the - /// attribute to improve the lint message. - /// - /// **Known problems:** None. - /// - /// **Examples:** - /// ```rust - /// #[must_use] - /// fn double_must_use() -> Result<(), ()> { - /// unimplemented!(); - /// } - /// ``` - pub DOUBLE_MUST_USE, - style, - "`#[must_use]` attribute on a `#[must_use]`-returning function / method" -} - -declare_clippy_lint! { - /// **What it does:** Checks for public functions that have no - /// [`#[must_use]`] attribute, but return something not already marked - /// must-use, have no mutable arg and mutate no statics. - /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// - /// **Why is this bad?** Not bad at all, this lint just shows places where - /// you could add the attribute. - /// - /// **Known problems:** The lint only checks the arguments for mutable - /// types without looking if they are actually changed. On the other hand, - /// it also ignores a broad range of potentially interesting side effects, - /// because we cannot decide whether the programmer intends the function to - /// be called for the side effect or the result. Expect many false - /// positives. At least we don't lint if the result type is unit or already - /// `#[must_use]`. - /// - /// **Examples:** - /// ```rust - /// // this could be annotated with `#[must_use]`. - /// fn id(t: T) -> T { t } - /// ``` - pub MUST_USE_CANDIDATE, - pedantic, - "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)] - /// pub 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, - max_lines: u64, -} - -impl Functions { - pub fn new(threshold: u64, max_lines: u64) -> Self { - Self { threshold, max_lines } - } -} - -impl_lint_pass!(Functions => [ - TOO_MANY_ARGUMENTS, - TOO_MANY_LINES, - NOT_UNSAFE_PTR_ARG_DEREF, - MUST_USE_UNIT, - DOUBLE_MUST_USE, - MUST_USE_CANDIDATE, - RESULT_UNIT_ERR, -]); - -impl<'tcx> LateLintPass<'tcx> for Functions { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - span: Span, - hir_id: hir::HirId, - ) { - let unsafety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, - intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, - intravisit::FnKind::Closure => return, - }; - - // don't warn for implementations, it's not their fault - if !is_trait_impl_item(cx, hir_id) { - // don't lint extern functions decls, it's not their fault either - match kind { - intravisit::FnKind::Method( - _, - &hir::FnSig { - header: hir::FnHeader { abi: Abi::Rust, .. }, - .. - }, - _, - ) - | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => { - self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi())) - }, - _ => {}, - } - } - - Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); - self.check_line_number(cx, span, body); - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(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 { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - return; - } - if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this function could have a `#[must_use]` attribute", - ); - } - } - } - - 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 attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() - { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { - // don't lint extern functions decls, it's not their fault - 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 attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let Some(attr) = attr { - 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() && is_public && !is_proc_macro(cx.sess(), attrs) { - check_must_use_candidate( - cx, - &sig.decl, - body, - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } - } - } - } -} - -impl<'tcx> Functions { - fn check_arg_number(self, cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span) { - let args = decl.inputs.len() as u64; - if args > self.threshold { - span_lint( - cx, - TOO_MANY_ARGUMENTS, - fn_span, - &format!("this function has too many arguments ({}/{})", args, self.threshold), - ); - } - } - - fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>) { - if in_external_macro(cx.sess(), span) { - return; - } - - let code_snippet = snippet(cx, body.value.span, ".."); - let mut line_count: u64 = 0; - let mut in_comment = false; - let mut code_in_line; - - // Skip the surrounding function decl. - let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); - let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); - let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); - - for mut line in function_lines { - code_in_line = false; - loop { - line = line.trim_start(); - if line.is_empty() { - break; - } - if in_comment { - if let Some(i) = line.find("*/") { - line = &line[i + 2..]; - in_comment = false; - continue; - } - } else { - let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); - let single_idx = line.find("//").unwrap_or_else(|| line.len()); - code_in_line |= multi_idx > 0 && single_idx > 0; - // Implies multi_idx is below line.len() - if multi_idx < single_idx { - line = &line[multi_idx + 2..]; - in_comment = true; - continue; - } - } - break; - } - if code_in_line { - line_count += 1; - } - } - - if line_count > self.max_lines { - span_lint( - cx, - TOO_MANY_LINES, - span, - &format!("this function has too many lines ({}/{})", line_count, self.max_lines), - ) - } - } - - fn check_raw_ptr( - cx: &LateContext<'tcx>, - unsafety: hir::Unsafety, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - hir_id: hir::HirId, - ) { - let expr = &body.value; - if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { - let raw_ptrs = iter_input_pats(decl, body) - .zip(decl.inputs.iter()) - .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) - .collect::>(); - - if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); - } - } - } -} - -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; - let ty = hir_ty_to_ty(cx.tcx, ty); - if is_type_diagnostic_item(cx, ty, sym::result_type); - if let ty::Adt(_, substs) = ty.kind(); - let err_ty = substs.type_at(1); - if err_ty.is_unit(); - 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<'_>, - item_id: hir::HirId, - item_span: Span, - fn_header_span: Span, - attr: &Attribute, -) { - if in_external_macro(cx.sess(), item_span) { - return; - } - if returns_unit(decl) { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "".into(), - Applicability::MachineApplicable, - ); - }, - ); - } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { - span_lint_and_help( - cx, - DOUBLE_MUST_USE, - fn_header_span, - "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", - None, - "either add some descriptive text or remove the attribute", - ); - } -} - -fn check_must_use_candidate<'tcx>( - cx: &LateContext<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - item_span: Span, - item_id: hir::HirId, - fn_span: Span, - msg: &str, -) { - if has_mutable_arg(cx, body) - || mutates_static(cx, body) - || in_external_macro(cx.sess(), item_span) - || returns_unit(decl) - || !cx.access_levels.is_exported(item_id) - || is_must_use_ty(cx, return_ty(cx, item_id)) - { - return; - } - span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { - if let Some(snippet) = snippet_opt(cx, fn_span) { - diag.span_suggestion( - fn_span, - "add the attribute", - format!("#[must_use] {}", snippet), - Applicability::MachineApplicable, - ); - } - }); -} - -fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { - match decl.output { - hir::FnRetTy::DefaultReturn(_) => true, - hir::FnRetTy::Return(ref ty) => match ty.kind { - hir::TyKind::Tup(ref tys) => tys.is_empty(), - hir::TyKind::Never => true, - _ => false, - }, - } -} - -fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { - let mut tys = FxHashSet::default(); - body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) -} - -fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { - if let hir::PatKind::Wild = pat.kind { - return false; // ignore `_` patterns - } - 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 - } -} - -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; - -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { - match *ty.kind() { - // primitive types are never mutable - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(ref adt, ref substs) => { - tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) - }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) - }, - // calling something constitutes a side effect, so return true on all callables - // also never calls need not be used, so return true for them, too - _ => true, - } -} - -fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { - if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { - Some(id) - } else { - None - } -} - -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: FxHashSet, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(ref f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, _, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function dereferences a raw pointer but is not marked `unsafe`", - ); - } - } - } -} - -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - - if self.mutates_static { - return; - } - match expr.kind { - Call(_, args) | MethodCall(_, _, args, _) => { - let mut tys = FxHashSet::default(); - for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { - self.mutates_static |= is_mutated_static(target) - }, - _ => {}, - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - -fn is_mutated_static(e: &hir::Expr<'_>) -> bool { - use hir::ExprKind::{Field, Index, Path}; - - match e.kind { - Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), - Path(_) => true, - Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), - _ => false, - } -} - -fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, &body.value); - v.mutates_static -} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs new file mode 100644 index 00000000000..68badb87b22 --- /dev/null +++ b/clippy_lints/src/functions/mod.rs @@ -0,0 +1,713 @@ +mod too_many_arguments; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; +use clippy_utils::{ + attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty, + trait_ref_of_method, +}; +use if_chain::if_chain; +use rustc_ast::ast::Attribute; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit; +use rustc_hir::{def::Res, def_id::DefId, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +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::{sym, Span}; +use rustc_typeck::hir_ty_to_ty; + +declare_clippy_lint! { + /// **What it does:** Checks for functions with too many parameters. + /// + /// **Why is this bad?** Functions with lots of parameters are considered bad + /// style and reduce readability (“what does the 5th parameter mean?”). Consider + /// grouping some parameters into a new type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct Color; + /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { + /// // .. + /// } + /// ``` + pub TOO_MANY_ARGUMENTS, + complexity, + "functions with too many arguments" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions with a large amount of lines. + /// + /// **Why is this bad?** Functions with a lot of lines are harder to understand + /// due to having to look at a larger amount of code to understand what the + /// function is doing. Consider splitting the body of the function into + /// multiple functions. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn im_too_long() { + /// println!(""); + /// // ... 100 more LoC + /// println!(""); + /// } + /// ``` + pub TOO_MANY_LINES, + pedantic, + "functions with too many lines" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that dereference raw pointer + /// arguments but are not marked unsafe. + /// + /// **Why is this bad?** The function should probably be marked `unsafe`, since + /// for an arbitrary raw pointer, there is no way of telling for sure if it is + /// valid. + /// + /// **Known problems:** + /// + /// * It does not check functions recursively so if the pointer is passed to a + /// private non-`unsafe` function which does the dereferencing, the lint won't + /// trigger. + /// * It only checks for arguments whose type are raw pointers, not raw pointers + /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or + /// `some_argument.get_raw_ptr()`). + /// + /// **Example:** + /// ```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, + "public functions dereferencing raw pointer arguments but not marked `unsafe`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for a [`#[must_use]`] attribute on + /// unit-returning functions and methods. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** Unit values are useless. The attribute is likely + /// a remnant of a refactoring that removed the return type. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// #[must_use] + /// fn useless() { } + /// ``` + pub MUST_USE_UNIT, + style, + "`#[must_use]` attribute on a unit-returning function / method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for a [`#[must_use]`] attribute without + /// further information on functions and methods that return a type already + /// marked as `#[must_use]`. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** The attribute isn't needed. Not using the result + /// will already be reported. Alternatively, one can add some text to the + /// attribute to improve the lint message. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// #[must_use] + /// fn double_must_use() -> Result<(), ()> { + /// unimplemented!(); + /// } + /// ``` + pub DOUBLE_MUST_USE, + style, + "`#[must_use]` attribute on a `#[must_use]`-returning function / method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that have no + /// [`#[must_use]`] attribute, but return something not already marked + /// must-use, have no mutable arg and mutate no statics. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** Not bad at all, this lint just shows places where + /// you could add the attribute. + /// + /// **Known problems:** The lint only checks the arguments for mutable + /// types without looking if they are actually changed. On the other hand, + /// it also ignores a broad range of potentially interesting side effects, + /// because we cannot decide whether the programmer intends the function to + /// be called for the side effect or the result. Expect many false + /// positives. At least we don't lint if the result type is unit or already + /// `#[must_use]`. + /// + /// **Examples:** + /// ```rust + /// // this could be annotated with `#[must_use]`. + /// fn id(t: T) -> T { t } + /// ``` + pub MUST_USE_CANDIDATE, + pedantic, + "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)] + /// pub 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 { + too_many_arguments_threshold: u64, + too_many_lines_threshold: u64, +} + +impl Functions { + pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self { + Self { + too_many_arguments_threshold, + too_many_lines_threshold, + } + } +} + +impl_lint_pass!(Functions => [ + TOO_MANY_ARGUMENTS, + TOO_MANY_LINES, + NOT_UNSAFE_PTR_ARG_DEREF, + MUST_USE_UNIT, + DOUBLE_MUST_USE, + MUST_USE_CANDIDATE, + RESULT_UNIT_ERR, +]); + +impl<'tcx> LateLintPass<'tcx> for Functions { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + span: Span, + hir_id: hir::HirId, + ) { + too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + + let unsafety = match kind { + intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, + intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, + intravisit::FnKind::Closure => return, + }; + + Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); + self.check_line_number(cx, span, body); + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(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 { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + return; + } + if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this function could have a `#[must_use]` attribute", + ); + } + } + } + + 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 attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() + { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); + + if let hir::TraitItemKind::Fn(ref sig, ref eid) = 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); + } + + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + 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() && is_public && !is_proc_macro(cx.sess(), attrs) { + check_must_use_candidate( + cx, + &sig.decl, + body, + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } + } +} + +impl<'tcx> Functions { + fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>) { + if in_external_macro(cx.sess(), span) { + return; + } + + let code_snippet = snippet(cx, body.value.span, ".."); + let mut line_count: u64 = 0; + let mut in_comment = false; + let mut code_in_line; + + // Skip the surrounding function decl. + let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); + let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); + let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + + for mut line in function_lines { + code_in_line = false; + loop { + line = line.trim_start(); + if line.is_empty() { + break; + } + if in_comment { + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; + } + } else { + let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); + let single_idx = line.find("//").unwrap_or_else(|| line.len()); + code_in_line |= multi_idx > 0 && single_idx > 0; + // Implies multi_idx is below line.len() + if multi_idx < single_idx { + line = &line[multi_idx + 2..]; + in_comment = true; + continue; + } + } + break; + } + if code_in_line { + line_count += 1; + } + } + + if line_count > self.too_many_lines_threshold { + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!( + "this function has too many lines ({}/{})", + line_count, self.too_many_lines_threshold + ), + ) + } + } + + fn check_raw_ptr( + cx: &LateContext<'tcx>, + unsafety: hir::Unsafety, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + hir_id: hir::HirId, + ) { + let expr = &body.value; + if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { + let raw_ptrs = iter_input_pats(decl, body) + .zip(decl.inputs.iter()) + .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) + .collect::>(); + + if !raw_ptrs.is_empty() { + let typeck_results = cx.tcx.typeck_body(body.id()); + let mut v = DerefVisitor { + cx, + ptrs: raw_ptrs, + typeck_results, + }; + + intravisit::walk_expr(&mut v, expr); + } + } + } +} + +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; + let ty = hir_ty_to_ty(cx.tcx, ty); + if is_type_diagnostic_item(cx, ty, sym::result_type); + if let ty::Adt(_, substs) = ty.kind(); + let err_ty = substs.type_at(1); + if err_ty.is_unit(); + 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<'_>, + item_id: hir::HirId, + item_span: Span, + fn_header_span: Span, + attr: &Attribute, +) { + if in_external_macro(cx.sess(), item_span) { + return; + } + if returns_unit(decl) { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + diag.span_suggestion( + attr.span, + "remove the attribute", + "".into(), + Applicability::MachineApplicable, + ); + }, + ); + } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { + span_lint_and_help( + cx, + DOUBLE_MUST_USE, + fn_header_span, + "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", + None, + "either add some descriptive text or remove the attribute", + ); + } +} + +fn check_must_use_candidate<'tcx>( + cx: &LateContext<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + item_span: Span, + item_id: hir::HirId, + fn_span: Span, + msg: &str, +) { + if has_mutable_arg(cx, body) + || mutates_static(cx, body) + || in_external_macro(cx.sess(), item_span) + || returns_unit(decl) + || !cx.access_levels.is_exported(item_id) + || is_must_use_ty(cx, return_ty(cx, item_id)) + { + return; + } + span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { + if let Some(snippet) = snippet_opt(cx, fn_span) { + diag.span_suggestion( + fn_span, + "add the attribute", + format!("#[must_use] {}", snippet), + Applicability::MachineApplicable, + ); + } + }); +} + +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { + match decl.output { + hir::FnRetTy::DefaultReturn(_) => true, + hir::FnRetTy::Return(ref ty) => match ty.kind { + hir::TyKind::Tup(ref tys) => tys.is_empty(), + hir::TyKind::Never => true, + _ => false, + }, + } +} + +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { + let mut tys = FxHashSet::default(); + body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) +} + +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { + if let hir::PatKind::Wild = pat.kind { + return false; // ignore `_` patterns + } + 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 + } +} + +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; + +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { + match *ty.kind() { + // primitive types are never mutable + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, + ty::Adt(ref adt, ref substs) => { + tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + }, + // calling something constitutes a side effect, so return true on all callables + // also never calls need not be used, so return true for them, too + _ => true, + } +} + +fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { + if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { + Some(id) + } else { + None + } +} + +struct DerefVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + ptrs: FxHashSet, + typeck_results: &'a ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + match expr.kind { + hir::ExprKind::Call(ref f, args) => { + let ty = self.typeck_results.expr_ty(f); + + if type_is_unsafe_function(self.cx, ty) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::MethodCall(_, _, args, _) => { + let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); + let base_type = self.cx.tcx.type_of(def_id); + + if type_is_unsafe_function(self.cx, base_type) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), + _ => (), + } + + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { + fn check_arg(&self, ptr: &hir::Expr<'_>) { + if let Some(id) = path_to_local(ptr) { + if self.ptrs.contains(&id) { + span_lint( + self.cx, + NOT_UNSAFE_PTR_ARG_DEREF, + ptr.span, + "this public function dereferences a raw pointer but is not marked `unsafe`", + ); + } + } + } +} + +struct StaticMutVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + mutates_static: bool, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + if self.mutates_static { + return; + } + match expr.kind { + Call(_, args) | MethodCall(_, _, args, _) => { + let mut tys = FxHashSet::default(); + for arg in args { + if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + self.cx, + self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + self.mutates_static = true; + return; + } + tys.clear(); + } + }, + Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { + self.mutates_static |= is_mutated_static(target) + }, + _ => {}, + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +fn is_mutated_static(e: &hir::Expr<'_>) -> bool { + use hir::ExprKind::{Field, Index, Path}; + + match e.kind { + Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), + Path(_) => true, + Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), + _ => false, + } +} + +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { + let mut v = StaticMutVisitor { + cx, + mutates_static: false, + }; + intravisit::walk_expr(&mut v, &body.value); + v.mutates_static +} diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs new file mode 100644 index 00000000000..62b1e6bd7ca --- /dev/null +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -0,0 +1,73 @@ +use rustc_hir::{self as hir, intravisit}; +use rustc_lint::LateContext; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_trait_impl_item; + +use super::TOO_MANY_ARGUMENTS; + +pub(super) fn check_fn( + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + span: Span, + hir_id: hir::HirId, + too_many_arguments_threshold: u64, +) { + // don't warn for implementations, it's not their fault + if !is_trait_impl_item(cx, hir_id) { + // don't lint extern functions decls, it's not their fault either + match kind { + intravisit::FnKind::Method( + _, + &hir::FnSig { + header: hir::FnHeader { abi: Abi::Rust, .. }, + .. + }, + _, + ) + | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => check_arg_number( + cx, + decl, + span.with_hi(decl.output.span().hi()), + too_many_arguments_threshold, + ), + _ => {}, + } + } +} + +pub(super) fn check_trait_item( + cx: &LateContext<'tcx>, + item: &'tcx hir::TraitItem<'_>, + too_many_arguments_threshold: u64, +) { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + // don't lint extern functions decls, it's not their fault + if sig.header.abi == Abi::Rust { + check_arg_number( + cx, + &sig.decl, + item.span.with_hi(sig.decl.output.span().hi()), + too_many_arguments_threshold, + ); + } + } +} + +fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, too_many_arguments_threshold: u64) { + let args = decl.inputs.len() as u64; + if args > too_many_arguments_threshold { + span_lint( + cx, + TOO_MANY_ARGUMENTS, + fn_span, + &format!( + "this function has too many arguments ({}/{})", + args, too_many_arguments_threshold + ), + ); + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..3d3e57f7d2f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1124,9 +1124,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::>(); store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); - let too_many_arguments_threshold1 = conf.too_many_arguments_threshold; - let too_many_lines_threshold2 = conf.too_many_lines_threshold; - store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2)); + let too_many_arguments_threshold = conf.too_many_arguments_threshold; + let too_many_lines_threshold = conf.too_many_lines_threshold; + store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); store.register_late_pass(|| box neg_multiply::NegMultiply); -- cgit 1.4.1-3-g733a5 From 7c028de05f7b6803c062e12ee09a41ea9007d1e1 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 24 Mar 2021 23:20:31 +0900 Subject: Move too_many_lines to its own module --- clippy_lints/src/functions/mod.rs | 64 ++------------------------ clippy_lints/src/functions/too_many_lines.rs | 68 ++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 61 deletions(-) create mode 100644 clippy_lints/src/functions/too_many_lines.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 68badb87b22..97d3623d919 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,7 +1,8 @@ mod too_many_arguments; +mod too_many_lines; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; use clippy_utils::{ attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty, @@ -256,6 +257,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { hir_id: hir::HirId, ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + too_many_lines::check(cx, span, body, self.too_many_lines_threshold); let unsafety = match kind { intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, @@ -264,7 +266,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { }; Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); - self.check_line_number(cx, span, body); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -356,65 +357,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } impl<'tcx> Functions { - fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>) { - if in_external_macro(cx.sess(), span) { - return; - } - - let code_snippet = snippet(cx, body.value.span, ".."); - let mut line_count: u64 = 0; - let mut in_comment = false; - let mut code_in_line; - - // Skip the surrounding function decl. - let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); - let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); - let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); - - for mut line in function_lines { - code_in_line = false; - loop { - line = line.trim_start(); - if line.is_empty() { - break; - } - if in_comment { - if let Some(i) = line.find("*/") { - line = &line[i + 2..]; - in_comment = false; - continue; - } - } else { - let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); - let single_idx = line.find("//").unwrap_or_else(|| line.len()); - code_in_line |= multi_idx > 0 && single_idx > 0; - // Implies multi_idx is below line.len() - if multi_idx < single_idx { - line = &line[multi_idx + 2..]; - in_comment = true; - continue; - } - } - break; - } - if code_in_line { - line_count += 1; - } - } - - if line_count > self.too_many_lines_threshold { - span_lint( - cx, - TOO_MANY_LINES, - span, - &format!( - "this function has too many lines ({}/{})", - line_count, self.too_many_lines_threshold - ), - ) - } - } - fn check_raw_ptr( cx: &LateContext<'tcx>, unsafety: hir::Unsafety, diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs new file mode 100644 index 00000000000..99d5028befc --- /dev/null +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -0,0 +1,68 @@ +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::Span; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet; + +use super::TOO_MANY_LINES; + +pub(super) fn check(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { + if in_external_macro(cx.sess(), span) { + return; + } + + let code_snippet = snippet(cx, body.value.span, ".."); + let mut line_count: u64 = 0; + let mut in_comment = false; + let mut code_in_line; + + // Skip the surrounding function decl. + let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); + let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); + let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + + for mut line in function_lines { + code_in_line = false; + loop { + line = line.trim_start(); + if line.is_empty() { + break; + } + if in_comment { + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; + } + } else { + let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); + let single_idx = line.find("//").unwrap_or_else(|| line.len()); + code_in_line |= multi_idx > 0 && single_idx > 0; + // Implies multi_idx is below line.len() + if multi_idx < single_idx { + line = &line[multi_idx + 2..]; + in_comment = true; + continue; + } + } + break; + } + if code_in_line { + line_count += 1; + } + } + + if line_count > too_many_lines_threshold { + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!( + "this function has too many lines ({}/{})", + line_count, too_many_lines_threshold + ), + ) + } +} -- cgit 1.4.1-3-g733a5 From 9782fc42852927428736f27f0496deece545f46c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Mar 2021 20:43:59 +0900 Subject: move not_unsafe_ptr_arg_deref to its own module --- clippy_lints/src/functions/mod.rs | 116 +------------------ .../src/functions/not_unsafe_ptr_arg_deref.rs | 125 +++++++++++++++++++++ 2 files changed, 131 insertions(+), 110 deletions(-) create mode 100644 clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 97d3623d919..f53edc1b3c7 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,13 +1,11 @@ +mod not_unsafe_ptr_arg_deref; mod too_many_arguments; mod too_many_lines; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; -use clippy_utils::{ - attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty, - trait_ref_of_method, -}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item}; +use clippy_utils::{attr_by_name, attrs::is_proc_macro, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -258,14 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check(cx, span, body, self.too_many_lines_threshold); - - let unsafety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, - intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, - intravisit::FnKind::Closure => return, - }; - - Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); + not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -323,6 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); + not_unsafe_ptr_arg_deref::check_trait_item(cx, item); if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { let is_public = cx.access_levels.is_exported(item.hir_id()); @@ -338,8 +330,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } 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() && is_public && !is_proc_macro(cx.sess(), attrs) { check_must_use_candidate( cx, @@ -356,35 +346,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } } -impl<'tcx> Functions { - fn check_raw_ptr( - cx: &LateContext<'tcx>, - unsafety: hir::Unsafety, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - hir_id: hir::HirId, - ) { - let expr = &body.value; - if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { - let raw_ptrs = iter_input_pats(decl, body) - .zip(decl.inputs.iter()) - .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) - .collect::>(); - - if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); - } - } - } -} - 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); @@ -524,71 +485,6 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m } } -fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { - if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { - Some(id) - } else { - None - } -} - -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: FxHashSet, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(ref f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, _, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function dereferences a raw pointer but is not marked `unsafe`", - ); - } - } - } -} - struct StaticMutVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, mutates_static: bool, diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs new file mode 100644 index 00000000000..ac02b60a356 --- /dev/null +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -0,0 +1,125 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{self as hir, intravisit}; +use rustc_lint::LateContext; +use rustc_middle::{hir::map::Map, ty}; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::{iter_input_pats, path_to_local}; + +use super::NOT_UNSAFE_PTR_ARG_DEREF; + +pub(super) fn check_fn( + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + hir_id: hir::HirId, +) { + let unsafety = match kind { + intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, + intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, + intravisit::FnKind::Closure => return, + }; + + check_raw_ptr(cx, unsafety, decl, body, hir_id); +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { + let body = cx.tcx.hir().body(eid); + check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id()); + } +} + +fn check_raw_ptr( + cx: &LateContext<'tcx>, + unsafety: hir::Unsafety, + decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + hir_id: hir::HirId, +) { + let expr = &body.value; + if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { + let raw_ptrs = iter_input_pats(decl, body) + .zip(decl.inputs.iter()) + .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) + .collect::>(); + + if !raw_ptrs.is_empty() { + let typeck_results = cx.tcx.typeck_body(body.id()); + let mut v = DerefVisitor { + cx, + ptrs: raw_ptrs, + typeck_results, + }; + + intravisit::walk_expr(&mut v, expr); + } + } +} + +fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { + if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { + Some(id) + } else { + None + } +} + +struct DerefVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + ptrs: FxHashSet, + typeck_results: &'a ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + match expr.kind { + hir::ExprKind::Call(ref f, args) => { + let ty = self.typeck_results.expr_ty(f); + + if type_is_unsafe_function(self.cx, ty) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::MethodCall(_, _, args, _) => { + let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); + let base_type = self.cx.tcx.type_of(def_id); + + if type_is_unsafe_function(self.cx, base_type) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), + _ => (), + } + + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { + fn check_arg(&self, ptr: &hir::Expr<'_>) { + if let Some(id) = path_to_local(ptr) { + if self.ptrs.contains(&id) { + span_lint( + self.cx, + NOT_UNSAFE_PTR_ARG_DEREF, + ptr.span, + "this public function dereferences a raw pointer but is not marked `unsafe`", + ); + } + } + } +} -- cgit 1.4.1-3-g733a5 From c37916501db2c649f57d2cc0d84ee41db34094c4 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Mar 2021 22:41:55 +0900 Subject: Move lints related to must_use to their own module --- clippy_lints/src/functions/mod.rs | 259 ++----------------------------- clippy_lints/src/functions/must_use.rs | 272 +++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 248 deletions(-) create mode 100644 clippy_lints/src/functions/must_use.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index f53edc1b3c7..a1f4a453608 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,22 +1,17 @@ +mod must_use; mod not_unsafe_ptr_arg_deref; mod too_many_arguments; mod too_many_lines; -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item}; -use clippy_utils::{attr_by_name, attrs::is_proc_macro, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; -use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit; -use rustc_hir::{def::Res, def_id::DefId, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; @@ -260,88 +255,38 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + must_use::check_item(cx, item); + if let hir::ItemKind::Fn(ref sig, ref _generics, _) = 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 { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - return; - } - if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this function could have a `#[must_use]` attribute", - ); - } } } 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 { + must_use::check_impl_item(cx, item); + if let hir::ImplItemKind::Fn(ref sig, _) = 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 attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() - { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); + must_use::check_trait_item(cx, item); - if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, _) = 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); } - - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let Some(attr) = attr { - 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); - if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { - check_must_use_candidate( - cx, - &sig.decl, - body, - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } - } } } } @@ -367,185 +312,3 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span } } } - -fn check_needless_must_use( - cx: &LateContext<'_>, - decl: &hir::FnDecl<'_>, - item_id: hir::HirId, - item_span: Span, - fn_header_span: Span, - attr: &Attribute, -) { - if in_external_macro(cx.sess(), item_span) { - return; - } - if returns_unit(decl) { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "".into(), - Applicability::MachineApplicable, - ); - }, - ); - } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { - span_lint_and_help( - cx, - DOUBLE_MUST_USE, - fn_header_span, - "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", - None, - "either add some descriptive text or remove the attribute", - ); - } -} - -fn check_must_use_candidate<'tcx>( - cx: &LateContext<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - item_span: Span, - item_id: hir::HirId, - fn_span: Span, - msg: &str, -) { - if has_mutable_arg(cx, body) - || mutates_static(cx, body) - || in_external_macro(cx.sess(), item_span) - || returns_unit(decl) - || !cx.access_levels.is_exported(item_id) - || is_must_use_ty(cx, return_ty(cx, item_id)) - { - return; - } - span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { - if let Some(snippet) = snippet_opt(cx, fn_span) { - diag.span_suggestion( - fn_span, - "add the attribute", - format!("#[must_use] {}", snippet), - Applicability::MachineApplicable, - ); - } - }); -} - -fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { - match decl.output { - hir::FnRetTy::DefaultReturn(_) => true, - hir::FnRetTy::Return(ref ty) => match ty.kind { - hir::TyKind::Tup(ref tys) => tys.is_empty(), - hir::TyKind::Never => true, - _ => false, - }, - } -} - -fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { - let mut tys = FxHashSet::default(); - body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) -} - -fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { - if let hir::PatKind::Wild = pat.kind { - return false; // ignore `_` patterns - } - 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 - } -} - -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; - -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { - match *ty.kind() { - // primitive types are never mutable - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(ref adt, ref substs) => { - tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) - }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) - }, - // calling something constitutes a side effect, so return true on all callables - // also never calls need not be used, so return true for them, too - _ => true, - } -} - -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - - if self.mutates_static { - return; - } - match expr.kind { - Call(_, args) | MethodCall(_, _, args, _) => { - let mut tys = FxHashSet::default(); - for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { - self.mutates_static |= is_mutated_static(target) - }, - _ => {}, - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - -fn is_mutated_static(e: &hir::Expr<'_>) -> bool { - use hir::ExprKind::{Field, Index, Path}; - - match e.kind { - Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), - Path(_) => true, - Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), - _ => false, - } -} - -fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, &body.value); - v.mutates_static -} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs new file mode 100644 index 00000000000..3825699936f --- /dev/null +++ b/clippy_lints/src/functions/must_use.rs @@ -0,0 +1,272 @@ +use rustc_ast::ast::Attribute; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, def::Res, def_id::DefId, intravisit, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::{ + hir::map::Map, + lint::in_external_macro, + ty::{self, Ty}, +}; +use rustc_span::Span; + +use clippy_utils::attrs::is_proc_macro; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_must_use_ty; +use clippy_utils::{attr_by_name, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; + +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; + +pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(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 let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + return; + } else if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this function could have a `#[must_use]` attribute", + ); + } + } +} + +pub(super) fn check_impl_item(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()); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, ref eid) = 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()); + + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if let hir::TraitFn::Provided(eid) = *eid { + let body = cx.tcx.hir().body(eid); + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { + check_must_use_candidate( + cx, + &sig.decl, + body, + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } +} + +fn check_needless_must_use( + cx: &LateContext<'_>, + decl: &hir::FnDecl<'_>, + item_id: hir::HirId, + item_span: Span, + fn_header_span: Span, + attr: &Attribute, +) { + if in_external_macro(cx.sess(), item_span) { + return; + } + if returns_unit(decl) { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + diag.span_suggestion( + attr.span, + "remove the attribute", + "".into(), + Applicability::MachineApplicable, + ); + }, + ); + } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { + span_lint_and_help( + cx, + DOUBLE_MUST_USE, + fn_header_span, + "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", + None, + "either add some descriptive text or remove the attribute", + ); + } +} + +fn check_must_use_candidate<'tcx>( + cx: &LateContext<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + item_span: Span, + item_id: hir::HirId, + fn_span: Span, + msg: &str, +) { + if has_mutable_arg(cx, body) + || mutates_static(cx, body) + || in_external_macro(cx.sess(), item_span) + || returns_unit(decl) + || !cx.access_levels.is_exported(item_id) + || is_must_use_ty(cx, return_ty(cx, item_id)) + { + return; + } + span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { + if let Some(snippet) = snippet_opt(cx, fn_span) { + diag.span_suggestion( + fn_span, + "add the attribute", + format!("#[must_use] {}", snippet), + Applicability::MachineApplicable, + ); + } + }); +} + +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { + match decl.output { + hir::FnRetTy::DefaultReturn(_) => true, + hir::FnRetTy::Return(ref ty) => match ty.kind { + hir::TyKind::Tup(ref tys) => tys.is_empty(), + hir::TyKind::Never => true, + _ => false, + }, + } +} + +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { + let mut tys = FxHashSet::default(); + body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) +} + +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { + if let hir::PatKind::Wild = pat.kind { + return false; // ignore `_` patterns + } + 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 + } +} + +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; + +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { + match *ty.kind() { + // primitive types are never mutable + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, + ty::Adt(ref adt, ref substs) => { + tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + }, + // calling something constitutes a side effect, so return true on all callables + // also never calls need not be used, so return true for them, too + _ => true, + } +} + +struct StaticMutVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + mutates_static: bool, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + if self.mutates_static { + return; + } + match expr.kind { + Call(_, args) | MethodCall(_, _, args, _) => { + let mut tys = FxHashSet::default(); + for arg in args { + if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + self.cx, + self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + self.mutates_static = true; + return; + } + tys.clear(); + } + }, + Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { + self.mutates_static |= is_mutated_static(target) + }, + _ => {}, + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +fn is_mutated_static(e: &hir::Expr<'_>) -> bool { + use hir::ExprKind::{Field, Index, Path}; + + match e.kind { + Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), + Path(_) => true, + Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), + _ => false, + } +} + +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { + let mut v = StaticMutVisitor { + cx, + mutates_static: false, + }; + intravisit::walk_expr(&mut v, &body.value); + v.mutates_static +} -- cgit 1.4.1-3-g733a5 From add3e5094f83e7b36f7dfa0813be4c9d8d0706c1 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Mar 2021 22:48:25 +0900 Subject: Move result_unit_err to its own module --- clippy_lints/src/functions/mod.rs | 61 +++---------------------- clippy_lints/src/functions/result_unit_err.rs | 66 +++++++++++++++++++++++++++ clippy_lints/src/functions/too_many_lines.rs | 2 +- 3 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 clippy_lints/src/functions/result_unit_err.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index a1f4a453608..4ba5166059f 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,20 +1,14 @@ mod must_use; mod not_unsafe_ptr_arg_deref; +mod result_unit_err; mod too_many_arguments; mod too_many_lines; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; -use rustc_typeck::hir_ty_to_ty; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -250,65 +244,24 @@ impl<'tcx> LateLintPass<'tcx> for Functions { hir_id: hir::HirId, ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); - too_many_lines::check(cx, span, body, self.too_many_lines_threshold); + too_many_lines::check_fn(cx, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { must_use::check_item(cx, item); - if let hir::ItemKind::Fn(ref sig, ref _generics, _) = 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); - } - } + result_unit_err::check_item(cx, item); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { must_use::check_impl_item(cx, item); - if let hir::ImplItemKind::Fn(ref sig, _) = 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); - } - } + result_unit_err::check_impl_item(cx, item); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); must_use::check_trait_item(cx, item); - - if let hir::TraitItemKind::Fn(ref sig, _) = 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); - } - } - } -} - -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; - let ty = hir_ty_to_ty(cx.tcx, ty); - if is_type_diagnostic_item(cx, ty, sym::result_type); - if let ty::Adt(_, substs) = ty.kind(); - let err_ty = substs.type_at(1); - if err_ty.is_unit(); - then { - span_lint_and_help( - cx, - RESULT_UNIT_ERR, - fn_header_span, - "this returns a `Result<_, ()>", - None, - "use a custom Error type instead", - ); - } + result_unit_err::check_trait_item(cx, item); } } diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs new file mode 100644 index 00000000000..f71bfc690f6 --- /dev/null +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -0,0 +1,66 @@ +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_span::{sym, Span}; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use clippy_utils::ty::is_type_diagnostic_item; + +use super::RESULT_UNIT_ERR; + +pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if let hir::ItemKind::Fn(ref sig, ref _generics, _) = 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); + } + } +} + +pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + if let hir::ImplItemKind::Fn(ref sig, _) = 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); + } + } +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, _) = 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); + } + } +} + +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; + let ty = hir_ty_to_ty(cx.tcx, ty); + if is_type_diagnostic_item(cx, ty, sym::result_type); + if let ty::Adt(_, substs) = ty.kind(); + let err_ty = substs.type_at(1); + if err_ty.is_unit(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "this returns a `Result<_, ()>", + None, + "use a custom Error type instead", + ); + } + } +} diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 99d5028befc..aa5494d5a7d 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -8,7 +8,7 @@ use clippy_utils::source::snippet; use super::TOO_MANY_LINES; -pub(super) fn check(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { +pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { if in_external_macro(cx.sess(), span) { return; } -- cgit 1.4.1-3-g733a5 From e006c77d611e20692beb3f319b0fec8cb6f09dc3 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 28 Mar 2021 00:04:44 +0100 Subject: redundant_pattern_matching: look inside Refs look inside refs and detect if let &None = ... Fixes https://github.com/rust-lang/rust-clippy/issues/5396 changelog: redundant_pattern_matching: look inside Refs to fix FNs with "if let &None = .. " --- clippy_lints/src/matches.rs | 8 +++++++- tests/ui/match_ref_pats.stderr | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 3680429fed7..425124b78f4 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1723,7 +1723,13 @@ mod redundant_pattern_match { arms: &[Arm<'_>], keyword: &'static str, ) { - let good_method = match arms[0].pat.kind { + // also look inside refs + let mut kind = &arms[0].pat.kind; + // if we have &None for example, peel it so we can detect "if let None = x" + if let PatKind::Ref(inner, _mutability) = kind { + kind = &inner.kind; + } + let good_method = match 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) { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 52cb4a14b72..67474e65cde 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -46,6 +46,14 @@ LL | Some(v) => println!("{:?}", v), LL | None => println!("none"), | +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_ref_pats.rs:35:12 + | +LL | if let &None = a { + | -------^^^^^---- help: try this: `if a.is_none()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + error: you don't need to add `&` to all patterns --> $DIR/match_ref_pats.rs:35:5 | @@ -59,6 +67,12 @@ help: instead of prefixing all patterns with `&`, you can dereference the expres LL | if let None = *a { | ^^^^ ^^ +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_ref_pats.rs:40:12 + | +LL | if let &None = &b { + | -------^^^^^----- help: try this: `if b.is_none()` + error: you don't need to add `&` to both the expression and the patterns --> $DIR/match_ref_pats.rs:40:5 | @@ -87,5 +101,5 @@ LL | match *foo_variant!(0) { LL | Foo::A => println!("A"), | -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 1768efa33344022c550f3cfcd30ede168bcf3143 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 14 Mar 2021 19:35:35 +0100 Subject: Fix FP in `single_component_path_imports` lint --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 90 ++++++++++++++++++---- .../ui/single_component_path_imports_self_after.rs | 16 ++++ .../single_component_path_imports_self_before.rs | 17 ++++ 4 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 tests/ui/single_component_path_imports_self_after.rs create mode 100644 tests/ui/single_component_path_imports_self_before.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..97557fefe3e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1232,7 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box as_conversions::AsConversions); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); - store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); + store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports::default()); let max_fn_params_bools = conf.max_fn_params_bools; 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)); diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index c9d72aabb6a..ee74ef97bcd 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; use if_chain::if_chain; -use rustc_ast::{Item, ItemKind, UseTreeKind}; +use rustc_ast::{Crate, Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -35,29 +37,87 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// keep track of imports reused with `self` keyword, + /// such as `self::crypto_hash` in the example below + /// + /// ```rust,ignore + /// use self::crypto_hash::{Algorithm, Hasher}; + /// ``` + imports_reused_with_self: Vec, + /// keep track of single use statements + /// such as `crypto_hash` in the example below + /// + /// ```rust,ignore + /// use crypto_hash; + /// ``` + single_use_usages: Vec<(Symbol, Span)>, +} + +impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if !in_macro(item.span); - if cx.sess.opts.edition >= Edition::Edition2018; - if !item.vis.kind.is_pub(); - if let ItemKind::Use(use_tree) = &item.kind; - if let segments = &use_tree.prefix.segments; - if segments.len() == 1; - if let UseTreeKind::Simple(None, _, _) = use_tree.kind; - then { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if cx.sess.opts.edition < Edition::Edition2018 { + return; + } + for item in &krate.items { + self.track_uses(&item); + } + for single_use in &self.single_use_usages { + if !self.imports_reused_with_self.contains(&single_use.0) { span_lint_and_sugg( cx, SINGLE_COMPONENT_PATH_IMPORTS, - item.span, + single_use.1, "this import is redundant", "remove it entirely", String::new(), - Applicability::MachineApplicable + Applicability::MachineApplicable, ); } } } } + +impl SingleComponentPathImports { + fn track_uses(&mut self, item: &Item) { + if_chain! { + if !in_macro(item.span); + if !item.vis.kind.is_pub(); + if let ItemKind::Use(use_tree) = &item.kind; + if let segments = &use_tree.prefix.segments; + + then { + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let ident = &segments[0].ident; + self.single_use_usages.push((ident.name, item.span)); + } + return; + } + + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + self.imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + self.imports_reused_with_self.push(segments[0].ident.name); + } + } + } + } + } + } + } +} diff --git a/tests/ui/single_component_path_imports_self_after.rs b/tests/ui/single_component_path_imports_self_after.rs new file mode 100644 index 00000000000..94319ade0ac --- /dev/null +++ b/tests/ui/single_component_path_imports_self_after.rs @@ -0,0 +1,16 @@ +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use self::regex::{Regex as xeger, RegexSet as tesxeger}; +pub use self::{ + regex::{Regex, RegexSet}, + some_mod::SomeType, +}; +use regex; + +mod some_mod { + pub struct SomeType; +} + +fn main() {} diff --git a/tests/ui/single_component_path_imports_self_before.rs b/tests/ui/single_component_path_imports_self_before.rs new file mode 100644 index 00000000000..c7437b23456 --- /dev/null +++ b/tests/ui/single_component_path_imports_self_before.rs @@ -0,0 +1,17 @@ +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use regex; + +use self::regex::{Regex as xeger, RegexSet as tesxeger}; +pub use self::{ + regex::{Regex, RegexSet}, + some_mod::SomeType, +}; + +mod some_mod { + pub struct SomeType; +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 6985d13a9783bb7181b6c43e83465e7630e48ec2 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 28 Mar 2021 09:35:44 +0200 Subject: Take into account sub modules --- clippy_lints/src/single_component_path_imports.rs | 24 ++++++++++++++--------- tests/ui/single_component_path_imports.fixed | 6 ++++++ tests/ui/single_component_path_imports.rs | 6 ++++++ tests/ui/single_component_path_imports.stderr | 8 +++++++- 4 files changed, 34 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index ee74ef97bcd..901f7642a12 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; -use if_chain::if_chain; -use rustc_ast::{Crate, Item, ItemKind, UseTreeKind}; +use rustc_ast::{Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -83,13 +82,19 @@ impl EarlyLintPass for SingleComponentPathImports { impl SingleComponentPathImports { fn track_uses(&mut self, item: &Item) { - if_chain! { - if !in_macro(item.span); - if !item.vis.kind.is_pub(); - if let ItemKind::Use(use_tree) = &item.kind; - if let segments = &use_tree.prefix.segments; + if in_macro(item.span) || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + for item in items.iter() { + self.track_uses(&item); + } + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; - then { // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { @@ -117,7 +122,8 @@ impl SingleComponentPathImports { } } } - } + }, + _ => {}, } } } diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index a7a8499b58f..226d2b315d7 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -19,3 +19,9 @@ fn main() { // False positive #5154, shouldn't trigger lint. m!(); } + +mod hello_mod { + + #[allow(dead_code)] + fn hello_mod() {} +} diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index 9a427e90ad3..88bf7f1fc5a 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -19,3 +19,9 @@ fn main() { // False positive #5154, shouldn't trigger lint. m!(); } + +mod hello_mod { + use regex; + #[allow(dead_code)] + fn hello_mod() {} +} diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 519ada0169a..646e6d3647a 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -6,5 +6,11 @@ LL | use regex; | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` -error: aborting due to previous error +error: this import is redundant + --> $DIR/single_component_path_imports.rs:24:5 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 879fa5c9721c89c27be6a9db5f51d935a51f549b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 27 Mar 2021 21:48:10 -0400 Subject: Improve `expl_impl_clone_on_copy` Check to see if the generic constraints are the same as if using derive --- clippy_lints/src/derive.rs | 87 ++++++++++++++++++++++++---------------------- tests/ui/derive.rs | 21 +++++++++-- tests/ui/derive.stderr | 30 +++++++++++++--- 3 files changed, 89 insertions(+), 49 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 834136f910d..9fad85f8ef4 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::paths; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path}; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{def_id::LOCAL_CRATE, source_map::Span}; declare_clippy_lint! { /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq` @@ -293,48 +293,53 @@ fn check_ord_partial_ord<'tcx>( /// 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 cx - .tcx - .lang_items() - .clone_trait() - .map_or(false, |id| Some(id) == trait_ref.trait_def_id()) - { - if !is_copy(cx, ty) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let copy_id = match cx.tcx.lang_items().copy_trait() { + Some(id) => id, + None => return, + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx + .tcx + .all_local_trait_impls(LOCAL_CRATE) + .get(©_id) + .map_or(false, |impls| { + impls + .iter() + .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did)) + }); + if !has_copy_impl { + return; + } + } else { return; } - - match *ty.kind() { - ty::Adt(def, _) if def.is_union() => return, - - // Some types are not Clone by default but could be cloned “by hand” if necessary - ty::Adt(def, substs) => { - for variant in &def.variants { - for field in &variant.fields { - if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() { - return; - } - } - for subst in substs { - if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() { - if let ty::Param(_) = subst.kind() { - return; - } - } - } - } - }, - _ => (), - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); } /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 8fcb0e8b28d..4e46bf13991 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -35,7 +35,6 @@ impl<'a> Clone for Lt<'a> { } } -// Ok, `Clone` cannot be derived because of the big array #[derive(Copy)] struct BigArray { a: [u8; 65], @@ -47,7 +46,6 @@ impl Clone for BigArray { } } -// Ok, function pointers are not always Clone #[derive(Copy)] struct FnPtr { a: fn() -> !, @@ -59,7 +57,7 @@ impl Clone for FnPtr { } } -// Ok, generics +// Ok, Clone trait impl doesn't have constrained generics. #[derive(Copy)] struct Generic { a: T, @@ -71,4 +69,21 @@ impl Clone for Generic { } } +#[derive(Copy)] +struct Generic2(T); +impl Clone for Generic2 { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +// Ok, Clone trait impl doesn't have constrained generics. +#[derive(Copy)] +struct GenericRef<'a, T, U>(T, &'a U); +impl Clone for GenericRef<'_, T, U> { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1) + } +} + fn main() {} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 1328a9b3107..82a70ceecc3 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -40,7 +40,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:44:1 + --> $DIR/derive.rs:43:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -50,7 +50,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:44:1 + --> $DIR/derive.rs:43:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -60,7 +60,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:56:1 + --> $DIR/derive.rs:54:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -70,7 +70,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:56:1 + --> $DIR/derive.rs:54:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -79,5 +79,25 @@ LL | | } LL | | } | |_^ -error: aborting due to 4 previous errors +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:74:1 + | +LL | / impl Clone for Generic2 { +LL | | fn clone(&self) -> Self { +LL | | Self(self.0.clone()) +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:74:1 + | +LL | / impl Clone for Generic2 { +LL | | fn clone(&self) -> Self { +LL | | Self(self.0.clone()) +LL | | } +LL | | } + | |_^ + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 31afdfc12bbe1bd2681ac17bfb07134ffd3cf061 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 29 Mar 2021 17:19:05 +0900 Subject: missing_panics_doc: Ignore usage of debug_assert family --- clippy_lints/src/doc.rs | 6 ++++++ tests/ui/missing_panics_doc.rs | 8 ++++++++ 2 files changed, 14 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 14338ac8faf..69800f9d331 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -715,6 +715,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { if let Some(path_def_id) = path.res.opt_def_id(); if match_panic_def_id(self.cx, path_def_id); if is_expn_of(expr.span, "unreachable").is_none(); + if !is_expn_of_debug_assertions(expr.span); then { self.panic_span = Some(expr.span); } @@ -738,3 +739,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } + +fn is_expn_of_debug_assertions(span: Span) -> bool { + const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"]; + MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some()) +} diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 17e72353f80..3fe35c75799 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -112,3 +112,11 @@ fn inner_body_private(opt: Option) { pub fn unreachable() { unreachable!("This function panics") } + +/// #6970. +/// This is okay because it is expansion of `debug_assert` family. +pub fn debug_assertions() { + debug_assert!(false); + debug_assert_eq!(1, 2); + debug_assert_ne!(1, 2); +} -- cgit 1.4.1-3-g733a5 From d2657769a22f15098343f9272f52a01145698786 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 29 Mar 2021 13:29:58 -0400 Subject: Improve `clone_on_copy` Lint on `_.clone().method()` when method takes self by value Set applicability correctly Correct suggestion when the cloned value is a macro call. e.g. `m!(x).clone()` Don't lint when not using the `Clone` trait --- clippy_lints/src/methods/clone_on_copy.rs | 118 +++++++++++++++++------------- tests/ui/clone_on_copy.fixed | 34 ++++++++- tests/ui/clone_on_copy.rs | 34 ++++++++- tests/ui/clone_on_copy.stderr | 30 ++++++-- tests/ui/clone_on_copy_mut.rs | 18 ----- 5 files changed, 157 insertions(+), 77 deletions(-) delete mode 100644 tests/ui/clone_on_copy_mut.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index edb6649b87b..ce2e8fa8b10 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -1,10 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::get_parent_node; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg; use clippy_utils::ty::is_copy; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, adjustment::Adjust}; use rustc_span::symbol::{sym, Symbol}; use std::iter; @@ -12,12 +14,26 @@ use super::CLONE_DOUBLE_REF; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { - if !(args.len() == 1 && method_name == sym::clone) { +#[allow(clippy::too_many_lines)] +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) { + let arg = match args { + [arg] if method_name == sym::clone => arg, + _ => return, + }; + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|id| cx.tcx.trait_of_item(id)) + .zip(cx.tcx.lang_items().clone_trait()) + .map_or(true, |(x, y)| x != y) + { return; } - let arg = &args[0]; - let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); + let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_ty = arg_adjustments + .last() + .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() { if let ty::Ref(_, innermost, _) = inner.kind() { @@ -61,57 +77,57 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Sym } if is_copy(cx, ty) { - let snip; - if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { - let parent = cx.tcx.hir().get_parent_node(expr.hir_id); - match &cx.tcx.hir().get(parent) { - hir::Node::Expr(parent) => match parent.kind { - // &*x is a nop, &x.clone() is not - hir::ExprKind::AddrOf(..) => return, - // (*x).func() is useless, x.clone().func() can work in case func borrows mutably - hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { - return; - }, - - _ => {}, - }, - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Local(ref loc) = stmt.kind { - if let hir::PatKind::Ref(..) = loc.pat.kind { - // let ref y = *x borrows x, let ref y = x.clone() does not - return; - } - } - }, - _ => {}, + let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Expr(parent)) => match parent.kind { + // &*x is a nop, &x.clone() is not + ExprKind::AddrOf(..) => return, + // (*x).func() is useless, x.clone().func() can work in case func borrows self + ExprKind::MethodCall(_, _, [self_arg, ..], _) + if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) => + { + return; + } + ExprKind::MethodCall(_, _, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true, + ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) => true, + _ => false, + }, + // local binding capturing a reference + Some(Node::Local(l)) + if matches!( + l.pat.kind, + PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) + ) => + { + return; } + _ => false, + }; - // x.clone() might have dereferenced x, possibly through Deref impls - if cx.typeck_results().expr_ty(arg) == ty { - snip = Some(("try removing the `clone` call", format!("{}", snippet))); - } else { - let deref_count = cx - .typeck_results() - .expr_adjustments(arg) - .iter() - .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))); - } + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + + let deref_count = arg_adjustments + .iter() + .take_while(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + let (help, sugg) = if deref_count == 0 { + ("try removing the `clone` call", snip.into()) + } else if parent_is_suffix_expr { + ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) } else { - snip = None; - } - span_lint_and_then( + ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) + }; + + span_lint_and_sugg( cx, CLONE_ON_COPY, expr.span, &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), - |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }, + help, + sugg, + app, ); } } diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index d924625132e..8d43f64768d 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -6,7 +6,8 @@ clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push + clippy::vec_init_then_push, + clippy::toplevel_ref_arg )] use std::cell::RefCell; @@ -29,6 +30,37 @@ fn clone_on_copy() { let rc = RefCell::new(0); *rc.borrow(); + let x = 0u32; + x.rotate_left(1); + + #[derive(Clone, Copy)] + struct Foo; + impl Foo { + fn clone(&self) -> u32 { + 0 + } + } + Foo.clone(); // ok, this is not the clone trait + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + m!(42); + + struct Wrap([u32; 2]); + impl core::ops::Deref for Wrap { + type Target = [u32; 2]; + fn deref(&self) -> &[u32; 2] { + &self.0 + } + } + let x = Wrap([0, 0]); + (*x)[0]; + + let x = 42; + let ref y = x.clone(); // ok, binds by reference + let ref mut y = x.clone(); // ok, binds by reference + // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index 97f49467244..f15501f7184 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -6,7 +6,8 @@ clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push + clippy::vec_init_then_push, + clippy::toplevel_ref_arg )] use std::cell::RefCell; @@ -29,6 +30,37 @@ fn clone_on_copy() { let rc = RefCell::new(0); rc.borrow().clone(); + let x = 0u32; + x.clone().rotate_left(1); + + #[derive(Clone, Copy)] + struct Foo; + impl Foo { + fn clone(&self) -> u32 { + 0 + } + } + Foo.clone(); // ok, this is not the clone trait + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + m!(42).clone(); + + struct Wrap([u32; 2]); + impl core::ops::Deref for Wrap { + type Target = [u32; 2]; + fn deref(&self) -> &[u32; 2] { + &self.0 + } + } + let x = Wrap([0, 0]); + x.clone()[0]; + + let x = 42; + let ref y = x.clone(); // ok, binds by reference + let ref mut y = x.clone(); // ok, binds by reference + // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 7a706884fb0..e7d28b4320b 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:23:5 + --> $DIR/clone_on_copy.rs:24:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,28 +7,46 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:27:5 + --> $DIR/clone_on_copy.rs:28:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:30:5 + --> $DIR/clone_on_copy.rs:31:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` +error: using `clone` on type `u32` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:34:5 + | +LL | x.clone().rotate_left(1); + | ^^^^^^^^^ help: try removing the `clone` call: `x` + +error: using `clone` on type `i32` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:48:5 + | +LL | m!(42).clone(); + | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` + +error: using `clone` on type `[u32; 2]` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:58:5 + | +LL | x.clone()[0]; + | ^^^^^^^^^ help: try dereferencing it: `(*x)` + error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:36:14 + --> $DIR/clone_on_copy.rs:68:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:40:14 + --> $DIR/clone_on_copy.rs:72:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/clone_on_copy_mut.rs b/tests/ui/clone_on_copy_mut.rs deleted file mode 100644 index 5bfa256623b..00000000000 --- a/tests/ui/clone_on_copy_mut.rs +++ /dev/null @@ -1,18 +0,0 @@ -pub fn dec_read_dec(i: &mut i32) -> i32 { - *i -= 1; - let ret = *i; - *i -= 1; - ret -} - -pub fn minus_1(i: &i32) -> i32 { - dec_read_dec(&mut i.clone()) -} - -fn main() { - let mut i = 10; - assert_eq!(minus_1(&i), 9); - assert_eq!(i, 10); - assert_eq!(dec_read_dec(&mut i), 9); - assert_eq!(i, 8); -} -- cgit 1.4.1-3-g733a5 From 541c8b8f69b1b14fe3de272d5fad4323cfd1ae0c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 30 Mar 2021 11:45:54 +0900 Subject: Improve documents in functions group --- clippy_lints/src/functions/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 4ba5166059f..2beb9bc94bf 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for public functions that dereference raw pointer - /// arguments but are not marked unsafe. + /// arguments but are not marked `unsafe`. /// /// **Why is this bad?** The function should probably be marked `unsafe`, since /// for an arbitrary raw pointer, there is no way of telling for sure if it is @@ -165,7 +165,7 @@ declare_clippy_lint! { 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`]. + /// implements `std::error::Error`. /// /// **Why is this bad?** Unit does not implement `Error` and carries no /// further information about what went wrong. -- cgit 1.4.1-3-g733a5 From 6966c78be74ec7dea5cbbb18f1ec10771bf4b728 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 29 Mar 2021 12:51:23 -0700 Subject: wrong_self_convention: fix FP inside trait impl for `to_*` method When the `to_*` method takes `&self` and it is a trait implementation, we don't trigger the lint. --- clippy_lints/src/methods/mod.rs | 10 ++++++- clippy_lints/src/methods/wrong_self_convention.rs | 32 ++++++++++++++++++----- tests/ui/wrong_self_convention.rs | 9 ++----- tests/ui/wrong_self_convention.stderr | 4 +-- tests/ui/wrong_self_convention2.rs | 32 +++++++++++++++++++++++ tests/ui/wrong_self_convention2.stderr | 11 ++++++++ 6 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 tests/ui/wrong_self_convention2.rs create mode 100644 tests/ui/wrong_self_convention2.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fccdee07877..95592523928 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -205,6 +205,13 @@ declare_clippy_lint! { /// |`to_` | not `_mut` |`self` | `Copy` | /// |`to_` | not `_mut` |`&self` | not `Copy` | /// + /// Note: Clippy doesn't trigger methods with `to_` prefix in: + /// - Traits definition. + /// Clippy can not tell if a type that implements a trait is `Copy` or not. + /// - Traits implementation, when `&self` is taken. + /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait + /// (see e.g. the `std::string::ToString` trait). + /// /// Please find more info here: /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv /// @@ -1850,7 +1857,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.tcx.type_of(item.def_id); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); - if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); @@ -1902,6 +1908,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self_ty, first_arg_ty, first_arg.pat.span, + implements_trait, false ); } @@ -1972,6 +1979,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self_ty, first_arg_ty, first_arg_span, + false, true ); } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 59e683aa9a7..1e0de249a91 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -21,8 +21,10 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), + Convention::IsTraitItem(false)], &[SelfKind::Ref]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), + Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; enum Convention { @@ -32,18 +34,27 @@ enum Convention { NotEndsWith(&'static str), IsSelfTypeCopy(bool), ImplementsTrait(bool), + IsTraitItem(bool), } impl Convention { #[must_use] - fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool { + fn check<'tcx>( + &self, + cx: &LateContext<'tcx>, + self_ty: &'tcx TyS<'tcx>, + other: &str, + implements_trait: bool, + is_trait_item: bool, + ) -> bool { match *self { Self::Eq(this) => this == other, Self::StartsWith(this) => other.starts_with(this) && this != other, Self::EndsWith(this) => other.ends_with(this) && this != other, - Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def), + Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item), Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty), - Self::ImplementsTrait(is_true) => is_true == is_trait_def, + Self::ImplementsTrait(is_true) => is_true == implements_trait, + Self::IsTraitItem(is_true) => is_true == is_trait_item, } } } @@ -60,12 +71,17 @@ impl fmt::Display for Convention { }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("Method{} implement{} a trait", negation, s_suffix).fmt(f) + format!("method{} implement{} a trait", negation, s_suffix).fmt(f) + }, + Self::IsTraitItem(is_true) => { + let suffix = if is_true { " is" } else { " is not" }; + format!("method{} a trait item", suffix).fmt(f) }, } } } +#[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: &str, @@ -73,6 +89,7 @@ pub(super) fn check<'tcx>( self_ty: &'tcx TyS<'tcx>, first_arg_ty: &'tcx TyS<'tcx>, first_arg_span: Span, + implements_trait: bool, is_trait_item: bool, ) { let lint = if is_pub { @@ -83,7 +100,7 @@ pub(super) fn check<'tcx>( if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() - .all(|conv| conv.check(cx, self_ty, item_name, is_trait_item)) + .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) }) { if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { @@ -99,6 +116,7 @@ pub(super) fn check<'tcx>( .filter_map(|conv| { if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) || matches!(conv, Convention::ImplementsTrait(_)) + || matches!(conv, Convention::IsTraitItem(_)) { None } else { diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index ba9e19a1722..cdfbdb8b0db 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -165,15 +165,10 @@ mod issue6307 { } mod issue6727 { - trait ToU64 { - fn to_u64(self) -> u64; - fn to_u64_v2(&self) -> u64; - } - #[derive(Clone, Copy)] struct FooCopy; - impl ToU64 for FooCopy { + impl FooCopy { fn to_u64(self) -> u64 { 1 } @@ -185,7 +180,7 @@ mod issue6727 { struct FooNoCopy; - impl ToU64 for FooNoCopy { + impl FooNoCopy { // trigger lint fn to_u64(self) -> u64 { 2 diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 1d58a12ac79..29f5ba82695 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -176,7 +176,7 @@ LL | fn from_i32(self); = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value - --> $DIR/wrong_self_convention.rs:181:22 + --> $DIR/wrong_self_convention.rs:176:22 | LL | fn to_u64_v2(&self) -> u64 { | ^^^^^ @@ -184,7 +184,7 @@ LL | fn to_u64_v2(&self) -> u64 { = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:190:19 + --> $DIR/wrong_self_convention.rs:185:19 | LL | fn to_u64(self) -> u64 { | ^^^^ diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs new file mode 100644 index 00000000000..8b42aa59e13 --- /dev/null +++ b/tests/ui/wrong_self_convention2.rs @@ -0,0 +1,32 @@ +// edition:2018 +#![warn(clippy::wrong_self_convention)] +#![warn(clippy::wrong_pub_self_convention)] +#![allow(dead_code)] + +fn main() {} + +mod issue6983 { + pub struct Thing; + pub trait Trait { + fn to_thing(&self) -> Thing; + } + + impl Trait for u8 { + // don't trigger, e.g. `ToString` from `std` requires `&self` + fn to_thing(&self) -> Thing { + Thing + } + } + + trait ToU64 { + fn to_u64(self) -> u64; + } + + struct FooNoCopy; + // trigger lint + impl ToU64 for FooNoCopy { + fn to_u64(self) -> u64 { + 2 + } + } +} diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr new file mode 100644 index 00000000000..0ca1a390974 --- /dev/null +++ b/tests/ui/wrong_self_convention2.stderr @@ -0,0 +1,11 @@ +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference + --> $DIR/wrong_self_convention2.rs:28:19 + | +LL | fn to_u64(self) -> u64 { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From fa689f865e4096a1788c38fe9fef0c05dc15b9be Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 29 Mar 2021 20:39:28 -0400 Subject: Fix `manual_map` at the end of an if chain --- clippy_lints/src/manual_map.rs | 5 ++-- clippy_utils/src/lib.rs | 23 ++++++++++------ tests/ui/manual_map_option.fixed | 7 ++++- tests/ui/manual_map_option.rs | 9 +++++++ tests/ui/manual_map_option.stderr | 55 +++++++++++++++++++++------------------ 5 files changed, 61 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index ed157783b72..d6ef3aa1e77 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,7 +2,7 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{is_allowed, is_else_clause_of_if_let_else, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ @@ -181,8 +181,7 @@ impl LateLintPass<'_> for ManualMap { expr.span, "manual implementation of `Option::map`", "try this", - if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause_of_if_let_else(cx.tcx, expr) - { + if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) { format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) } else { format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9e4dcb600ed..d6364625e70 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -797,22 +797,29 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { } } -/// Checks if the given expression is the else clause in the expression `if let .. {} else {}` -pub fn is_else_clause_of_if_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { +/// Checks if the given expression is the else clause of either an `if` or `if let` expression. +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let map = tcx.hir(); let mut iter = map.parent_iter(expr.hir_id); - let arm_id = match iter.next() { - Some((id, Node::Arm(..))) => id, - _ => return false, - }; match iter.next() { + Some((arm_id, Node::Arm(..))) => matches!( + iter.next(), + Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }), + .. + }) + )) + if else_arm.hir_id == arm_id + ), Some(( _, Node::Expr(Expr { - kind: ExprKind::Match(_, [_, else_arm], kind), + kind: ExprKind::If(_, _, Some(else_expr)), .. }), - )) => else_arm.hir_id == arm_id && matches!(kind, MatchSource::IfLetDesugar { .. }), + )) => else_expr.hir_id == expr.hir_id, _ => false, } } diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index acb6a580ceb..5e26958041d 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,6 +7,7 @@ clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats, + clippy::redundant_pattern_matching, dead_code )] @@ -130,7 +131,11 @@ fn main() { } // #6847 - if Some(0).is_some() { + if let Some(_) = Some(0) { + Some(0) + } else { Some(0).map(|x| x + 1) }; + + if true { Some(0) } else { Some(0).map(|x| x + 1) }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 3299e617707..33eb8156105 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,6 +7,7 @@ clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats, + clippy::redundant_pattern_matching, dead_code )] @@ -195,4 +196,12 @@ fn main() { } else { None }; + + if true { + Some(0) + } else if let Some(x) = Some(0) { + Some(x + 1) + } else { + None + }; } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 048ccfb9582..cdc2c0e62a9 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:14:5 + --> $DIR/manual_map_option.rs:15:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:19:5 + --> $DIR/manual_map_option.rs:20:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:24:5 + --> $DIR/manual_map_option.rs:25:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:29:5 + --> $DIR/manual_map_option.rs:30:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:36:5 + --> $DIR/manual_map_option.rs:37:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:41:5 + --> $DIR/manual_map_option.rs:42:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:51:5 + --> $DIR/manual_map_option.rs:52:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:56:5 + --> $DIR/manual_map_option.rs:57:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:61:5 + --> $DIR/manual_map_option.rs:62:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:66:5 + --> $DIR/manual_map_option.rs:67:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:71:5 + --> $DIR/manual_map_option.rs:72:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:84:9 + --> $DIR/manual_map_option.rs:85:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:90:5 + --> $DIR/manual_map_option.rs:91:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:95:5 + --> $DIR/manual_map_option.rs:96:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:100:5 + --> $DIR/manual_map_option.rs:101:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:105:5 + --> $DIR/manual_map_option.rs:106:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:110:5 + --> $DIR/manual_map_option.rs:111:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -155,7 +155,7 @@ LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:168:5 + --> $DIR/manual_map_option.rs:169:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -164,7 +164,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:173:5 + --> $DIR/manual_map_option.rs:174:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -172,16 +172,19 @@ LL | | None => None, LL | | }; | |_____^ help: try this: `option_env!("").map(String::from)` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/manual_map_option.rs:191:12 - | -LL | if let Some(_) = Some(0) { - | -------^^^^^^^---------- help: try this: `if Some(0).is_some()` +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:194:12 | - = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` +LL | } else if let Some(x) = Some(0) { + | ____________^ +LL | | Some(x + 1) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:193:12 + --> $DIR/manual_map_option.rs:202:12 | LL | } else if let Some(x) = Some(0) { | ____________^ -- cgit 1.4.1-3-g733a5 From cc7f1daab2e8104d8ae5952e28f22dcac920d244 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 22:48:27 -0400 Subject: Don't lint `manual_map` in const functions --- clippy_lints/src/manual_map.rs | 10 +++++----- tests/ui/manual_map_option.fixed | 8 ++++++++ tests/ui/manual_map_option.rs | 8 ++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index d6ef3aa1e77..8c9e3af62f4 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,7 +2,7 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ @@ -47,16 +47,16 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - if let ExprKind::Match( scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], match_kind, ) = expr.kind { + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { + return; + } + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 5e26958041d..ee015845777 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -138,4 +138,12 @@ fn main() { if true { Some(0) } else { Some(0).map(|x| x + 1) }; + + // #6967 + const fn f4() { + match Some(0) { + Some(x) => Some(x + 1), + None => None, + }; + } } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 33eb8156105..29509bddfd9 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -204,4 +204,12 @@ fn main() { } else { None }; + + // #6967 + const fn f4() { + match Some(0) { + Some(x) => Some(x + 1), + None => None, + }; + } } -- cgit 1.4.1-3-g733a5 From 7fcd15571201d55951cf518a564069ff3ed115aa Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Mon, 29 Mar 2021 11:18:59 -0700 Subject: Add non_octal_unix_permissions lint --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/non_octal_unix_permissions.rs | 106 +++++++++++++++++++++++++ clippy_utils/src/paths.rs | 3 + tests/ui/non_octal_unix_permissions.fixed | 33 ++++++++ tests/ui/non_octal_unix_permissions.rs | 33 ++++++++ tests/ui/non_octal_unix_permissions.stderr | 28 +++++++ 8 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/non_octal_unix_permissions.rs create mode 100644 tests/ui/non_octal_unix_permissions.fixed create mode 100644 tests/ui/non_octal_unix_permissions.rs create mode 100644 tests/ui/non_octal_unix_permissions.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 57996802182..681ecd6832a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2382,6 +2382,7 @@ Released 2018-09-13 [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref diff --git a/README.md b/README.md index 63057609bb6..8c0c16c443d 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 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..237c0f48881 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -297,6 +297,7 @@ mod new_without_default; mod no_effect; mod non_copy_const; mod non_expressive_names; +mod non_octal_unix_permissions; mod open_options; mod option_env_unwrap; mod option_if_let_else; @@ -873,6 +874,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, &non_expressive_names::MANY_SINGLE_CHAR_NAMES, &non_expressive_names::SIMILAR_NAMES, + &non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, &open_options::NONSENSICAL_OPEN_OPTIONS, &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, @@ -1057,6 +1059,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 default_numeric_fallback::DefaultNumericFallback); store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); + store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1647,6 +1650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), @@ -1987,6 +1991,7 @@ 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_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), 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_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs new file mode 100644 index 00000000000..6d45e7bc6cf --- /dev/null +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -0,0 +1,106 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::ty::match_type; +use clippy_utils::{match_def_path, paths}; +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:** Checks for non-octal values used to set Unix file permissions. + /// + /// **Why is this bad?** They will be converted into octal, creating potentially + /// unintended file permissions. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(644); + /// ``` + /// Use instead: + /// ```rust,ignore + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); + /// ``` + pub NON_OCTAL_UNIX_PERMISSIONS, + correctness, + "use of non-octal value to set unix file permissions, which will be translated into octal" +} + +declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); + +impl LateLintPass<'_> for NonOctalUnixPermissions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::MethodCall(path, _, [func, param], _) => { + let obj_ty = cx.typeck_results().expr_ty(&func).peel_refs(); + + if_chain! { + if (path.ident.name == sym!(mode) + && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS) + || match_type(cx, obj_ty, &paths::DIR_BUILDER))) + || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); + if let ExprKind::Lit(_) = param.kind; + + then { + let snip = match snippet_opt(cx, param.span) { + Some(s) => s, + _ => return, + }; + + if !snip.starts_with("0o") { + show_error(cx, param); + } + } + } + }, + ExprKind::Call(ref func, [param]) => { + if_chain! { + if let ExprKind::Path(ref path) = func.kind; + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); + if let ExprKind::Lit(_) = param.kind; + + then { + let snip = match snippet_opt(cx, param.span) { + Some(s) => s, + _ => return, + }; + + if !snip.starts_with("0o") { + show_error(cx, param); + } + } + } + }, + _ => {}, + }; + } +} + +fn show_error(cx: &LateContext<'_>, param: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NON_OCTAL_UNIX_PERMISSIONS, + param.span, + "using a non-octal value to set unix file permissions", + "consider using an octal literal instead", + format!( + "0o{}", + snippet_with_applicability(cx, param.span, "0o..", &mut applicability,), + ), + applicability, + ); +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 11a446e42a4..7c83a9fe4e2 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -26,6 +26,7 @@ pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; @@ -95,6 +96,8 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri 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 PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; +pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; diff --git a/tests/ui/non_octal_unix_permissions.fixed b/tests/ui/non_octal_unix_permissions.fixed new file mode 100644 index 00000000000..a9b2dcfb085 --- /dev/null +++ b/tests/ui/non_octal_unix_permissions.fixed @@ -0,0 +1,33 @@ +// ignore-windows +// run-rustfix +#![warn(clippy::non_octal_unix_permissions)] +use std::fs::{DirBuilder, File, OpenOptions, Permissions}; +use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt}; + +fn main() { + let permissions = 0o760; + + // OpenOptionsExt::mode + let mut options = OpenOptions::new(); + options.mode(0o440); + options.mode(0o400); + options.mode(permissions); + + // PermissionsExt::from_mode + let _permissions = Permissions::from_mode(0o647); + let _permissions = Permissions::from_mode(0o000); + let _permissions = Permissions::from_mode(permissions); + + // PermissionsExt::set_mode + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(0o644); + permissions.set_mode(0o704); + + // DirBuilderExt::mode + let mut builder = DirBuilder::new(); + builder.mode(0o755); + builder.mode(0o406); +} diff --git a/tests/ui/non_octal_unix_permissions.rs b/tests/ui/non_octal_unix_permissions.rs new file mode 100644 index 00000000000..7d2922f494e --- /dev/null +++ b/tests/ui/non_octal_unix_permissions.rs @@ -0,0 +1,33 @@ +// ignore-windows +// run-rustfix +#![warn(clippy::non_octal_unix_permissions)] +use std::fs::{DirBuilder, File, OpenOptions, Permissions}; +use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt}; + +fn main() { + let permissions = 0o760; + + // OpenOptionsExt::mode + let mut options = OpenOptions::new(); + options.mode(440); + options.mode(0o400); + options.mode(permissions); + + // PermissionsExt::from_mode + let _permissions = Permissions::from_mode(647); + let _permissions = Permissions::from_mode(0o000); + let _permissions = Permissions::from_mode(permissions); + + // PermissionsExt::set_mode + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(644); + permissions.set_mode(0o704); + + // DirBuilderExt::mode + let mut builder = DirBuilder::new(); + builder.mode(755); + builder.mode(0o406); +} diff --git a/tests/ui/non_octal_unix_permissions.stderr b/tests/ui/non_octal_unix_permissions.stderr new file mode 100644 index 00000000000..32845d06594 --- /dev/null +++ b/tests/ui/non_octal_unix_permissions.stderr @@ -0,0 +1,28 @@ +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:12:18 + | +LL | options.mode(440); + | ^^^ help: consider using an octal literal instead: `0o440` + | + = note: `-D clippy::non-octal-unix-permissions` implied by `-D warnings` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:17:47 + | +LL | let _permissions = Permissions::from_mode(647); + | ^^^ help: consider using an octal literal instead: `0o647` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:26:26 + | +LL | permissions.set_mode(644); + | ^^^ help: consider using an octal literal instead: `0o644` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:31:18 + | +LL | builder.mode(755); + | ^^^ help: consider using an octal literal instead: `0o755` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 45164de59fb09d8aec57ffe87a7ea7619daa2781 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 31 Mar 2021 11:18:48 +0900 Subject: result_unit_err: Fix typo --- clippy_lints/src/functions/result_unit_err.rs | 4 ++-- tests/ui/len_without_is_empty.stderr | 16 ++++++++-------- tests/ui/result_unit_error.stderr | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index f71bfc690f6..3d1092ce21f 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -57,9 +57,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/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index ff7f5562308..3282709bcd6 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -86,38 +86,38 @@ LL | pub fn is_empty(&self) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature: `(&self) -> bool` or `(&self) -> Result -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/len_without_is_empty.rs:230:5 | LL | pub fn len(&self) -> 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/len_without_is_empty.rs:242:5 | LL | pub fn len(&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/len_without_is_empty.rs:246:5 | LL | pub fn is_empty(&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/len_without_is_empty.rs:253:5 | LL | pub fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead error: aborting due to 12 previous errors diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 41d8b0a7cb7..8c7573eabda 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,43 +1,43 @@ -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:3: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:12: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:14: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:32:5 | LL | pub fn unit_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:41:5 | LL | pub fn should_lint() -> ResInv<(), usize> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 8abab5561caf0243799fba29a53e175948977ec2 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sat, 27 Mar 2021 08:50:19 +0200 Subject: Fix hidden variant suggestion on single variant Fixes #6984 --- clippy_lints/src/lib.rs | 1 - clippy_lints/src/manual_non_exhaustive.rs | 14 +++----------- clippy_lints/src/matches.rs | 7 ++++++- clippy_lints/src/missing_doc.rs | 11 ++--------- clippy_utils/src/attrs.rs | 12 +++++++++++- tests/ui/match_wildcard_for_single_variants.fixed | 16 ++++++++++++++++ tests/ui/match_wildcard_for_single_variants.rs | 16 ++++++++++++++++ 7 files changed, 54 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 153972671fd..c060429edea 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -21,7 +21,6 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 95261580b8e..7f7aeaf2be8 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,9 +1,9 @@ +use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::meets_msrv; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; -use rustc_attr as attr; +use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_semver::RustcVersion; @@ -102,15 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants 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.has_name(sym::doc) - && match attr.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym::hidden), - None => false, - } + && is_doc_hidden(&variant.attrs) } if_chain! { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 425124b78f4..052bbb48887 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -983,6 +983,11 @@ impl CommonPrefixSearcher<'a> { } } +fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + let attrs = cx.tcx.get_attrs(variant_def.def_id); + clippy_utils::attrs::is_doc_hidden(attrs) +} + #[allow(clippy::too_many_lines)] fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); @@ -1102,7 +1107,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index ff87828c2e7..d731a9fe223 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -5,10 +5,10 @@ // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // +use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; -use rustc_ast::attr; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; @@ -111,14 +111,7 @@ impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { - let doc_hidden = self.doc_hidden() - || attrs.iter().any(|attr| { - attr.has_name(sym::doc) - && match attr.meta_item_list() { - None => false, - Some(l) => attr::list_contains_name(&l[..], sym::hidden), - } - }); + let doc_hidden = self.doc_hidden() || is_doc_hidden(attrs); self.doc_hidden_stack.push(doc_hidden); } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8d28421d70d..7ec8452bf4c 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast; +use rustc_ast::{ast, attr}; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::sym; @@ -148,3 +148,13 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { attrs.iter().any(|attr| sess.is_proc_macro_attr(attr)) } + +/// Return true if the attributes contain `#[doc(hidden)]` +pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { + #[allow(clippy::filter_map)] + attrs + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(ast::Attribute::meta_item_list) + .any(|l| attr::list_contains_name(&l, sym::hidden)) +} diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index d99f9af3faf..31b743f7a18 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -108,4 +108,20 @@ fn main() { Bar::B => (), _ => (), }; + + //#6984 + { + #![allow(clippy::manual_non_exhaustive)] + pub enum Enum { + A, + B, + #[doc(hidden)] + __Private, + } + match Enum::A { + Enum::A => (), + Enum::B => (), + _ => (), + } + } } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1752a95de4b..d19e6ab7eb2 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -108,4 +108,20 @@ fn main() { Bar::B => (), _ => (), }; + + //#6984 + { + #![allow(clippy::manual_non_exhaustive)] + pub enum Enum { + A, + B, + #[doc(hidden)] + __Private, + } + match Enum::A { + Enum::A => (), + Enum::B => (), + _ => (), + } + } } -- cgit 1.4.1-3-g733a5 From 5029dc805fa1b8c58988cad119a45a6d51bcdaad Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 9 Feb 2021 00:24:23 +0900 Subject: New Lint: excessive_for_each --- CHANGELOG.md | 1 + clippy_lints/src/iter_for_each.rs | 0 clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/excessive_for_each.rs | 149 +++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 29 +++++ tests/ui/excessive_for_each.rs | 96 ++++++++++++++++ tests/ui/excessive_for_each.stderr | 123 ++++++++++++++++++++ 7 files changed, 401 insertions(+) create mode 100644 clippy_lints/src/iter_for_each.rs create mode 100644 clippy_lints/src/methods/excessive_for_each.rs create mode 100644 tests/ui/excessive_for_each.rs create mode 100644 tests/ui/excessive_for_each.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 681ecd6832a..c35ab6c94bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2192,6 +2192,7 @@ Released 2018-09-13 [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence +[`excessive_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_for_each [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums [`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs diff --git a/clippy_lints/src/iter_for_each.rs b/clippy_lints/src/iter_for_each.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d37e229fb57..8fb3bfdafc9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -781,6 +781,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_DOUBLE_REF, &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, + &methods::EXCESSIVE_FOR_EACH, &methods::EXPECT_FUN_CALL, &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, @@ -1581,6 +1582,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_DOUBLE_REF), LintId::of(&methods::CLONE_ON_COPY), + LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::EXPECT_FUN_CALL), LintId::of(&methods::FILTER_MAP_IDENTITY), LintId::of(&methods::FILTER_NEXT), @@ -1797,6 +1799,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::BYTES_NTH), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), + LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs new file mode 100644 index 00000000000..36f92d5b95f --- /dev/null +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -0,0 +1,149 @@ +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_expr, NestedVisitorMap, Visitor}, + Expr, ExprKind, +}; +use rustc_lint::LateContext; +use rustc_middle::{hir::map::Map, ty, ty::Ty}; +use rustc_span::source_map::Span; + +use crate::utils::{match_trait_method, match_type, paths, snippet, span_lint_and_then}; + +use if_chain::if_chain; + +pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_>]]) { + if args.len() < 2 { + return; + } + + let for_each_args = args[0]; + if for_each_args.len() < 2 { + return; + } + let for_each_receiver = &for_each_args[0]; + let for_each_arg = &for_each_args[1]; + let iter_receiver = &args[1][0]; + + if_chain! { + if match_trait_method(cx, expr, &paths::ITERATOR); + if !match_trait_method(cx, for_each_receiver, &paths::ITERATOR); + if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver)); + if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; + then { + let body = cx.tcx.hir().body(body_id); + let mut ret_span_collector = RetSpanCollector::new(); + ret_span_collector.visit_expr(&body.value); + + let label = "'outer"; + let loop_label = if ret_span_collector.need_label { + format!("{}: ", label) + } else { + "".to_string() + }; + let sugg = + format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); + + let mut notes = vec![]; + for (span, need_label) in ret_span_collector.spans { + let cont_label = if need_label { + format!(" {}", label) + } else { + "".to_string() + }; + let note = format!("change `return` to `continue{}` in the loop body", cont_label); + notes.push((span, note)); + } + + span_lint_and_then(cx, + super::EXCESSIVE_FOR_EACH, + expr.span, + "excessive use of `for_each`", + |diag| { + diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders); + for note in notes { + diag.span_note(note.0, ¬e.1); + } + } + ); + } + } +} + +type PathSegment = &'static [&'static str]; + +const TARGET_ITER_RECEIVER_TY: &[PathSegment] = &[ + &paths::VEC, + &paths::VEC_DEQUE, + &paths::LINKED_LIST, + &paths::HASHMAP, + &paths::BTREEMAP, + &paths::HASHSET, + &paths::BTREESET, + &paths::BINARY_HEAP, +]; + +fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool { + let expr_ty = expr_ty.peel_refs(); + for target in TARGET_ITER_RECEIVER_TY { + if match_type(cx, expr_ty, target) { + return true; + } + } + + if_chain! { + if matches!(expr_ty.kind(), ty::Slice(_) | ty::Array(..)); + then { + return true; + } + } + + false +} + +/// Collect spans of `return` in the closure body. +struct RetSpanCollector { + spans: Vec<(Span, bool)>, + loop_depth: u16, + need_label: bool, +} + +impl RetSpanCollector { + fn new() -> Self { + Self { + spans: Vec::new(), + loop_depth: 0, + need_label: false, + } + } +} + +impl<'tcx> Visitor<'tcx> for RetSpanCollector { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &Expr<'_>) { + match expr.kind { + ExprKind::Ret(..) => { + if self.loop_depth > 0 && !self.need_label { + self.need_label = true + } + + self.spans.push((expr.span, self.loop_depth > 0)) + }, + + ExprKind::Loop(..) => { + self.loop_depth += 1; + walk_expr(self, expr); + self.loop_depth -= 1; + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fccdee07877..058140fddb8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -974,6 +974,33 @@ declare_clippy_lint! { "using `.skip(x).next()` on an iterator" } +declare_clippy_lint! { + /// **What it does:** Checks for use of `obj.method().for_each(closure)` if obj doesn't + /// implelement `Iterator` and `method()` returns `Impl Iterator` type. + /// + /// **Why is this bad?** Excessive use of `for_each` reduces redability, using `for` loop is + /// clearer and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let v = vec![0, 1, 2]; + /// v.iter().for_each(|elem| println!("{}", elem)); + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0, 1, 2]; + /// for elem in v.iter() { + /// println!("{}", elem); + /// } + /// ``` + pub EXCESSIVE_FOR_EACH, + style, + "using `.iter().for_each(|x| {..})` when using `for` loop would work instead" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `.get().unwrap()` (or /// `.get_mut().unwrap`) on a standard library type which implements `Index` @@ -1661,6 +1688,7 @@ impl_lint_pass!(Methods => [ ITER_NTH_ZERO, BYTES_NTH, ITER_SKIP_NEXT, + EXCESSIVE_FOR_EACH, GET_UNWRAP, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, @@ -1807,6 +1835,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), ["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice), + ["for_each", ..] => excessive_for_each::lint(cx, expr, &arg_lists), _ => {}, } diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs new file mode 100644 index 00000000000..12c87782d97 --- /dev/null +++ b/tests/ui/excessive_for_each.rs @@ -0,0 +1,96 @@ +#![warn(clippy::excessive_for_each)] +#![allow(clippy::needless_return)] + +use std::collections::*; + +fn main() { + // Should trigger this lint: Vec. + let vec: Vec = Vec::new(); + vec.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: &Vec. + let vec_ref = &vec; + vec_ref.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: VecDeque. + let vec_deq: VecDeque = VecDeque::new(); + vec_deq.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: LinkedList. + let list: LinkedList = LinkedList::new(); + list.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: HashMap. + let mut hash_map: HashMap = HashMap::new(); + hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); + hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); + hash_map.keys().for_each(|k| println!("{}", k)); + hash_map.values().for_each(|v| println!("{}", v)); + + // Should trigger this lint: HashSet. + let hash_set: HashSet = HashSet::new(); + hash_set.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: BTreeSet. + let btree_set: BTreeSet = BTreeSet::new(); + btree_set.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: BinaryHeap. + let binary_heap: BinaryHeap = BinaryHeap::new(); + binary_heap.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: Array. + let s = [1, 2, 3]; + s.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint. Slice. + vec.as_slice().iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint with notes that say "change `return` to `continue`". + vec.iter().for_each(|v| { + if *v == 10 { + return; + } else { + println!("{}", v); + } + }); + + // Should trigger this lint with notes that say "change `return` to `continue 'outer`". + vec.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // Should NOT trigger this lint in case `for_each` follows long iterator chain. + vec.iter().chain(vec.iter()).for_each(|v| println!("{}", v)); + + // Should NOT trigger this lint in case a `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + vec.iter().for_each(print); + + // Should NOT trigger this lint in case the receiver of `iter` is a user defined type. + let my_collection = MyCollection { v: vec![] }; + my_collection.iter().for_each(|v| println!("{}", v)); +} + +struct MyCollection { + v: Vec, +} + +impl MyCollection { + fn iter(&self) -> impl Iterator { + self.v.iter() + } +} diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr new file mode 100644 index 00000000000..026b14a5899 --- /dev/null +++ b/tests/ui/excessive_for_each.stderr @@ -0,0 +1,123 @@ +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:9:5 + | +LL | vec.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.iter() { .. }` + | + = note: `-D clippy::excessive-for-each` implied by `-D warnings` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:13:5 + | +LL | vec_ref.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_ref.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:17:5 + | +LL | vec_deq.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_deq.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:21:5 + | +LL | list.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in list.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:25:5 + | +LL | hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:26:5 + | +LL | hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:27:5 + | +LL | hash_map.keys().for_each(|k| println!("{}", k)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for k in hash_map.keys() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:28:5 + | +LL | hash_map.values().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_map.values() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:32:5 + | +LL | hash_set.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_set.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:36:5 + | +LL | btree_set.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in btree_set.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:40:5 + | +LL | binary_heap.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in binary_heap.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:44:5 + | +LL | s.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in s.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:47:5 + | +LL | vec.as_slice().iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.as_slice().iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:50:5 + | +LL | / vec.iter().for_each(|v| { +LL | | if *v == 10 { +LL | | return; +LL | | } else { +LL | | println!("{}", v); +LL | | } +LL | | }); + | |______^ help: try: `for v in vec.iter() { .. }` + | +note: change `return` to `continue` in the loop body + --> $DIR/excessive_for_each.rs:52:13 + | +LL | return; + | ^^^^^^ + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:59:5 + | +LL | / vec.iter().for_each(|v| { +LL | | for i in 0..*v { +LL | | if i == 10 { +LL | | return; +... | +LL | | } +LL | | }); + | |______^ help: try: `'outer: for v in vec.iter() { .. }` + | +note: change `return` to `continue 'outer` in the loop body + --> $DIR/excessive_for_each.rs:62:17 + | +LL | return; + | ^^^^^^ +note: change `return` to `continue` in the loop body + --> $DIR/excessive_for_each.rs:68:13 + | +LL | return; + | ^^^^^^ + +error: aborting due to 15 previous errors + -- cgit 1.4.1-3-g733a5 From 30952530c502acbfc40853f0392853469ece8024 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 9 Feb 2021 21:20:42 +0900 Subject: Fix codes that fails dogfood --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 425124b78f4..8c6c30a1881 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -924,7 +924,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm let mut ident_bind_name = String::from("_"); if !matching_wild { // Looking for unused bindings (i.e.: `_e`) - inner.iter().for_each(|pat| { + for pat in inner.iter() { if let PatKind::Binding(_, id, ident, None) = pat.kind { if ident.as_str().starts_with('_') && !LocalUsedVisitor::new(cx, id).check_expr(arm.body) @@ -933,7 +933,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm matching_wild = true; } } - }); + } } if_chain! { if matching_wild; -- cgit 1.4.1-3-g733a5 From 54a04711edb15ad0ce5e19f833a5172f651bb4c0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 9 Feb 2021 23:36:20 +0900 Subject: Change a category of excessive_for_each: Style -> Restriction --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/methods/excessive_for_each.rs | 1 - clippy_lints/src/methods/mod.rs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8fb3bfdafc9..2f2ccdb310a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1314,6 +1314,7 @@ 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::EXCESSIVE_FOR_EACH), LintId::of(&methods::EXPECT_USED), LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), @@ -1582,7 +1583,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_DOUBLE_REF), LintId::of(&methods::CLONE_ON_COPY), - LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::EXPECT_FUN_CALL), LintId::of(&methods::FILTER_MAP_IDENTITY), LintId::of(&methods::FILTER_NEXT), @@ -1799,7 +1799,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::BYTES_NTH), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), - LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index 36f92d5b95f..6b3a11044f0 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -26,7 +26,6 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ if_chain! { if match_trait_method(cx, expr, &paths::ITERATOR); - if !match_trait_method(cx, for_each_receiver, &paths::ITERATOR); if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver)); if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; then { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 058140fddb8..344c7f1bd9a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -997,7 +997,7 @@ declare_clippy_lint! { /// } /// ``` pub EXCESSIVE_FOR_EACH, - style, + restriction, "using `.iter().for_each(|x| {..})` when using `for` loop would work instead" } -- cgit 1.4.1-3-g733a5 From 5bb0f16552e8eab45025c7f4581b8ae5edca3219 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 10 Feb 2021 00:53:53 +0900 Subject: Trigger the lint iff exposure's body is ExprKind::Block. --- clippy_lints/src/methods/excessive_for_each.rs | 3 +- tests/ui/excessive_for_each.rs | 56 +++++++++--- tests/ui/excessive_for_each.stderr | 114 +++++++++++++++---------- 3 files changed, 115 insertions(+), 58 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index 6b3a11044f0..f3e9e2400f1 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -28,8 +28,9 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ if match_trait_method(cx, expr, &paths::ITERATOR); if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver)); if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(..) = body.value.kind; then { - let body = cx.tcx.hir().body(body_id); let mut ret_span_collector = RetSpanCollector::new(); ret_span_collector.visit_expr(&body.value); diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs index 12c87782d97..1c8e450398e 100644 --- a/tests/ui/excessive_for_each.rs +++ b/tests/ui/excessive_for_each.rs @@ -6,45 +6,72 @@ use std::collections::*; fn main() { // Should trigger this lint: Vec. let vec: Vec = Vec::new(); - vec.iter().for_each(|v| println!("{}", v)); + let mut acc = 0; + vec.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: &Vec. let vec_ref = &vec; - vec_ref.iter().for_each(|v| println!("{}", v)); + vec_ref.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: VecDeque. let vec_deq: VecDeque = VecDeque::new(); - vec_deq.iter().for_each(|v| println!("{}", v)); + vec_deq.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: LinkedList. let list: LinkedList = LinkedList::new(); - list.iter().for_each(|v| println!("{}", v)); + list.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: HashMap. let mut hash_map: HashMap = HashMap::new(); - hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); - hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); - hash_map.keys().for_each(|k| println!("{}", k)); - hash_map.values().for_each(|v| println!("{}", v)); + hash_map.iter().for_each(|(k, v)| { + acc += k + v; + }); + hash_map.iter_mut().for_each(|(k, v)| { + acc += *k + *v; + }); + hash_map.keys().for_each(|k| { + acc += k; + }); + hash_map.values().for_each(|v| { + acc += v; + }); // Should trigger this lint: HashSet. let hash_set: HashSet = HashSet::new(); - hash_set.iter().for_each(|v| println!("{}", v)); + hash_set.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: BTreeSet. let btree_set: BTreeSet = BTreeSet::new(); - btree_set.iter().for_each(|v| println!("{}", v)); + btree_set.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: BinaryHeap. let binary_heap: BinaryHeap = BinaryHeap::new(); - binary_heap.iter().for_each(|v| println!("{}", v)); + binary_heap.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: Array. let s = [1, 2, 3]; - s.iter().for_each(|v| println!("{}", v)); + s.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint. Slice. - vec.as_slice().iter().for_each(|v| println!("{}", v)); + vec.as_slice().iter().for_each(|v| { + acc += v; + }); // Should trigger this lint with notes that say "change `return` to `continue`". vec.iter().for_each(|v| { @@ -83,6 +110,9 @@ fn main() { // Should NOT trigger this lint in case the receiver of `iter` is a user defined type. let my_collection = MyCollection { v: vec![] }; my_collection.iter().for_each(|v| println!("{}", v)); + + // Should NOT trigger this lint in case the closure body is not a `ExprKind::Block`. + vec.iter().for_each(|x| acc += x); } struct MyCollection { diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr index 026b14a5899..c4b66e3a034 100644 --- a/tests/ui/excessive_for_each.stderr +++ b/tests/ui/excessive_for_each.stderr @@ -1,85 +1,111 @@ error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:9:5 + --> $DIR/excessive_for_each.rs:10:5 | -LL | vec.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.iter() { .. }` +LL | / vec.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec.iter() { .. }` | = note: `-D clippy::excessive-for-each` implied by `-D warnings` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:13:5 + --> $DIR/excessive_for_each.rs:16:5 | -LL | vec_ref.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_ref.iter() { .. }` +LL | / vec_ref.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec_ref.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:17:5 + --> $DIR/excessive_for_each.rs:22:5 | -LL | vec_deq.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_deq.iter() { .. }` +LL | / vec_deq.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec_deq.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:21:5 + --> $DIR/excessive_for_each.rs:28:5 | -LL | list.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in list.iter() { .. }` +LL | / list.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in list.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:25:5 + --> $DIR/excessive_for_each.rs:34:5 | -LL | hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter() { .. }` +LL | / hash_map.iter().for_each(|(k, v)| { +LL | | acc += k + v; +LL | | }); + | |______^ help: try: `for (k, v) in hash_map.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:26:5 + --> $DIR/excessive_for_each.rs:37:5 | -LL | hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` +LL | / hash_map.iter_mut().for_each(|(k, v)| { +LL | | acc += *k + *v; +LL | | }); + | |______^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:27:5 + --> $DIR/excessive_for_each.rs:40:5 | -LL | hash_map.keys().for_each(|k| println!("{}", k)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for k in hash_map.keys() { .. }` +LL | / hash_map.keys().for_each(|k| { +LL | | acc += k; +LL | | }); + | |______^ help: try: `for k in hash_map.keys() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:28:5 + --> $DIR/excessive_for_each.rs:43:5 | -LL | hash_map.values().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_map.values() { .. }` +LL | / hash_map.values().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in hash_map.values() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:32:5 + --> $DIR/excessive_for_each.rs:49:5 | -LL | hash_set.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_set.iter() { .. }` +LL | / hash_set.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in hash_set.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:36:5 + --> $DIR/excessive_for_each.rs:55:5 | -LL | btree_set.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in btree_set.iter() { .. }` +LL | / btree_set.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in btree_set.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:40:5 + --> $DIR/excessive_for_each.rs:61:5 | -LL | binary_heap.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in binary_heap.iter() { .. }` +LL | / binary_heap.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in binary_heap.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:44:5 + --> $DIR/excessive_for_each.rs:67:5 | -LL | s.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in s.iter() { .. }` +LL | / s.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in s.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:47:5 + --> $DIR/excessive_for_each.rs:72:5 | -LL | vec.as_slice().iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.as_slice().iter() { .. }` +LL | / vec.as_slice().iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec.as_slice().iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:50:5 + --> $DIR/excessive_for_each.rs:77:5 | LL | / vec.iter().for_each(|v| { LL | | if *v == 10 { @@ -91,13 +117,13 @@ LL | | }); | |______^ help: try: `for v in vec.iter() { .. }` | note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:52:13 + --> $DIR/excessive_for_each.rs:79:13 | LL | return; | ^^^^^^ error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:59:5 + --> $DIR/excessive_for_each.rs:86:5 | LL | / vec.iter().for_each(|v| { LL | | for i in 0..*v { @@ -109,12 +135,12 @@ LL | | }); | |______^ help: try: `'outer: for v in vec.iter() { .. }` | note: change `return` to `continue 'outer` in the loop body - --> $DIR/excessive_for_each.rs:62:17 + --> $DIR/excessive_for_each.rs:89:17 | LL | return; | ^^^^^^ note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:68:13 + --> $DIR/excessive_for_each.rs:95:13 | LL | return; | ^^^^^^ -- cgit 1.4.1-3-g733a5 From 90cbbb2da3989a437a36c075396331f81a0e3b30 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 11 Feb 2021 12:50:20 +0900 Subject: Avoid to suggest using label --- clippy_lints/src/methods/excessive_for_each.rs | 51 ++++++++++++-------------- tests/ui/excessive_for_each.rs | 2 +- tests/ui/excessive_for_each.stderr | 25 +------------ 3 files changed, 26 insertions(+), 52 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index f3e9e2400f1..e1440e68327 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -31,26 +31,20 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; then { - let mut ret_span_collector = RetSpanCollector::new(); - ret_span_collector.visit_expr(&body.value); - - let label = "'outer"; - let loop_label = if ret_span_collector.need_label { - format!("{}: ", label) - } else { - "".to_string() - }; + let mut ret_collector = RetCollector::new(); + ret_collector.visit_expr(&body.value); + + // Skip the lint if `return` is used in `Loop` to avoid a suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } + let sugg = - format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); + format!("for {} in {} {{ .. }}", snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); let mut notes = vec![]; - for (span, need_label) in ret_span_collector.spans { - let cont_label = if need_label { - format!(" {}", label) - } else { - "".to_string() - }; - let note = format!("change `return` to `continue{}` in the loop body", cont_label); + for span in ret_collector.spans { + let note = format!("change `return` to `continue` in the loop body"); notes.push((span, note)); } @@ -100,34 +94,37 @@ fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool { false } -/// Collect spans of `return` in the closure body. -struct RetSpanCollector { - spans: Vec<(Span, bool)>, +/// This type plays two roles. +/// 1. Collect spans of `return` in the closure body. +/// 2. Detect use of `return` in `Loop` in the closure body. +struct RetCollector { + spans: Vec, + ret_in_loop: bool, + loop_depth: u16, - need_label: bool, } -impl RetSpanCollector { +impl RetCollector { fn new() -> Self { Self { spans: Vec::new(), + ret_in_loop: false, loop_depth: 0, - need_label: false, } } } -impl<'tcx> Visitor<'tcx> for RetSpanCollector { +impl<'tcx> Visitor<'tcx> for RetCollector { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &Expr<'_>) { match expr.kind { ExprKind::Ret(..) => { - if self.loop_depth > 0 && !self.need_label { - self.need_label = true + if self.loop_depth > 0 && !self.ret_in_loop { + self.ret_in_loop = true } - self.spans.push((expr.span, self.loop_depth > 0)) + self.spans.push(expr.span) }, ExprKind::Loop(..) => { diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs index 1c8e450398e..0800bef71e9 100644 --- a/tests/ui/excessive_for_each.rs +++ b/tests/ui/excessive_for_each.rs @@ -82,7 +82,7 @@ fn main() { } }); - // Should trigger this lint with notes that say "change `return` to `continue 'outer`". + // Should NOT trigger this lint in case `return` is used in `Loop` of the closure. vec.iter().for_each(|v| { for i in 0..*v { if i == 10 { diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr index c4b66e3a034..f5799484e03 100644 --- a/tests/ui/excessive_for_each.stderr +++ b/tests/ui/excessive_for_each.stderr @@ -122,28 +122,5 @@ note: change `return` to `continue` in the loop body LL | return; | ^^^^^^ -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:86:5 - | -LL | / vec.iter().for_each(|v| { -LL | | for i in 0..*v { -LL | | if i == 10 { -LL | | return; -... | -LL | | } -LL | | }); - | |______^ help: try: `'outer: for v in vec.iter() { .. }` - | -note: change `return` to `continue 'outer` in the loop body - --> $DIR/excessive_for_each.rs:89:17 - | -LL | return; - | ^^^^^^ -note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:95:13 - | -LL | return; - | ^^^^^^ - -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From ccd7a600233ead22a77e024503edf4ef679c1751 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 22 Feb 2021 21:41:38 +0900 Subject: Refactor: Remove duplicated codes from excessive_for_each --- clippy_lints/src/methods/excessive_for_each.rs | 81 ++++++++------------------ 1 file changed, 25 insertions(+), 56 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index e1440e68327..14aef0e99d5 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -4,13 +4,15 @@ use rustc_hir::{ Expr, ExprKind, }; use rustc_lint::LateContext; -use rustc_middle::{hir::map::Map, ty, ty::Ty}; +use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; -use crate::utils::{match_trait_method, match_type, paths, snippet, span_lint_and_then}; - use if_chain::if_chain; +use crate::utils::{has_iter_method, match_trait_method, paths, snippet, span_lint_and_then}; + +use super::EXCESSIVE_FOR_EACH; + pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_>]]) { if args.len() < 2 { return; @@ -25,8 +27,8 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ let iter_receiver = &args[1][0]; if_chain! { + if has_iter_method(cx, cx.typeck_results().expr_ty(iter_receiver)).is_some(); if match_trait_method(cx, expr, &paths::ITERATOR); - if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver)); if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; @@ -34,64 +36,31 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ let mut ret_collector = RetCollector::new(); ret_collector.visit_expr(&body.value); - // Skip the lint if `return` is used in `Loop` to avoid a suggest using `'label`. + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. if ret_collector.ret_in_loop { return; } - let sugg = - format!("for {} in {} {{ .. }}", snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); - - let mut notes = vec![]; - for span in ret_collector.spans { - let note = format!("change `return` to `continue` in the loop body"); - notes.push((span, note)); - } - - span_lint_and_then(cx, - super::EXCESSIVE_FOR_EACH, - expr.span, - "excessive use of `for_each`", - |diag| { - diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders); - for note in notes { - diag.span_note(note.0, ¬e.1); - } - } - ); - } - } -} - -type PathSegment = &'static [&'static str]; - -const TARGET_ITER_RECEIVER_TY: &[PathSegment] = &[ - &paths::VEC, - &paths::VEC_DEQUE, - &paths::LINKED_LIST, - &paths::HASHMAP, - &paths::BTREEMAP, - &paths::HASHSET, - &paths::BTREESET, - &paths::BINARY_HEAP, -]; - -fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool { - let expr_ty = expr_ty.peel_refs(); - for target in TARGET_ITER_RECEIVER_TY { - if match_type(cx, expr_ty, target) { - return true; - } - } - - if_chain! { - if matches!(expr_ty.kind(), ty::Slice(_) | ty::Array(..)); - then { - return true; + let sugg = format!( + "for {} in {} {{ .. }}", + snippet(cx, body.params[0].pat.span, ""), + snippet(cx, for_each_receiver.span, "") + ); + + span_lint_and_then( + cx, + EXCESSIVE_FOR_EACH, + expr.span, + "excessive use of `for_each`", + |diag| { + diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders); + for span in ret_collector.spans { + diag.span_note(span, "change `return` to `continue` in the loop body"); + } + } + ) } } - - false } /// This type plays two roles. -- cgit 1.4.1-3-g733a5 From 25d8b94cecf5f7d17fc1d3d6140b62ae2f910b56 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 23 Feb 2021 21:55:00 +0900 Subject: Add comments to clarify why RetCollector is needed --- clippy_lints/src/methods/excessive_for_each.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index 14aef0e99d5..470f4ea2204 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -66,6 +66,13 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ /// This type plays two roles. /// 1. Collect spans of `return` in the closure body. /// 2. Detect use of `return` in `Loop` in the closure body. +/// +/// NOTE: The functionality of this type is similar to +/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use +/// `find_all_ret_expressions` instead of this type. The reasons are: +/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we +/// need here is `ExprKind::Ret` itself. +/// 2. We can't trace current loop depth with `find_all_ret_expressions`. struct RetCollector { spans: Vec, ret_in_loop: bool, -- cgit 1.4.1-3-g733a5 From 38431712998affe21621174faa2e490525d6b221 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Feb 2021 13:50:22 +0900 Subject: Improve the document of excessive_for_each --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 344c7f1bd9a..f0c99528fe1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -975,8 +975,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for use of `obj.method().for_each(closure)` if obj doesn't - /// implelement `Iterator` and `method()` returns `Impl Iterator` type. + /// **What it does:** Checks for use of `.method(..).for_each(closure)` if the reciever of `.method(..)` doesn't + /// implement `Iterator` and the return type of `.method(..)` implements `Iterator`. /// /// **Why is this bad?** Excessive use of `for_each` reduces redability, using `for` loop is /// clearer and more concise. -- cgit 1.4.1-3-g733a5 From 5543c34699ea2723565c1d517b3d3d98cb9fd8d4 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Feb 2021 13:58:41 +0900 Subject: Use ".." as default value of snippet in excessive_for_each --- clippy_lints/src/methods/excessive_for_each.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index 470f4ea2204..bcb7cf1491f 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -43,8 +43,8 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ let sugg = format!( "for {} in {} {{ .. }}", - snippet(cx, body.params[0].pat.span, ""), - snippet(cx, for_each_receiver.span, "") + snippet(cx, body.params[0].pat.span, ".."), + snippet(cx, for_each_receiver.span, "..") ); span_lint_and_then( -- cgit 1.4.1-3-g733a5 From 527fbbef486bab32a95209b638b097a14ff41db0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Mar 2021 00:42:43 +0900 Subject: Refactor excessive_for_each --- CHANGELOG.md | 2 +- clippy_lints/src/iter_for_each.rs | 0 clippy_lints/src/lib.rs | 6 +- clippy_lints/src/methods/excessive_for_each.rs | 122 ------------------ clippy_lints/src/methods/mod.rs | 29 ----- clippy_lints/src/needless_for_each.rs | 170 +++++++++++++++++++++++++ tests/ui/excessive_for_each.rs | 126 ------------------ tests/ui/excessive_for_each.stderr | 126 ------------------ tests/ui/needless_for_each_fixable.fixed | 99 ++++++++++++++ tests/ui/needless_for_each_fixable.rs | 99 ++++++++++++++ tests/ui/needless_for_each_fixable.stderr | 108 ++++++++++++++++ tests/ui/needless_for_each_unfixable.rs | 14 ++ tests/ui/needless_for_each_unfixable.stderr | 30 +++++ 13 files changed, 525 insertions(+), 406 deletions(-) delete mode 100644 clippy_lints/src/iter_for_each.rs delete mode 100644 clippy_lints/src/methods/excessive_for_each.rs create mode 100644 clippy_lints/src/needless_for_each.rs delete mode 100644 tests/ui/excessive_for_each.rs delete mode 100644 tests/ui/excessive_for_each.stderr create mode 100644 tests/ui/needless_for_each_fixable.fixed create mode 100644 tests/ui/needless_for_each_fixable.rs create mode 100644 tests/ui/needless_for_each_fixable.stderr create mode 100644 tests/ui/needless_for_each_unfixable.rs create mode 100644 tests/ui/needless_for_each_unfixable.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index c35ab6c94bf..a2193f0eab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2192,7 +2192,6 @@ Released 2018-09-13 [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence -[`excessive_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_for_each [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums [`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs @@ -2370,6 +2369,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_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`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_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark diff --git a/clippy_lints/src/iter_for_each.rs b/clippy_lints/src/iter_for_each.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2f2ccdb310a..8c9247d9781 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -291,6 +291,7 @@ mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; +mod needless_for_each; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -781,7 +782,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_DOUBLE_REF, &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, - &methods::EXCESSIVE_FOR_EACH, &methods::EXPECT_FUN_CALL, &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, @@ -868,6 +868,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_for_each::NEEDLESS_FOR_EACH, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_question_mark::NEEDLESS_QUESTION_MARK, &needless_update::NEEDLESS_UPDATE, @@ -1046,6 +1047,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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 needless_for_each::NeedlessForEach); store.register_late_pass(|| box approx_const::ApproxConstant); store.register_late_pass(|| box misc::MiscLints); store.register_late_pass(|| box eta_reduction::EtaReduction); @@ -1314,7 +1316,6 @@ 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::EXCESSIVE_FOR_EACH), LintId::of(&methods::EXPECT_USED), LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), @@ -1325,6 +1326,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(&needless_for_each::NEEDLESS_FOR_EACH), LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs deleted file mode 100644 index bcb7cf1491f..00000000000 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ /dev/null @@ -1,122 +0,0 @@ -use rustc_errors::Applicability; -use rustc_hir::{ - intravisit::{walk_expr, NestedVisitorMap, Visitor}, - Expr, ExprKind, -}; -use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; -use rustc_span::source_map::Span; - -use if_chain::if_chain; - -use crate::utils::{has_iter_method, match_trait_method, paths, snippet, span_lint_and_then}; - -use super::EXCESSIVE_FOR_EACH; - -pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_>]]) { - if args.len() < 2 { - return; - } - - let for_each_args = args[0]; - if for_each_args.len() < 2 { - return; - } - let for_each_receiver = &for_each_args[0]; - let for_each_arg = &for_each_args[1]; - let iter_receiver = &args[1][0]; - - if_chain! { - if has_iter_method(cx, cx.typeck_results().expr_ty(iter_receiver)).is_some(); - if match_trait_method(cx, expr, &paths::ITERATOR); - if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; - let body = cx.tcx.hir().body(body_id); - if let ExprKind::Block(..) = body.value.kind; - then { - let mut ret_collector = RetCollector::new(); - ret_collector.visit_expr(&body.value); - - // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. - if ret_collector.ret_in_loop { - return; - } - - let sugg = format!( - "for {} in {} {{ .. }}", - snippet(cx, body.params[0].pat.span, ".."), - snippet(cx, for_each_receiver.span, "..") - ); - - span_lint_and_then( - cx, - EXCESSIVE_FOR_EACH, - expr.span, - "excessive use of `for_each`", - |diag| { - diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders); - for span in ret_collector.spans { - diag.span_note(span, "change `return` to `continue` in the loop body"); - } - } - ) - } - } -} - -/// This type plays two roles. -/// 1. Collect spans of `return` in the closure body. -/// 2. Detect use of `return` in `Loop` in the closure body. -/// -/// NOTE: The functionality of this type is similar to -/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use -/// `find_all_ret_expressions` instead of this type. The reasons are: -/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we -/// need here is `ExprKind::Ret` itself. -/// 2. We can't trace current loop depth with `find_all_ret_expressions`. -struct RetCollector { - spans: Vec, - ret_in_loop: bool, - - loop_depth: u16, -} - -impl RetCollector { - fn new() -> Self { - Self { - spans: Vec::new(), - ret_in_loop: false, - loop_depth: 0, - } - } -} - -impl<'tcx> Visitor<'tcx> for RetCollector { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &Expr<'_>) { - match expr.kind { - ExprKind::Ret(..) => { - if self.loop_depth > 0 && !self.ret_in_loop { - self.ret_in_loop = true - } - - self.spans.push(expr.span) - }, - - ExprKind::Loop(..) => { - self.loop_depth += 1; - walk_expr(self, expr); - self.loop_depth -= 1; - return; - }, - - _ => {}, - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f0c99528fe1..fccdee07877 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -974,33 +974,6 @@ declare_clippy_lint! { "using `.skip(x).next()` on an iterator" } -declare_clippy_lint! { - /// **What it does:** Checks for use of `.method(..).for_each(closure)` if the reciever of `.method(..)` doesn't - /// implement `Iterator` and the return type of `.method(..)` implements `Iterator`. - /// - /// **Why is this bad?** Excessive use of `for_each` reduces redability, using `for` loop is - /// clearer and more concise. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// let v = vec![0, 1, 2]; - /// v.iter().for_each(|elem| println!("{}", elem)); - /// ``` - /// Use instead: - /// ```rust - /// let v = vec![0, 1, 2]; - /// for elem in v.iter() { - /// println!("{}", elem); - /// } - /// ``` - pub EXCESSIVE_FOR_EACH, - restriction, - "using `.iter().for_each(|x| {..})` when using `for` loop would work instead" -} - declare_clippy_lint! { /// **What it does:** Checks for use of `.get().unwrap()` (or /// `.get_mut().unwrap`) on a standard library type which implements `Index` @@ -1688,7 +1661,6 @@ impl_lint_pass!(Methods => [ ITER_NTH_ZERO, BYTES_NTH, ITER_SKIP_NEXT, - EXCESSIVE_FOR_EACH, GET_UNWRAP, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, @@ -1835,7 +1807,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), ["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice), - ["for_each", ..] => excessive_for_each::lint(cx, expr, &arg_lists), _ => {}, } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs new file mode 100644 index 00000000000..a7b0a1ca082 --- /dev/null +++ b/clippy_lints/src/needless_for_each.rs @@ -0,0 +1,170 @@ +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_expr, NestedVisitorMap, Visitor}, + Expr, ExprKind, Stmt, StmtKind, +}; +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, sym, Symbol}; + +use if_chain::if_chain; + +use crate::utils::{ + has_iter_method, is_diagnostic_assoc_item, method_calls, snippet_with_applicability, span_lint_and_then, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `for_each` that would be more simply written as a + /// `for` loop. + /// + /// **Why is this bad?** `for_each` may be used after applying iterator transformers like + /// `filter` for better readability and performance. It may also be used to fit a simple + /// operation on one line. + /// But when none of these apply, a simple `for` loop is more idiomatic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let v = vec![0, 1, 2]; + /// v.iter().for_each(|elem| { + /// println!("{}", elem); + /// }) + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0, 1, 2]; + /// for elem in v.iter() { + /// println!("{}", elem); + /// } + /// ``` + pub NEEDLESS_FOR_EACH, + restriction, + "using `for_each` where a `for` loop would be simpler" +} + +declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); + +impl LateLintPass<'_> for NeedlessForEach { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + let expr = match stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + StmtKind::Local(local) if local.init.is_some() => local.init.unwrap(), + _ => return, + }; + + // Max depth is set to 3 because we need to check the method chain length is just two. + let (method_names, arg_lists, _) = method_calls(expr, 3); + + if_chain! { + // This assures the length of this method chain is two. + if let [for_each_args, iter_args] = arg_lists.as_slice(); + if let Some(for_each_sym) = method_names.first(); + if *for_each_sym == Symbol::intern("for_each"); + if let Some(did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if is_diagnostic_assoc_item(cx, did, sym::Iterator); + // Checks the type of the first method receiver is NOT a user defined type. + if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_args[0])).is_some(); + if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; + let body = cx.tcx.hir().body(body_id); + // Skip the lint if the body is not block because this is simpler than `for` loop. + // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. + if let ExprKind::Block(..) = body.value.kind; + then { + let mut ret_collector = RetCollector::default(); + ret_collector.visit_expr(&body.value); + + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } + + // We can't use `Applicability::MachineApplicable` when the closure contains `return` + // because `Diagnostic::multipart_suggestion` doesn't work with multiple overlapped + // spans. + let mut applicability = if ret_collector.spans.is_empty() { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + + let mut suggs = vec![]; + suggs.push((stmt.span, format!( + "for {} in {} {}", + snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_args[0].span, "..", &mut applicability), + snippet_with_applicability(cx, body.value.span, "..", &mut applicability), + ))); + + for span in &ret_collector.spans { + suggs.push((*span, "return".to_string())); + } + + span_lint_and_then( + cx, + NEEDLESS_FOR_EACH, + stmt.span, + "needless use of `for_each`", + |diag| { + diag.multipart_suggestion("try", suggs, applicability); + // `Diagnostic::multipart_suggestion` ignores the second and subsequent overlapped spans, + // so `span_note` is needed here even though `suggs` includes the replacements. + for span in ret_collector.spans { + diag.span_note(span, "replace `return` with `continue`"); + } + } + ) + } + } + } +} + +/// This type plays two roles. +/// 1. Collect spans of `return` in the closure body. +/// 2. Detect use of `return` in `Loop` in the closure body. +/// +/// NOTE: The functionality of this type is similar to +/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use +/// `find_all_ret_expressions` instead of this type. The reasons are: +/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we +/// need here is `ExprKind::Ret` itself. +/// 2. We can't trace current loop depth with `find_all_ret_expressions`. +#[derive(Default)] +struct RetCollector { + spans: Vec, + ret_in_loop: bool, + loop_depth: u16, +} + +impl<'tcx> Visitor<'tcx> for RetCollector { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &Expr<'_>) { + match expr.kind { + ExprKind::Ret(..) => { + if self.loop_depth > 0 && !self.ret_in_loop { + self.ret_in_loop = true + } + + self.spans.push(expr.span) + }, + + ExprKind::Loop(..) => { + self.loop_depth += 1; + walk_expr(self, expr); + self.loop_depth -= 1; + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs deleted file mode 100644 index 0800bef71e9..00000000000 --- a/tests/ui/excessive_for_each.rs +++ /dev/null @@ -1,126 +0,0 @@ -#![warn(clippy::excessive_for_each)] -#![allow(clippy::needless_return)] - -use std::collections::*; - -fn main() { - // Should trigger this lint: Vec. - let vec: Vec = Vec::new(); - let mut acc = 0; - vec.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: &Vec. - let vec_ref = &vec; - vec_ref.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: VecDeque. - let vec_deq: VecDeque = VecDeque::new(); - vec_deq.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: LinkedList. - let list: LinkedList = LinkedList::new(); - list.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: HashMap. - let mut hash_map: HashMap = HashMap::new(); - hash_map.iter().for_each(|(k, v)| { - acc += k + v; - }); - hash_map.iter_mut().for_each(|(k, v)| { - acc += *k + *v; - }); - hash_map.keys().for_each(|k| { - acc += k; - }); - hash_map.values().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: HashSet. - let hash_set: HashSet = HashSet::new(); - hash_set.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: BTreeSet. - let btree_set: BTreeSet = BTreeSet::new(); - btree_set.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: BinaryHeap. - let binary_heap: BinaryHeap = BinaryHeap::new(); - binary_heap.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: Array. - let s = [1, 2, 3]; - s.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint. Slice. - vec.as_slice().iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint with notes that say "change `return` to `continue`". - vec.iter().for_each(|v| { - if *v == 10 { - return; - } else { - println!("{}", v); - } - }); - - // Should NOT trigger this lint in case `return` is used in `Loop` of the closure. - vec.iter().for_each(|v| { - for i in 0..*v { - if i == 10 { - return; - } else { - println!("{}", v); - } - } - if *v == 20 { - return; - } else { - println!("{}", v); - } - }); - - // Should NOT trigger this lint in case `for_each` follows long iterator chain. - vec.iter().chain(vec.iter()).for_each(|v| println!("{}", v)); - - // Should NOT trigger this lint in case a `for_each` argument is not closure. - fn print(x: &i32) { - println!("{}", x); - } - vec.iter().for_each(print); - - // Should NOT trigger this lint in case the receiver of `iter` is a user defined type. - let my_collection = MyCollection { v: vec![] }; - my_collection.iter().for_each(|v| println!("{}", v)); - - // Should NOT trigger this lint in case the closure body is not a `ExprKind::Block`. - vec.iter().for_each(|x| acc += x); -} - -struct MyCollection { - v: Vec, -} - -impl MyCollection { - fn iter(&self) -> impl Iterator { - self.v.iter() - } -} diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr deleted file mode 100644 index f5799484e03..00000000000 --- a/tests/ui/excessive_for_each.stderr +++ /dev/null @@ -1,126 +0,0 @@ -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:10:5 - | -LL | / vec.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec.iter() { .. }` - | - = note: `-D clippy::excessive-for-each` implied by `-D warnings` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:16:5 - | -LL | / vec_ref.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec_ref.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:22:5 - | -LL | / vec_deq.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec_deq.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:28:5 - | -LL | / list.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in list.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:34:5 - | -LL | / hash_map.iter().for_each(|(k, v)| { -LL | | acc += k + v; -LL | | }); - | |______^ help: try: `for (k, v) in hash_map.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:37:5 - | -LL | / hash_map.iter_mut().for_each(|(k, v)| { -LL | | acc += *k + *v; -LL | | }); - | |______^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:40:5 - | -LL | / hash_map.keys().for_each(|k| { -LL | | acc += k; -LL | | }); - | |______^ help: try: `for k in hash_map.keys() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:43:5 - | -LL | / hash_map.values().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in hash_map.values() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:49:5 - | -LL | / hash_set.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in hash_set.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:55:5 - | -LL | / btree_set.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in btree_set.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:61:5 - | -LL | / binary_heap.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in binary_heap.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:67:5 - | -LL | / s.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in s.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:72:5 - | -LL | / vec.as_slice().iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec.as_slice().iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:77:5 - | -LL | / vec.iter().for_each(|v| { -LL | | if *v == 10 { -LL | | return; -LL | | } else { -LL | | println!("{}", v); -LL | | } -LL | | }); - | |______^ help: try: `for v in vec.iter() { .. }` - | -note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:79:13 - | -LL | return; - | ^^^^^^ - -error: aborting due to 14 previous errors - diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed new file mode 100644 index 00000000000..0caa95a9f53 --- /dev/null +++ b/tests/ui/needless_for_each_fixable.fixed @@ -0,0 +1,99 @@ +// run-rustfix +#![warn(clippy::needless_for_each)] +#![allow(unused, clippy::needless_return, clippy::match_single_binding)] + +use std::collections::HashMap; + +fn should_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + for elem in v.iter() { + acc += elem; + } + for elem in v.into_iter() { + acc += elem; + } + + let mut hash_map: HashMap = HashMap::new(); + for (k, v) in hash_map.iter() { + acc += k + v; + } + for (k, v) in hash_map.iter_mut() { + acc += *k + *v; + } + for k in hash_map.keys() { + acc += k; + } + for v in hash_map.values() { + acc += v; + } + + fn my_vec() -> Vec { + Vec::new() + } + for elem in my_vec().iter() { + acc += elem; + } +} + +fn should_not_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + + // `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + v.iter().for_each(print); + + // `for_each` follows long iterator chain. + v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.as_slice().iter().for_each(|v| { + acc += v; + }); + + // `return` is used in `Loop` of the closure. + v.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + + // Previously transformed iterator variable. + let it = v.iter(); + it.chain(v.iter()).for_each(|elem| { + acc += elem; + }); + + // `for_each` is not directly in a statement. + match 1 { + _ => v.iter().for_each(|elem| { + acc += elem; + }), + } +} + +fn main() {} diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs new file mode 100644 index 00000000000..a04243de27a --- /dev/null +++ b/tests/ui/needless_for_each_fixable.rs @@ -0,0 +1,99 @@ +// run-rustfix +#![warn(clippy::needless_for_each)] +#![allow(unused, clippy::needless_return, clippy::match_single_binding)] + +use std::collections::HashMap; + +fn should_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + v.iter().for_each(|elem| { + acc += elem; + }); + v.into_iter().for_each(|elem| { + acc += elem; + }); + + let mut hash_map: HashMap = HashMap::new(); + hash_map.iter().for_each(|(k, v)| { + acc += k + v; + }); + hash_map.iter_mut().for_each(|(k, v)| { + acc += *k + *v; + }); + hash_map.keys().for_each(|k| { + acc += k; + }); + hash_map.values().for_each(|v| { + acc += v; + }); + + fn my_vec() -> Vec { + Vec::new() + } + my_vec().iter().for_each(|elem| { + acc += elem; + }); +} + +fn should_not_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + + // `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + v.iter().for_each(print); + + // `for_each` follows long iterator chain. + v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.as_slice().iter().for_each(|v| { + acc += v; + }); + + // `return` is used in `Loop` of the closure. + v.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + + // Previously transformed iterator variable. + let it = v.iter(); + it.chain(v.iter()).for_each(|elem| { + acc += elem; + }); + + // `for_each` is not directly in a statement. + match 1 { + _ => v.iter().for_each(|elem| { + acc += elem; + }), + } +} + +fn main() {} diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr new file mode 100644 index 00000000000..214e357a208 --- /dev/null +++ b/tests/ui/needless_for_each_fixable.stderr @@ -0,0 +1,108 @@ +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:10:5 + | +LL | / v.iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | + = note: `-D clippy::needless-for-each` implied by `-D warnings` +help: try + | +LL | for elem in v.iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:13:5 + | +LL | / v.into_iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in v.into_iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:18:5 + | +LL | / hash_map.iter().for_each(|(k, v)| { +LL | | acc += k + v; +LL | | }); + | |_______^ + | +help: try + | +LL | for (k, v) in hash_map.iter() { +LL | acc += k + v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:21:5 + | +LL | / hash_map.iter_mut().for_each(|(k, v)| { +LL | | acc += *k + *v; +LL | | }); + | |_______^ + | +help: try + | +LL | for (k, v) in hash_map.iter_mut() { +LL | acc += *k + *v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:24:5 + | +LL | / hash_map.keys().for_each(|k| { +LL | | acc += k; +LL | | }); + | |_______^ + | +help: try + | +LL | for k in hash_map.keys() { +LL | acc += k; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:27:5 + | +LL | / hash_map.values().for_each(|v| { +LL | | acc += v; +LL | | }); + | |_______^ + | +help: try + | +LL | for v in hash_map.values() { +LL | acc += v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:34:5 + | +LL | / my_vec().iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in my_vec().iter() { +LL | acc += elem; +LL | } + | + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs new file mode 100644 index 00000000000..d765d7dab65 --- /dev/null +++ b/tests/ui/needless_for_each_unfixable.rs @@ -0,0 +1,14 @@ +#![warn(clippy::needless_for_each)] +#![allow(clippy::needless_return)] + +fn main() { + let v: Vec = Vec::new(); + // This is unfixable because the closure includes `return`. + v.iter().for_each(|v| { + if *v == 10 { + return; + } else { + println!("{}", v); + } + }); +} diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr new file mode 100644 index 00000000000..58d107062bc --- /dev/null +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -0,0 +1,30 @@ +error: needless use of `for_each` + --> $DIR/needless_for_each_unfixable.rs:7:5 + | +LL | / v.iter().for_each(|v| { +LL | | if *v == 10 { +LL | | return; +LL | | } else { +LL | | println!("{}", v); +LL | | } +LL | | }); + | |_______^ + | + = note: `-D clippy::needless-for-each` implied by `-D warnings` +note: replace `return` with `continue` + --> $DIR/needless_for_each_unfixable.rs:9:13 + | +LL | return; + | ^^^^^^ +help: try + | +LL | for v in v.iter() { +LL | if *v == 10 { +LL | return; +LL | } else { +LL | println!("{}", v); +LL | } + ... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f2cc995bcfdc86a564b4040585f97f012be9454b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Mar 2021 15:11:39 +0900 Subject: Remove method_calls --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/needless_for_each.rs | 40 +++++++++++++++++-------------- tests/ui/needless_for_each_fixable.fixed | 39 ++++++++++++++++++------------ tests/ui/needless_for_each_fixable.rs | 39 ++++++++++++++++++------------ tests/ui/needless_for_each_fixable.stderr | 27 ++++++++++++++++----- 5 files changed, 92 insertions(+), 55 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8c9247d9781..11716afe11c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1326,7 +1326,6 @@ 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(&needless_for_each::NEEDLESS_FOR_EACH), LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), @@ -1409,6 +1408,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), LintId::of(&needless_continue::NEEDLESS_CONTINUE), + LintId::of(&needless_for_each::NEEDLESS_FOR_EACH), 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), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index a7b0a1ca082..f60b09898ab 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -10,9 +10,7 @@ use rustc_span::{source_map::Span, sym, Symbol}; use if_chain::if_chain; -use crate::utils::{ - has_iter_method, is_diagnostic_assoc_item, method_calls, snippet_with_applicability, span_lint_and_then, -}; +use crate::utils::{has_iter_method, is_trait_method, snippet_with_applicability, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for usage of `for_each` that would be more simply written as a @@ -41,7 +39,7 @@ declare_clippy_lint! { /// } /// ``` pub NEEDLESS_FOR_EACH, - restriction, + pedantic, "using `for_each` where a `for` loop would be simpler" } @@ -55,22 +53,28 @@ impl LateLintPass<'_> for NeedlessForEach { _ => return, }; - // Max depth is set to 3 because we need to check the method chain length is just two. - let (method_names, arg_lists, _) = method_calls(expr, 3); - if_chain! { - // This assures the length of this method chain is two. - if let [for_each_args, iter_args] = arg_lists.as_slice(); - if let Some(for_each_sym) = method_names.first(); - if *for_each_sym == Symbol::intern("for_each"); - if let Some(did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diagnostic_assoc_item(cx, did, sym::Iterator); - // Checks the type of the first method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_args[0])).is_some(); - if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; - let body = cx.tcx.hir().body(body_id); + // Check the method name is `for_each`. + if let ExprKind::MethodCall(method_name, _, for_each_args, _) = expr.kind; + if method_name.ident.name == Symbol::intern("for_each"); + // Check `for_each` is an associated function of `Iterator`. + if is_trait_method(cx, expr, sym::Iterator); + // Checks the receiver of `for_each` is also a method call. + if let Some(for_each_receiver) = for_each_args.get(0); + if let ExprKind::MethodCall(_, _, iter_args, _) = for_each_receiver.kind; + // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or + // `v.foo().iter().for_each()` must be skipped. + if let Some(iter_receiver) = iter_args.get(0); + if matches!( + iter_receiver.kind, + ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) + ); + // Checks the type of the `iter` method receiver is NOT a user defined type. + if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_receiver)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. + if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; + let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; then { let mut ret_collector = RetCollector::default(); @@ -99,7 +103,7 @@ impl LateLintPass<'_> for NeedlessForEach { ))); for span in &ret_collector.spans { - suggs.push((*span, "return".to_string())); + suggs.push((*span, "continue".to_string())); } span_lint_and_then( diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index 0caa95a9f53..a4d4937a19a 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -14,6 +14,10 @@ fn should_lint() { acc += elem; } + for elem in [1, 2, 3].iter() { + acc += elem; + } + let mut hash_map: HashMap = HashMap::new(); for (k, v) in hash_map.iter() { acc += k + v; @@ -46,11 +50,30 @@ fn should_not_lint() { } v.iter().for_each(print); + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + // `for_each` follows long iterator chain. - v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.iter().chain(v.iter()).for_each(|v| { + acc += v; + }); v.as_slice().iter().for_each(|v| { acc += v; }); + s.v.iter().for_each(|v| { + acc += v; + }); // `return` is used in `Loop` of the closure. v.iter().for_each(|v| { @@ -68,20 +91,6 @@ fn should_not_lint() { } }); - // User defined type. - struct MyStruct { - v: Vec, - } - impl MyStruct { - fn iter(&self) -> impl Iterator { - self.v.iter() - } - } - let s = MyStruct { v: Vec::new() }; - s.iter().for_each(|elem| { - acc += elem; - }); - // Previously transformed iterator variable. let it = v.iter(); it.chain(v.iter()).for_each(|elem| { diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index a04243de27a..b374128f253 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -14,6 +14,10 @@ fn should_lint() { acc += elem; }); + [1, 2, 3].iter().for_each(|elem| { + acc += elem; + }); + let mut hash_map: HashMap = HashMap::new(); hash_map.iter().for_each(|(k, v)| { acc += k + v; @@ -46,11 +50,30 @@ fn should_not_lint() { } v.iter().for_each(print); + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + // `for_each` follows long iterator chain. - v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.iter().chain(v.iter()).for_each(|v| { + acc += v; + }); v.as_slice().iter().for_each(|v| { acc += v; }); + s.v.iter().for_each(|v| { + acc += v; + }); // `return` is used in `Loop` of the closure. v.iter().for_each(|v| { @@ -68,20 +91,6 @@ fn should_not_lint() { } }); - // User defined type. - struct MyStruct { - v: Vec, - } - impl MyStruct { - fn iter(&self) -> impl Iterator { - self.v.iter() - } - } - let s = MyStruct { v: Vec::new() }; - s.iter().for_each(|elem| { - acc += elem; - }); - // Previously transformed iterator variable. let it = v.iter(); it.chain(v.iter()).for_each(|elem| { diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 214e357a208..483a5e6d61d 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -30,7 +30,22 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:18:5 + --> $DIR/needless_for_each_fixable.rs:17:5 + | +LL | / [1, 2, 3].iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in [1, 2, 3].iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:22:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -45,7 +60,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:21:5 + --> $DIR/needless_for_each_fixable.rs:25:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -60,7 +75,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:24:5 + --> $DIR/needless_for_each_fixable.rs:28:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -75,7 +90,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:27:5 + --> $DIR/needless_for_each_fixable.rs:31:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -90,7 +105,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:34:5 + --> $DIR/needless_for_each_fixable.rs:38:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; @@ -104,5 +119,5 @@ LL | acc += elem; LL | } | -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From bf1e3f7a9f53dd783ddf500ee45e8c0629dd5e67 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sun, 14 Mar 2021 10:22:28 +0900 Subject: Skip needless_for_each if an input stmt is local --- clippy_lints/src/needless_for_each.rs | 50 +++++++++++++---------------- tests/ui/needless_for_each_fixable.fixed | 5 +++ tests/ui/needless_for_each_fixable.rs | 5 +++ tests/ui/needless_for_each_unfixable.stderr | 9 +++--- 4 files changed, 37 insertions(+), 32 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index f60b09898ab..727937354d6 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -49,31 +49,28 @@ impl LateLintPass<'_> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let expr = match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, - StmtKind::Local(local) if local.init.is_some() => local.init.unwrap(), _ => return, }; if_chain! { // Check the method name is `for_each`. - if let ExprKind::MethodCall(method_name, _, for_each_args, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, [for_each_recv, for_each_arg], _) = expr.kind; if method_name.ident.name == Symbol::intern("for_each"); // Check `for_each` is an associated function of `Iterator`. if is_trait_method(cx, expr, sym::Iterator); // Checks the receiver of `for_each` is also a method call. - if let Some(for_each_receiver) = for_each_args.get(0); - if let ExprKind::MethodCall(_, _, iter_args, _) = for_each_receiver.kind; + if let ExprKind::MethodCall(_, _, [iter_recv], _) = for_each_recv.kind; // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. - if let Some(iter_receiver) = iter_args.get(0); if matches!( - iter_receiver.kind, + iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ); // Checks the type of the `iter` method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_receiver)).is_some(); + if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_recv)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; + if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; then { @@ -85,26 +82,27 @@ impl LateLintPass<'_> for NeedlessForEach { return; } - // We can't use `Applicability::MachineApplicable` when the closure contains `return` - // because `Diagnostic::multipart_suggestion` doesn't work with multiple overlapped - // spans. - let mut applicability = if ret_collector.spans.is_empty() { - Applicability::MachineApplicable + let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { + (Applicability::MachineApplicable, None) } else { - Applicability::MaybeIncorrect + ( + Applicability::MaybeIncorrect, + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), + ), + ) }; - let mut suggs = vec![]; - suggs.push((stmt.span, format!( + let sugg = format!( "for {} in {} {}", snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, for_each_args[0].span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), snippet_with_applicability(cx, body.value.span, "..", &mut applicability), - ))); - - for span in &ret_collector.spans { - suggs.push((*span, "continue".to_string())); - } + ); span_lint_and_then( cx, @@ -112,11 +110,9 @@ impl LateLintPass<'_> for NeedlessForEach { stmt.span, "needless use of `for_each`", |diag| { - diag.multipart_suggestion("try", suggs, applicability); - // `Diagnostic::multipart_suggestion` ignores the second and subsequent overlapped spans, - // so `span_note` is needed here even though `suggs` includes the replacements. - for span in ret_collector.spans { - diag.span_note(span, "replace `return` with `continue`"); + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("try replacing `return` with `continue`", ret_suggs, applicability); } } ) diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index a4d4937a19a..f00f9ee4c33 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -103,6 +103,11 @@ fn should_not_lint() { acc += elem; }), } + + // `for_each` is in a let bingind. + let _ = v.iter().for_each(|elem| { + acc += elem; + }); } fn main() {} diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index b374128f253..1bd400d348b 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -103,6 +103,11 @@ fn should_not_lint() { acc += elem; }), } + + // `for_each` is in a let bingind. + let _ = v.iter().for_each(|elem| { + acc += elem; + }); } fn main() {} diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index 58d107062bc..bbb63fd8deb 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -11,11 +11,6 @@ LL | | }); | |_______^ | = note: `-D clippy::needless-for-each` implied by `-D warnings` -note: replace `return` with `continue` - --> $DIR/needless_for_each_unfixable.rs:9:13 - | -LL | return; - | ^^^^^^ help: try | LL | for v in v.iter() { @@ -25,6 +20,10 @@ LL | } else { LL | println!("{}", v); LL | } ... +help: try replacing `return` with `continue` + | +LL | continue; + | ^^^^^^^^ error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From e61f9782c805f246394f870902140bf32c897b7e Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Mar 2021 12:01:39 +0900 Subject: Tweak a suggestion message of needless_for_each --- clippy_lints/src/needless_for_each.rs | 21 +++++++++------------ tests/ui/needless_for_each_unfixable.stderr | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 727937354d6..2ea871990f1 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -10,7 +10,10 @@ use rustc_span::{source_map::Span, sym, Symbol}; use if_chain::if_chain; -use crate::utils::{has_iter_method, is_trait_method, snippet_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::has_iter_method; declare_clippy_lint! { /// **What it does:** Checks for usage of `for_each` that would be more simply written as a @@ -104,18 +107,12 @@ impl LateLintPass<'_> for NeedlessForEach { snippet_with_applicability(cx, body.value.span, "..", &mut applicability), ); - span_lint_and_then( - cx, - NEEDLESS_FOR_EACH, - stmt.span, - "needless use of `for_each`", - |diag| { - diag.span_suggestion(stmt.span, "try", sugg, applicability); - if let Some(ret_suggs) = ret_suggs { - diag.multipart_suggestion("try replacing `return` with `continue`", ret_suggs, applicability); - } + span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); } - ) + }) } } } diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index bbb63fd8deb..8c4507d2328 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -20,7 +20,7 @@ LL | } else { LL | println!("{}", v); LL | } ... -help: try replacing `return` with `continue` +help: ...and replace `return` with `continue` | LL | continue; | ^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 56fbbf7b8fc7c99a6c54dfff6847770a4ed27eb1 Mon Sep 17 00:00:00 2001 From: Eric Loren Date: Sat, 14 Nov 2020 19:47:17 -0500 Subject: Suggest `flatten` instead of `is_some` -> `unwrap` --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/filter_map.rs | 213 ++++++++++++++++++++++----------- clippy_lints/src/methods/mod.rs | 27 ++++- tests/ui/option_filter_map.fixed | 23 ++++ tests/ui/option_filter_map.rs | 25 ++++ tests/ui/option_filter_map.stderr | 56 +++++++++ 8 files changed, 280 insertions(+), 70 deletions(-) create mode 100644 tests/ui/option_filter_map.fixed create mode 100644 tests/ui/option_filter_map.rs create mode 100644 tests/ui/option_filter_map.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 57996802182..47af42e14ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2389,6 +2389,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_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map [`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 diff --git a/README.md b/README.md index 63057609bb6..8c0c16c443d 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 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..82abd4803db 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -804,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AS_REF_DEREF, + &methods::OPTION_FILTER_MAP, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, &methods::RESULT_MAP_OR_INTO_OPTION, @@ -1596,6 +1597,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::OPTION_FILTER_MAP), LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::OR_FUN_CALL), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), @@ -1891,6 +1893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::MANUAL_FILTER_MAP), LintId::of(&methods::MANUAL_FIND_MAP), LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::OPTION_FILTER_MAP), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 2cb476acb2b..68f8480dc51 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,87 +1,166 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, path_to_local_id, SpanlessEq}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, PatKind, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -use rustc_span::symbol::sym; +use rustc_span::source_map::Span; +use rustc_span::symbol::{sym, Symbol}; +use std::borrow::Cow; use super::MANUAL_FILTER_MAP; use super::MANUAL_FIND_MAP; +use super::OPTION_FILTER_MAP; + +fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { + match &expr.kind { + hir::ExprKind::Path(QPath::TypeRelative(_, ref mname)) => mname.ident.name == method_name, + hir::ExprKind::Path(QPath::Resolved(_, segments)) => { + segments.segments.last().unwrap().ident.name == method_name + }, + hir::ExprKind::Closure(_, _, c, _, _) => { + let body = cx.tcx.hir().body(*c); + let closure_expr = remove_blocks(&body.value); + let arg_id = body.params[0].pat.hir_id; + match closure_expr.kind { + hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, ref args, _) => { + if_chain! { + if ident.name == method_name; + if let hir::ExprKind::Path(path) = &args[0].kind; + if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id); + then { + return arg_id == *local + } + } + false + }, + _ => false, + } + }, + _ => false, + } +} + +fn is_option_filter_map<'tcx>( + cx: &LateContext<'tcx>, + filter_arg: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, +) -> bool { + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) +} + +/// lint use of `filter().map()` for `Iterators` +fn lint_filter_some_map_unwrap<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_recv: &'tcx hir::Expr<'_>, + filter_arg: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, + target_span: Span, + methods_span: Span, +) { + let iterator = is_trait_method(cx, expr, sym::Iterator); + let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&filter_recv), sym::option_type); + if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) { + let msg = "`filter` for `Some` followed by `unwrap`"; + let help = "consider using `flatten` instead"; + let sugg = format!( + "{}", + reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),) + ); + span_lint_and_sugg( + cx, + OPTION_FILTER_MAP, + methods_span, + msg, + help, + sugg, + Applicability::MachineApplicable, + ); + } +} /// lint use of `filter().map()` or `find().map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool, target_span: Span) { if_chain! { - if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; - if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if is_trait_method(cx, map_recv, sym::Iterator); + if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; + if let ExprKind::MethodCall(_, _, [filter_recv, filter_arg], filter_span) = map_recv.kind; + then { + lint_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, + map_arg, target_span, filter_span.to(map_span)); + if_chain! { + if is_trait_method(cx, map_recv, sym::Iterator); - // filter(|x| ...is_some())... - if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) - }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { - Some(true) - } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + // filter(|x| ...is_some())... + if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - if_chain! { - if path_to_local_id(a_path, filter_param_id); - if path_to_local_id(b, map_param_id); - if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); - then { - return true; + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + if_chain! { + if path_to_local_id(a_path, filter_param_id); + if path_to_local_id(b, map_param_id); + if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); + then { + return true; + } } - } - false - }; - if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); - then { - let span = filter_span.to(map_span); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) + false }; - let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); - let to_opt = if is_result { ".ok()" } else { "" }; - let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, - snippet(cx, map_arg.span, ".."), to_opt); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + then { + let span = filter_span.to(map_span); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); + let to_opt = if is_result { ".ok()" } else { "" }; + let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, + snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } + } } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fccdee07877..8a04fd0060d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -896,6 +896,28 @@ declare_clippy_lint! { "using `Iterator::step_by(0)`, which will panic at runtime" } +declare_clippy_lint! { + /// **What it does:** Checks for indirect collection of populated `Option` + /// + /// **Why is this bad?** `Option` is like a collection of 0-1 things, so `flatten` + /// automatically does this without suspicious-looking `unwrap` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let _ = std::iter::empty::>().filter(Option::is_some).map(Option::unwrap); + /// ``` + /// Use instead: + /// ```rust + /// let _ = std::iter::empty::>().flatten(); + /// ``` + pub OPTION_FILTER_MAP, + complexity, + "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation" +} + declare_clippy_lint! { /// **What it does:** Checks for the use of `iter.nth(0)`. /// @@ -1651,6 +1673,7 @@ impl_lint_pass!(Methods => [ FILTER_MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, + OPTION_FILTER_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, MAP_FLATTEN, @@ -1720,10 +1743,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "filter"] => filter_next::check(cx, expr, arg_lists[1]), ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), - ["map", "filter"] => filter_map::check(cx, expr, false), + ["map", "filter"] => filter_map::check(cx, expr, false, method_spans[0]), ["map", "filter_map"] => filter_map_map::check(cx, expr), ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), - ["map", "find"] => filter_map::check(cx, expr, true), + ["map", "find"] => filter_map::check(cx, expr, true, method_spans[0]), ["flat_map", "filter"] => filter_flat_map::check(cx, expr), ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr), ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), diff --git a/tests/ui/option_filter_map.fixed b/tests/ui/option_filter_map.fixed new file mode 100644 index 00000000000..f9d1825ade0 --- /dev/null +++ b/tests/ui/option_filter_map.fixed @@ -0,0 +1,23 @@ +#![warn(clippy::option_filter_map)] +// run-rustfix +fn odds_out(x: i32) -> Option { + if x % 2 == 0 { Some(x) } else { None } +} + +fn main() { + let _ = Some(Some(1)).flatten(); + let _ = Some(Some(1)).flatten(); + let _ = Some(1).map(odds_out).flatten(); + let _ = Some(1).map(odds_out).flatten(); + + let _ = vec![Some(1)].into_iter().flatten(); + let _ = vec![Some(1)].into_iter().flatten(); + let _ = vec![1] + .into_iter() + .map(odds_out) + .flatten(); + let _ = vec![1] + .into_iter() + .map(odds_out) + .flatten(); +} diff --git a/tests/ui/option_filter_map.rs b/tests/ui/option_filter_map.rs new file mode 100644 index 00000000000..588e1ccccce --- /dev/null +++ b/tests/ui/option_filter_map.rs @@ -0,0 +1,25 @@ +#![warn(clippy::option_filter_map)] +// run-rustfix +fn odds_out(x: i32) -> Option { + if x % 2 == 0 { Some(x) } else { None } +} + +fn main() { + let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); + let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap()); + let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap); + let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap()); + + let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap); + let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap()); + let _ = vec![1] + .into_iter() + .map(odds_out) + .filter(Option::is_some) + .map(Option::unwrap); + let _ = vec![1] + .into_iter() + .map(odds_out) + .filter(|o| o.is_some()) + .map(|o| o.unwrap()); +} diff --git a/tests/ui/option_filter_map.stderr b/tests/ui/option_filter_map.stderr new file mode 100644 index 00000000000..31a82969d5a --- /dev/null +++ b/tests/ui/option_filter_map.stderr @@ -0,0 +1,56 @@ +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:8:27 + | +LL | let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + | + = note: `-D clippy::option-filter-map` implied by `-D warnings` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:9:27 + | +LL | let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:10:35 + | +LL | let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:11:35 + | +LL | let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:13:39 + | +LL | let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:14:39 + | +LL | let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:18:10 + | +LL | .filter(Option::is_some) + | __________^ +LL | | .map(Option::unwrap); + | |____________________________^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:23:10 + | +LL | .filter(|o| o.is_some()) + | __________^ +LL | | .map(|o| o.unwrap()); + | |____________________________^ help: consider using `flatten` instead: `flatten()` + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From 21083875d211c29fcfa4a21fcd66d4601d2b618b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 10 Mar 2021 23:40:20 -0600 Subject: Destructure args in methods module --- clippy_lints/src/methods/bind_instead_of_map.rs | 14 +- clippy_lints/src/methods/bytes_nth.rs | 11 +- clippy_lints/src/methods/expect_used.rs | 4 +- clippy_lints/src/methods/filetype_is_file.rs | 4 +- clippy_lints/src/methods/filter_map.rs | 50 ++-- clippy_lints/src/methods/filter_map_identity.rs | 15 +- clippy_lints/src/methods/filter_map_next.rs | 7 +- clippy_lints/src/methods/filter_next.rs | 11 +- clippy_lints/src/methods/flat_map_identity.rs | 4 +- clippy_lints/src/methods/get_unwrap.rs | 20 +- clippy_lints/src/methods/iter_cloned_collect.rs | 4 +- clippy_lints/src/methods/iter_count.rs | 8 +- clippy_lints/src/methods/iter_next_slice.rs | 4 +- clippy_lints/src/methods/iter_nth.rs | 14 +- clippy_lints/src/methods/iter_nth_zero.rs | 6 +- clippy_lints/src/methods/iter_skip_next.rs | 22 +- clippy_lints/src/methods/iterator_step_by_zero.rs | 4 +- .../src/methods/manual_saturating_arithmetic.rs | 13 +- .../src/methods/map_collect_result_unit.rs | 9 +- clippy_lints/src/methods/map_flatten.rs | 19 +- clippy_lints/src/methods/map_unwrap_or.rs | 21 +- clippy_lints/src/methods/mod.rs | 283 ++++++++++++--------- clippy_lints/src/methods/ok_expect.rs | 6 +- clippy_lints/src/methods/option_as_ref_deref.rs | 16 +- clippy_lints/src/methods/option_map_or_none.rs | 22 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 22 +- clippy_lints/src/methods/search_is_some.rs | 88 ++++--- clippy_lints/src/methods/skip_while_next.rs | 2 +- clippy_lints/src/methods/string_extend_chars.rs | 7 +- clippy_lints/src/methods/suspicious_map.rs | 9 +- clippy_lints/src/methods/uninit_assumed_init.rs | 8 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 4 +- clippy_lints/src/methods/unnecessary_fold.rs | 33 +-- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 11 +- clippy_lints/src/methods/unwrap_used.rs | 4 +- clippy_lints/src/methods/useless_asref.rs | 3 +- clippy_lints/src/methods/zst_offset.rs | 5 +- 37 files changed, 412 insertions(+), 375 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 0ba8a98a018..8ccb8f4268c 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -80,7 +80,7 @@ pub(crate) trait BindInsteadOfMap { fn lint_closure_autofixable( cx: &LateContext<'_>, expr: &hir::Expr<'_>, - args: &[hir::Expr<'_>], + recv: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>, closure_args_span: Span, ) -> bool { @@ -103,7 +103,7 @@ pub(crate) trait BindInsteadOfMap { }; let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, args[0].span, ".."); + let option_snip = snippet(cx, recv.span, ".."); let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); span_lint_and_sugg( cx, @@ -158,17 +158,17 @@ pub(crate) trait BindInsteadOfMap { } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { - if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) { + fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { + if !match_type(cx, cx.typeck_results().expr_ty(recv), Self::TYPE_QPATH) { return false; } - match args[1].kind { + match arg.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) { + if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) { true } else { Self::lint_closure(cx, expr, closure_expr) @@ -182,7 +182,7 @@ pub(crate) trait BindInsteadOfMap { expr.span, Self::no_op_msg().as_ref(), "use the expression directly", - snippet(cx, args[0].span, "..").into(), + snippet(cx, recv.span, "..").into(), Applicability::MachineApplicable, ); true diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 4f88f80a304..77f140510b6 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -3,16 +3,15 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; use super::BYTES_NTH; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) { if_chain! { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(&iter_args[0]).peel_refs(); + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if is_type_diagnostic_item(cx, ty, sym::string_type) { Some("String") } else if ty.is_str() { @@ -31,8 +30,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &' "try", format!( "{}.as_bytes().get({})", - snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability), - snippet_with_applicability(cx, args[1].span, "..", &mut applicability) + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + snippet_with_applicability(cx, n_arg.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 64531b29ade..63a834fdce0 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -7,8 +7,8 @@ use rustc_span::sym; use super::EXPECT_USED; /// lint use of `expect()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((EXPECT_USED, "an Option", "None")) diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 39d2f15dbc8..7b2967feb0f 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -8,8 +8,8 @@ use rustc_span::source_map::Span; use super::FILETYPE_IS_FILE; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let ty = cx.typeck_results().expr_ty(&args[0]); +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); if !match_type(cx, ty, &paths::FILE_TYPE) { return; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 68f8480dc51..45d1ed953b4 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -46,21 +46,17 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy } } -fn is_option_filter_map<'tcx>( - cx: &LateContext<'tcx>, - filter_arg: &'tcx hir::Expr<'_>, - map_arg: &'tcx hir::Expr<'_>, -) -> bool { +fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) } /// lint use of `filter().map()` for `Iterators` -fn lint_filter_some_map_unwrap<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - filter_recv: &'tcx hir::Expr<'_>, - filter_arg: &'tcx hir::Expr<'_>, - map_arg: &'tcx hir::Expr<'_>, +fn lint_filter_some_map_unwrap( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + filter_recv: &hir::Expr<'_>, + filter_arg: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, target_span: Span, methods_span: Span, ) { @@ -86,14 +82,28 @@ fn lint_filter_some_map_unwrap<'tcx>( } /// lint use of `filter().map()` or `find().map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool, target_span: Span) { +#[allow(clippy::too_many_arguments)] +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + filter_recv: &hir::Expr<'_>, + filter_arg: &hir::Expr<'_>, + filter_span: Span, + map_recv: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, + map_span: Span, + is_find: bool, +) { + lint_filter_some_map_unwrap( + cx, + expr, + filter_recv, + filter_arg, + map_arg, + map_span, + filter_span.with_hi(expr.span.hi()), + ); if_chain! { - if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; - if let ExprKind::MethodCall(_, _, [filter_recv, filter_arg], filter_span) = map_recv.kind; - then { - lint_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, - map_arg, target_span, filter_span.to(map_span)); - if_chain! { if is_trait_method(cx, map_recv, sym::Iterator); // filter(|x| ...is_some())... @@ -148,7 +158,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_ }; if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); then { - let span = filter_span.to(map_span); + let span = filter_span.with_hi(expr.span.hi()); let (filter_name, lint) = if is_find { ("find", MANUAL_FIND_MAP) } else { @@ -160,7 +170,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_ snippet(cx, map_arg.span, ".."), to_opt); span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); } - } - } } } diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 80598d88508..3a61f4ccad7 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -8,15 +8,8 @@ use rustc_span::{source_map::Span, sym}; use super::FILTER_MAP_IDENTITY; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - filter_map_args: &[hir::Expr<'_>], - filter_map_span: Span, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { if is_trait_method(cx, expr, sym::Iterator) { - let arg_node = &filter_map_args[1].kind; - let apply_lint = |message: &str| { span_lint_and_sugg( cx, @@ -30,8 +23,8 @@ pub(super) fn check( }; if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); + if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind; + let body = cx.tcx.hir().body(body_id); if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind; if path_to_local_id(&body.value, binding_id); @@ -41,7 +34,7 @@ pub(super) fn check( } if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; + if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind; if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index ba57abd16c9..2b19e4ee8c0 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -14,7 +14,8 @@ const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, - filter_args: &'tcx [hir::Expr<'_>], + recv: &'tcx hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, msrv: Option<&RustcVersion>, ) { if is_trait_method(cx, expr, sym::Iterator) { @@ -24,9 +25,9 @@ pub(super) fn check<'tcx>( 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, ".."); + let filter_snippet = snippet(cx, arg.span, ".."); if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, filter_args[0].span, ".."); + let iter_snippet = snippet(cx, recv.span, ".."); span_lint_and_sugg( cx, FILTER_MAP_NEXT, diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 6cd24334414..172714f6b01 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -9,14 +9,19 @@ use rustc_span::sym; use super::FILTER_NEXT; /// lint use of `filter().next()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + filter_arg: &'tcx hir::Expr<'_>, +) { // lint if caller of `.filter().next()` is an Iterator if is_trait_method(cx, expr, sym::Iterator) { 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, ".."); + let filter_snippet = snippet(cx, filter_arg.span, ".."); if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, filter_args[0].span, ".."); + let iter_snippet = snippet(cx, recv.span, ".."); // add note if not multi-line span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 034ea6c6562..664885a2f0e 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -12,11 +12,11 @@ use super::FLAT_MAP_IDENTITY; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, - flat_map_args: &'tcx [hir::Expr<'_>], + flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { if is_trait_method(cx, expr, sym::Iterator) { - let arg_node = &flat_map_args[1].kind; + let arg_node = &flat_map_arg.kind; let apply_lint = |message: &str| { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index b3a9743c614..54f28064384 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -11,18 +11,20 @@ use rustc_span::sym; use super::GET_UNWRAP; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: &'tcx [hir::Expr<'_>], is_mut: bool) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'tcx>, + get_arg: &'tcx hir::Expr<'_>, + is_mut: bool, +) { // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`, // because they do not implement `IndexMut` 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) - } else { - return; // not linting on a .get().unwrap() chain or variant - }; + let expr_ty = cx.typeck_results().expr_ty(recv); + let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability); let mut needs_ref; - let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() { + let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { needs_ref = get_args_str.parse::().is_ok(); "slice" } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) { @@ -77,7 +79,7 @@ pub(super) fn check<'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, recv.span, "..", &mut applicability), get_args_str ), applicability, diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index 848f47e39f6..739f313716e 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -9,10 +9,10 @@ use rustc_span::sym; use super::ITER_CLONED_COLLECT; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); - if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])); + if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)); if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); then { diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index e394a8fe819..c6b7c7cd179 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -10,9 +10,9 @@ use rustc_span::sym; use super::ITER_COUNT; -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], iter_method: &str) { - let ty = cx.typeck_results().expr_ty(&iter_args[0]); - let caller_type = if derefs_to_slice(cx, &iter_args[0], ty).is_some() { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: &str) { + let ty = cx.typeck_results().expr_ty(recv); + let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" } else if is_type_diagnostic_item(cx, ty, sym::vec_type) { "Vec" @@ -42,7 +42,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &' "try", format!( "{}.len()", - snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability), + snippet_with_applicability(cx, recv.span, "..", &mut applicability), ), applicability, ); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index e9b37b6f2bd..dab0a43a096 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -13,9 +13,7 @@ use rustc_span::symbol::sym; use super::ITER_NEXT_SLICE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { - let caller_expr = &iter_args[0]; - +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, caller_expr: &'tcx hir::Expr<'_>) { // 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); diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index c46af427b3c..c2232239fe4 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -11,20 +11,20 @@ use super::ITER_NTH; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, - nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]], + iter_recv: &'tcx hir::Expr<'tcx>, + nth_recv: &hir::Expr<'_>, + nth_arg: &hir::Expr<'_>, is_mut: bool, ) { - let iter_args = nth_and_iter_args[1]; let mut_str = if is_mut { "_mut" } else { "" }; - let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { + let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() { "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::vec_type) { "Vec" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vecdeque_type) { + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::vecdeque_type) { "VecDeque" } else { - let nth_args = nth_and_iter_args[0]; - iter_nth_zero::check(cx, expr, &nth_args); + iter_nth_zero::check(cx, expr, nth_recv, nth_arg); return; // caller is not a type that we want to lint }; diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index a12f672739c..52d7c15332e 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -10,10 +10,10 @@ use rustc_span::sym; use super::ITER_NTH_ZERO; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, expr, sym::Iterator); - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]); + if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args expr.span, "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)), + format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut applicability)), applicability, ); } diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index b1d398876d3..e32594757d0 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -8,19 +8,17 @@ use rustc_span::sym; use super::ITER_SKIP_NEXT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator if is_trait_method(cx, expr, sym::Iterator) { - if let [caller, n] = skip_args { - span_lint_and_sugg( - cx, - ITER_SKIP_NEXT, - expr.span.trim_start(caller.span).unwrap(), - "called `skip(..).next()` on an iterator", - "use `nth` instead", - format!(".nth({})", snippet(cx, n.span, "..")), - Applicability::MachineApplicable, - ); - } + span_lint_and_sugg( + cx, + ITER_SKIP_NEXT, + expr.span.trim_start(recv.span).unwrap(), + "called `skip(..).next()` on an iterator", + "use `nth` instead", + format!(".nth({})", snippet(cx, arg.span, "..")), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 3baa580314f..06b12998b1a 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -7,9 +7,9 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { if is_trait_method(cx, expr, sym::Iterator) { - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) { + if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg) { span_lint( cx, ITERATOR_STEP_BY_ZERO, diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index f16699322d1..6c5a842a912 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -8,11 +8,14 @@ use rustc_hir as hir; use rustc_lint::LateContext; use rustc_target::abi::LayoutOf; -pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) { - let unwrap_arg = &args[0][1]; - let arith_lhs = &args[1][0]; - let arith_rhs = &args[1][1]; - +pub fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + arith_lhs: &hir::Expr<'_>, + arith_rhs: &hir::Expr<'_>, + unwrap_arg: &hir::Expr<'_>, + arith: &str, +) { let ty = cx.typeck_results().expr_ty(arith_lhs); if !ty.is_integral() { return; diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index e4402b2da21..82063ad70b5 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -14,13 +14,13 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, - map_args: &[hir::Expr<'_>], - collect_args: &[hir::Expr<'_>], + iter: &hir::Expr<'_>, + map_fn: &hir::Expr<'_>, + collect_recv: &hir::Expr<'_>, ) { if_chain! { // called on Iterator - if let [map_expr] = collect_args; - if is_trait_method(cx, map_expr, sym::Iterator); + if is_trait_method(cx, collect_recv, sym::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); @@ -28,7 +28,6 @@ pub(super) fn check( 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, diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 4bc52b036a8..e8ad16bc0de 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -11,10 +11,15 @@ use rustc_span::symbol::sym; use super::MAP_FLATTEN; /// lint use of `map().flatten()` for `Iterators` and 'Options' -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, +) { // lint if caller of `.map().flatten()` is an Iterator if is_trait_method(cx, expr, sym::Iterator) { - let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); + let map_closure_ty = cx.typeck_results().expr_ty(map_arg); let is_map_to_option = match map_closure_ty.kind() { ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { let map_closure_sig = match map_closure_ty.kind() { @@ -34,12 +39,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // `(...).map(...)` has type `impl Iterator> "flat_map" }; - let func_snippet = snippet(cx, map_args[1].span, ".."); + let func_snippet = snippet(cx, map_arg.span, ".."); let hint = format!(".{0}({1})", method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span.with_lo(map_args[0].span.hi()), + expr.span.with_lo(recv.span.hi()), "called `map(..).flatten()` on an `Iterator`", &format!("try using `{}` instead", method_to_use), hint, @@ -48,13 +53,13 @@ pub(super) fn check<'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 func_snippet = snippet(cx, map_args[1].span, ".."); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) { + let func_snippet = snippet(cx, map_arg.span, ".."); let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span.with_lo(map_args[0].span.hi()), + expr.span.with_lo(recv.span.hi()), "called `map(..).flatten()` on an `Option`", "try using `and_then` instead", hint, diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index deb4b4492b5..4330fea727b 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -18,22 +18,23 @@ const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, - map_args: &'tcx [hir::Expr<'_>], - unwrap_args: &'tcx [hir::Expr<'_>], + recv: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, + unwrap_arg: &'tcx hir::Expr<'_>, msrv: Option<&RustcVersion>, ) -> bool { if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { return false; } // lint if the caller of `map()` is an `Option` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); if is_option || is_result { // Don't make a suggestion that may fail to compile due to mutably borrowing // the same variable twice. - let map_mutated_vars = mutated_variables(&map_args[0], cx); - let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); + let map_mutated_vars = mutated_variables(recv, cx); + let unwrap_mutated_vars = mutated_variables(unwrap_arg, 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 false; @@ -51,14 +52,14 @@ pub(super) fn check<'tcx>( `.map_or_else(, )` instead" }; // get snippets for args to map() and unwrap_or_else() - let map_snippet = snippet(cx, map_args[1].span, ".."); - let unwrap_snippet = snippet(cx, unwrap_args[1].span, ".."); + let map_snippet = snippet(cx, map_arg.span, ".."); + let unwrap_snippet = snippet(cx, unwrap_arg.span, ".."); // lint, with note if neither arg is > 1 line and both map() and // unwrap_or_else() have the same span 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(); + let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt(); if same_span && !multiline { - let var_snippet = snippet(cx, map_args[0].span, ".."); + let var_snippet = snippet(cx, recv.span, ".."); span_lint_and_sugg( cx, MAP_UNWRAP_OR, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8a04fd0060d..9a95354db7a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -62,17 +62,18 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty}; +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::{sym, SymbolStr}; +use rustc_span::symbol::SymbolStr; +use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { @@ -1704,134 +1705,32 @@ impl_lint_pass!(Methods => [ IMPLICIT_CLONE ]); +/// Extracts a method call name, args, and `Span` of the method name. +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'tcx [hir::Expr<'tcx>], Span)> { + if let ExprKind::MethodCall(path, span, args, _) = recv.kind { + if !args.iter().any(|e| e.span.from_expansion()) { + return Some((path.ident.name.as_str(), args, span)); + } + } + None +} + +/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str` +macro_rules! method_call { + ($expr:expr) => { + method_call($expr) + .as_ref() + .map(|&(ref name, args, span)| (&**name, args, span)) + }; +} + impl<'tcx> LateLintPass<'tcx> for Methods { - #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if in_macro(expr.span) { return; } - let (method_names, arg_lists, method_spans) = method_calls(expr, 2); - let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); - let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); - - match method_names.as_slice() { - ["unwrap", "get"] => get_unwrap::check(cx, expr, arg_lists[1], false), - ["unwrap", "get_mut"] => get_unwrap::check(cx, expr, arg_lists[1], true), - ["unwrap", ..] => unwrap_used::check(cx, expr, arg_lists[0]), - ["expect", "ok"] => ok_expect::check(cx, expr, arg_lists[1]), - ["expect", ..] => expect_used::check(cx, expr, arg_lists[0]), - ["unwrap_or", "map"] => option_map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => { - if !map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { - unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"); - } - }, - ["map_or", ..] => option_map_or_none::check(cx, expr, arg_lists[0]), - ["and_then", ..] => { - let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, arg_lists[0]); - let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, arg_lists[0]); - if !biom_option_linted && !biom_result_linted { - unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "and"); - } - }, - ["or_else", ..] => { - if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, arg_lists[0]) { - unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "or"); - } - }, - ["next", "filter"] => filter_next::check(cx, expr, arg_lists[1]), - ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), - ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), - ["map", "filter"] => filter_map::check(cx, expr, false, method_spans[0]), - ["map", "filter_map"] => filter_map_map::check(cx, expr), - ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), - ["map", "find"] => filter_map::check(cx, expr, true, method_spans[0]), - ["flat_map", "filter"] => filter_flat_map::check(cx, expr), - ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr), - ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), - ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]), - [option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => { - search_is_some::check( - cx, - expr, - "find", - option_check_method, - arg_lists[1], - arg_lists[0], - method_spans[1], - ) - }, - [option_check_method, "position"] - if "is_some" == *option_check_method || "is_none" == *option_check_method => - { - search_is_some::check( - cx, - expr, - "position", - option_check_method, - arg_lists[1], - arg_lists[0], - method_spans[1], - ) - }, - [option_check_method, "rposition"] - if "is_some" == *option_check_method || "is_none" == *option_check_method => - { - search_is_some::check( - cx, - expr, - "rposition", - option_check_method, - arg_lists[1], - arg_lists[0], - method_spans[1], - ) - }, - ["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]), - ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), - ["count", "iter"] => iter_count::check(cx, expr, &arg_lists[1], "iter"), - ["count", "iter_mut"] => iter_count::check(cx, expr, &arg_lists[1], "iter_mut"), - ["nth", "iter"] => iter_nth::check(cx, expr, &arg_lists, false), - ["nth", "iter_mut"] => iter_nth::check(cx, expr, &arg_lists, true), - ["nth", "bytes"] => bytes_nth::check(cx, expr, &arg_lists[1]), - ["nth", ..] => iter_nth_zero::check(cx, expr, arg_lists[0]), - ["step_by", ..] => iterator_step_by_zero::check(cx, expr, arg_lists[0]), - ["next", "skip"] => iter_skip_next::check(cx, expr, arg_lists[1]), - ["collect", "cloned"] => iter_cloned_collect::check(cx, expr, arg_lists[1]), - ["as_ref"] => useless_asref::check(cx, expr, "as_ref", arg_lists[0]), - ["as_mut"] => useless_asref::check(cx, expr, "as_mut", arg_lists[0]), - ["fold", ..] => unnecessary_fold::check(cx, expr, arg_lists[0], method_spans[0]), - ["filter_map", ..] => { - unnecessary_filter_map::check(cx, expr, arg_lists[0]); - filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); - }, - ["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]), - ["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr), - ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { - manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..]) - }, - ["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => { - zst_offset::check(cx, expr, arg_lists[0]) - }, - ["is_file", ..] => filetype_is_file::check(cx, expr, arg_lists[0]), - ["map", "as_ref"] => { - option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) - }, - ["map", "as_mut"] => { - option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) - }, - ["unwrap_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"), - ["get_or_insert_with", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "get_or_insert"), - ["ok_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "ok_or"), - ["collect", "map"] => map_collect_result_unit::check(cx, expr, arg_lists[1], arg_lists[0]), - ["for_each", "inspect"] => inspect_for_each::check(cx, expr, method_spans[1]), - ["to_owned", ..] => implicit_clone::check(cx, expr, sym::ToOwned), - ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), - ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), - ["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice), - _ => {}, - } + check_methods(cx, expr, self.msrv.as_ref()); match expr.kind { hir::ExprKind::Call(ref func, ref args) => { @@ -2020,6 +1919,140 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } + +#[allow(clippy::too_many_lines)] +fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { + if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { + match (name, args) { + ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => { + zst_offset::check(cx, expr, recv) + }, + ("and_then", [arg]) => { + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + } + }, + ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), + ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), + ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("collect", []) => match method_call!(recv) { + Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), + Some(("map", [m_recv, m_arg], _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); + }, + _ => {}, + }, + ("count", []) => match method_call!(recv) { + Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { + iter_count::check(cx, expr, recv2, name); + }, + Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), + _ => {}, + }, + ("expect", [_]) => match method_call!(recv) { + Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + _ => expect_used::check(cx, expr, recv), + }, + ("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg), + ("filter_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg); + filter_map_identity::check(cx, expr, arg, span); + }, + ("flat_map", [flm_arg]) => match method_call!(recv) { + Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr), + Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr), + _ => flat_map_identity::check(cx, expr, flm_arg, span), + }, + ("flatten", []) => { + if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { + map_flatten::check(cx, expr, recv, map_arg); + } + }, + ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), + ("for_each", [_]) => { + if let Some(("inspect", [_, _], span2)) = method_call!(recv) { + inspect_for_each::check(cx, expr, span2); + } + }, + ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), + ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("map", [m_arg]) => { + if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) { + match (name, args) { + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), + ("filter", [f_arg]) => { + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false) + }, + ("filter_map", [_]) => filter_map_map::check(cx, expr), + ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), + _ => {}, + } + } + }, + ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), + ("next", []) => { + if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { + match (name, args) { + ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), + ("iter", []) => iter_next_slice::check(cx, expr, recv), + ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg), + ("skip_while", [_]) => skip_while_next::check(cx, expr), + _ => {}, + } + } + }, + ("nth", [n_arg]) => match method_call!(recv) { + Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + _ => iter_nth_zero::check(cx, expr, recv, n_arg), + }, + ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("or_else", [arg]) => { + if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + } + }, + ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), + ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr), + ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned), + ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path), + ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice), + ("unwrap", []) => match method_call!(recv) { + Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), + Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), + _ => unwrap_used::check(cx, expr, recv), + }, + ("unwrap_or", [u_arg]) => match method_call!(recv) { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { + manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); + }, + Some(("map", [m_recv, m_arg], span)) => { + option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span) + }, + _ => {}, + }, + ("unwrap_or_else", [u_arg]) => match method_call!(recv) { + Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, + _ => unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"), + }, + _ => {}, + } + } +} + +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { + if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { + search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span) + } +} + /// Used for `lint_binary_expr_with_method_call`. #[derive(Copy, Clone)] struct BinaryExprInfo<'a> { diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index e6ce9cac397..d0b1b4b84be 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -9,11 +9,11 @@ use rustc_span::sym; use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { // lint if the caller of `ok()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type); - let result_type = cx.typeck_results().expr_ty(&ok_args[0]); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); + let result_type = cx.typeck_results().expr_ty(recv); if let Some(error_type) = get_error_type(cx, result_type); if has_debug_impl(error_type, cx); diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index d11ede080dc..1367a0c21d8 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -18,8 +18,8 @@ const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, - as_ref_args: &[hir::Expr<'_>], - map_args: &[hir::Expr<'_>], + as_ref_recv: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, is_mut: bool, msrv: Option<&RustcVersion>, ) { @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); - let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); + let option_ty = cx.typeck_results().expr_ty(as_ref_recv); if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { return; } @@ -46,9 +46,9 @@ pub(super) fn check<'tcx>( &paths::VEC_AS_MUT_SLICE, ]; - let is_deref = match map_args[1].kind { + let is_deref = match map_arg.kind { hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_args[1].hir_id) + .qpath_res(expr_qpath, map_arg.hir_id) .opt_def_id() .map_or(false, |fun_def_id| { deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) @@ -96,12 +96,12 @@ pub(super) fn check<'tcx>( if is_deref { let current_method = if is_mut { - format!(".as_mut().map({})", snippet(cx, map_args[1].span, "..")) + format!(".as_mut().map({})", snippet(cx, map_arg.span, "..")) } else { - format!(".as_ref().map({})", snippet(cx, map_args[1].span, "..")) + format!(".as_ref().map({})", snippet(cx, map_arg.span, "..")) }; let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; - let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint); + let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint); let suggestion = format!("try using {} instead", method_hint); let msg = format!( diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index d93db2c22e4..013a6f90ac9 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -11,9 +11,15 @@ use super::OPTION_MAP_OR_NONE; use super::RESULT_MAP_OR_INTO_OPTION; /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type); +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + def_arg: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, +) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` @@ -25,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } let (lint_name, msg, instead, hint) = { - let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind { + let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { match_qpath(qpath, &paths::OPTION_NONE) } else { return; @@ -36,15 +42,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map return; } - let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind { + let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { match_qpath(qpath, &paths::OPTION_SOME) } else { false }; if is_option { - let self_snippet = snippet(cx, map_or_args[0].span, ".."); - let func_snippet = snippet(cx, map_or_args[2].span, ".."); + let self_snippet = snippet(cx, recv.span, ".."); + let func_snippet = snippet(cx, map_arg.span, ".."); let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ `and_then(..)` instead"; ( @@ -56,7 +62,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } else if f_arg_is_some { let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ `ok()` instead"; - let self_snippet = snippet(cx, map_or_args[0].span, ".."); + let self_snippet = snippet(cx, recv.span, ".."); ( RESULT_MAP_OR_INTO_OPTION, msg, diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index e252abc177a..5bca49dec24 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -18,13 +18,15 @@ use super::MAP_UNWRAP_OR; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &rustc_hir::Expr<'_>, - map_args: &'tcx [rustc_hir::Expr<'_>], - unwrap_args: &'tcx [rustc_hir::Expr<'_>], + recv: &rustc_hir::Expr<'_>, + map_arg: &'tcx rustc_hir::Expr<'_>, + unwrap_recv: &rustc_hir::Expr<'_>, + unwrap_arg: &'tcx rustc_hir::Expr<'_>, map_span: Span, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { - if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) { + if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) { // Do not lint if the `map` argument uses identifiers in the `map` // argument that are also used in the `unwrap_or` argument @@ -32,27 +34,27 @@ pub(super) fn check<'tcx>( cx, identifiers: FxHashSet::default(), }; - unwrap_visitor.visit_expr(&unwrap_args[1]); + unwrap_visitor.visit_expr(unwrap_arg); let mut map_expr_visitor = MapExprVisitor { cx, identifiers: unwrap_visitor.identifiers, found_identifier: false, }; - map_expr_visitor.visit_expr(&map_args[1]); + map_expr_visitor.visit_expr(map_arg); if map_expr_visitor.found_identifier { return; } } - if differing_macro_contexts(unwrap_args[1].span, map_span) { + if differing_macro_contexts(unwrap_arg.span, map_span) { return; } let mut applicability = Applicability::MachineApplicable; // get snippet for unwrap_or() - let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability); + let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability); // lint message // comparing the snippet from source to raw text ("None") below is safe // because we already have checked the type. @@ -70,14 +72,14 @@ pub(super) fn check<'tcx>( ); span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { - let map_arg_span = map_args[1].span; + let map_arg_span = map_arg.span; let mut suggestion = vec![ ( map_span, String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }), ), - (expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")), + (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")), ]; if !unwrap_snippet_none { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index de7d168295f..8a94d7f4155 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -15,29 +15,31 @@ use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` /// or calling `find()` on a string followed by `is_some()` or `is_none()` -#[allow(clippy::too_many_lines)] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, + cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, search_method: &str, - option_check_method: &str, - search_args: &'tcx [hir::Expr<'_>], - is_some_args: &'tcx [hir::Expr<'_>], + is_some: bool, + search_recv: &hir::Expr<'_>, + search_arg: &'tcx hir::Expr<'_>, + is_some_recv: &hir::Expr<'_>, method_span: Span, ) { + let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator - if is_trait_method(cx, &is_some_args[0], sym::Iterator) { + if is_trait_method(cx, is_some_recv, sym::Iterator) { let msg = format!( "called `{}()` after searching an `Iterator` with `{}`", option_check_method, search_method ); - let search_snippet = snippet(cx, search_args[1].span, ".."); + let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let any_search_snippet = if_chain! { if search_method == "find"; - if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind; + if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind; let closure_body = cx.tcx.hir().body(body_id); if let Some(closure_arg) = closure_body.params.get(0); then { @@ -54,38 +56,34 @@ pub(super) fn check<'tcx>( } }; // add note if not multi-line - match option_check_method { - "is_some" => { - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `any()` instead", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - Applicability::MachineApplicable, - ); - }, - "is_none" => { - let iter = snippet(cx, search_args[0].span, ".."); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - expr.span, - &msg, - "use `!_.any()` instead", - format!( - "!{}.any({})", - iter, - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - Applicability::MachineApplicable, - ); - }, - _ => (), + if is_some { + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `any()` instead", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + } else { + let iter = snippet(cx, search_recv.span, ".."); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.any()` instead", + format!( + "!{}.any({})", + iter, + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); } } else { let hint = format!( @@ -110,14 +108,14 @@ pub(super) fn check<'tcx>( } }; if_chain! { - if is_string_or_str_slice(&search_args[0]); - if is_string_or_str_slice(&search_args[1]); + if is_string_or_str_slice(&search_recv); + if is_string_or_str_slice(&search_arg); then { let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); match option_check_method { "is_some" => { let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); span_lint_and_sugg( cx, SEARCH_IS_SOME, @@ -129,9 +127,9 @@ pub(super) fn check<'tcx>( ); }, "is_none" => { - let string = snippet(cx, search_args[0].span, ".."); + let string = snippet(cx, search_recv.span, ".."); let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); span_lint_and_sugg( cx, SEARCH_IS_SOME, diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index 3db83785b59..9f0b6c34ea2 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -7,7 +7,7 @@ use rustc_span::sym; use super::SKIP_WHILE_NEXT; /// lint use of `skip_while().next()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, _skip_while_args: &'tcx [hir::Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.skip_while().next()` is an Iterator if is_trait_method(cx, expr, sym::Iterator) { span_lint_and_help( diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 5c688ac5621..6e7890a3080 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -10,12 +10,11 @@ use rustc_span::symbol::sym; use super::STRING_EXTEND_CHARS; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) { return; } - let arg = &args[1]; if let Some(arglists) = method_chain_args(arg, &["chars"]) { let target = &arglists[0][0]; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); @@ -36,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp "try this", format!( "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "..", &mut applicability), + snippet_with_applicability(cx, recv.span, "..", &mut applicability), ref_str, snippet_with_applicability(cx, target.span, "..", &mut applicability) ), diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 7015bd54c35..0fd0668c734 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -8,15 +8,8 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; -pub fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - map_args: &[hir::Expr<'_>], - count_args: &[hir::Expr<'_>], -) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { if_chain! { - if let [count_recv] = count_args; - if let [_, map_arg] = map_args; if is_trait_method(cx, count_recv, sym::Iterator); let closure = expr_or_init(cx, map_arg); if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id); diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index f2f6ef4be6c..3cc1912b15a 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -8,18 +8,18 @@ use rustc_middle::ty::{self, Ty}; use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::Call(ref callee, ref args) = expr.kind; + if let hir::ExprKind::Call(ref callee, ref args) = recv.kind; if args.is_empty(); if let hir::ExprKind::Path(ref path) = callee.kind; if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); - if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(outer)); + if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( cx, UNINIT_ASSUMED_INIT, - outer.span, + expr.span, "this call for this type may be undefined behavior" ); } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 48d905ab833..dac7e039e37 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -10,12 +10,12 @@ use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if !is_trait_method(cx, expr, sym::Iterator) { return; } - if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].kind { + if let hir::ExprKind::Closure(_, _, body_id, ..) = arg.kind { let body = cx.tcx.hir().body(body_id); let arg_id = body.params[0].pat.hir_id; let mutates_arg = diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 1268fd4bda9..7c16470348f 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -11,11 +11,17 @@ use rustc_span::{source_map::Span, sym}; use super::UNNECESSARY_FOLD; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + init: &hir::Expr<'_>, + acc: &hir::Expr<'_>, + fold_span: Span, +) { fn check_fold_with_op( cx: &LateContext<'_>, expr: &hir::Expr<'_>, - fold_args: &[hir::Expr<'_>], + acc: &hir::Expr<'_>, fold_span: Span, op: hir::BinOpKind, replacement_method_name: &str, @@ -23,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir ) { if_chain! { // Extract the body of the closure passed to fold - if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind; + if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind; let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); @@ -74,25 +80,14 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir return; } - assert!( - fold_args.len() == 3, - "Expected fold_args to have three entries - the receiver, the initial value and the closure" - ); - // Check if the first argument to .fold is a suitable literal - if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind { + if let hir::ExprKind::Lit(ref lit) = init.kind { match lit.node { - ast::LitKind::Bool(false) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true) - }, - ast::LitKind::Bool(true) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true) - }, - ast::LitKind::Int(0, _) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false) - }, + ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true), + ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true), + ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false), ast::LitKind::Int(1, _) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false) + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false) }, _ => (), } diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a86185bf0a6..b7380883a5e 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -14,14 +14,15 @@ use super::UNNECESSARY_LAZY_EVALUATIONS; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, - args: &'tcx [hir::Expr<'_>], + recv: &'tcx hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); if is_option || is_result { - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + if let hir::ExprKind::Closure(_, _, eid, _, _) = arg.kind { let body = cx.tcx.hir().body(eid); let body_expr = &body.value; @@ -55,7 +56,7 @@ pub(super) fn check<'tcx>( &format!("use `{}` instead", simplify_using), format!( "{0}.{1}({2})", - snippet(cx, args[0].span, ".."), + snippet(cx, recv.span, ".."), simplify_using, snippet(cx, body_expr.span, ".."), ), diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index 2f5806115bd..7fd1948594d 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -7,8 +7,8 @@ use rustc_span::sym; use super::UNWRAP_USED; /// lint use of `unwrap()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs(); +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((UNWRAP_USED, "an Option", "None")) diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index b5505af0f7e..e0b1de68b37 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -10,12 +10,11 @@ use rustc_lint::LateContext; use super::USELESS_ASREF; /// Checks for the `USELESS_ASREF` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { // check if the type after `as_ref` or `as_mut` is the same as before - let recvr = &as_ref_args[0]; let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs index 9f6a7c4db17..0489d0f6fcf 100644 --- a/clippy_lints/src/methods/zst_offset.rs +++ b/clippy_lints/src/methods/zst_offset.rs @@ -6,10 +6,9 @@ use rustc_middle::ty; use super::ZST_OFFSET; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { - if args.len() == 2; - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind(); + if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); if layout.is_zst(); then { -- cgit 1.4.1-3-g733a5 From 5f887d09b87d7c6a9c3a4f6afa62a20847dd2509 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 30 Mar 2021 13:35:30 -0500 Subject: Add if_chain lints --- clippy_lints/src/lib.rs | 4 + clippy_lints/src/utils/internal_lints.rs | 177 ++++++++++++++++++++++++++++++- tests/ui-internal/if_chain_style.rs | 92 ++++++++++++++++ tests/ui-internal/if_chain_style.stderr | 85 +++++++++++++++ 4 files changed, 353 insertions(+), 5 deletions(-) create mode 100644 tests/ui-internal/if_chain_style.rs create mode 100644 tests/ui-internal/if_chain_style.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3e0a3e3ef99..f5941977dd1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -550,6 +550,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] + &utils::internal_lints::IF_CHAIN_STYLE, + #[cfg(feature = "internal-lints")] &utils::internal_lints::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, @@ -1026,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::IfChainStyle); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1442,6 +1445,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::IF_CHAIN_STYLE), LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index c496ff1fb24..266b88beeec 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,8 +1,10 @@ use crate::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; -use clippy_utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq}; +use clippy_utils::{ + is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq, +}; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; use rustc_ast::visit::FnKind; @@ -14,15 +16,17 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, + BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt, + StmtKind, Ty, TyKind, UnOp, }; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_span::{BytePos, Span}; use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -297,6 +301,13 @@ declare_clippy_lint! { "unnecessary conversion between Symbol and string" } +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -1063,3 +1074,159 @@ impl<'tcx> SymbolStrExpr<'tcx> { } } } + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ) + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = match expr.kind { + ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()), + ExprKind::Match( + _, + [arm, ..], + MatchSource::IfLetDesugar { + contains_else_clause: els, + }, + ) => (None, arm.body, els), + _ => return, + }; + let then_block = match then.kind { + ExprKind::Block(block, _) => block, + _ => return, + }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let if_chain_span = match if_chain_span { + None => return, + Some(span) => span, + }; + // check for `if a && b;` + if_chain! { + if let Some(cond) = cond; + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`") + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if matches!(tail.kind, + ExprKind::If(_, _, None) + | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false })); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then {} else { return } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt()) +} diff --git a/tests/ui-internal/if_chain_style.rs b/tests/ui-internal/if_chain_style.rs new file mode 100644 index 00000000000..8e871707aa8 --- /dev/null +++ b/tests/ui-internal/if_chain_style.rs @@ -0,0 +1,92 @@ +#![warn(clippy::if_chain_style)] +#![allow(clippy::no_effect)] + +extern crate if_chain; + +use if_chain::if_chain; + +fn main() { + if true { + let x = ""; + // `if_chain!` inside `if` + if_chain! { + if true; + if true; + then {} + } + } + if_chain! { + if true + // multi-line AND'ed conditions + && false; + if let Some(1) = Some(1); + // `let` before `then` + let x = ""; + then { + (); + } + } + if_chain! { + // single `if` condition + if true; + then { + let x = ""; + // nested if + if true {} + } + } + if_chain! { + // starts with `let ..` + let x = ""; + if let Some(1) = Some(1); + then { + let x = ""; + let x = ""; + // nested if_chain! + if_chain! { + if true; + if true; + then {} + } + } + } +} + +fn negative() { + if true { + (); + if_chain! { + if true; + if true; + then { (); } + } + } + if_chain! { + if true; + let x = ""; + if true; + then { (); } + } + if_chain! { + if true; + if true; + then { + if true { 1 } else { 2 } + } else { + 3 + } + }; + if true { + if_chain! { + if true; + if true; + then {} + } + } else if false { + if_chain! { + if true; + if false; + then {} + } + } +} diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr new file mode 100644 index 00000000000..b53c3ea05da --- /dev/null +++ b/tests/ui-internal/if_chain_style.stderr @@ -0,0 +1,85 @@ +error: this `if` can be part of the inner `if_chain!` + --> $DIR/if_chain_style.rs:9:5 + | +LL | / if true { +LL | | let x = ""; +LL | | // `if_chain!` inside `if` +LL | | if_chain! { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::if-chain-style` implied by `-D warnings` +help: this `let` statement can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:10:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: `if a && b;` should be `if a; if b;` + --> $DIR/if_chain_style.rs:19:12 + | +LL | if true + | ____________^ +LL | | // multi-line AND'ed conditions +LL | | && false; + | |____________________^ + +error: `let` expression should be inside `then { .. }` + --> $DIR/if_chain_style.rs:24:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: this `if` can be part of the outer `if_chain!` + --> $DIR/if_chain_style.rs:35:13 + | +LL | if true {} + | ^^^^^^^^^^ + | +help: this `let` statement can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:33:13 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: `if_chain!` only has one `if` + --> $DIR/if_chain_style.rs:29:5 + | +LL | / if_chain! { +LL | | // single `if` condition +LL | | if true; +LL | | then { +... | +LL | | } +LL | | } + | |_____^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `let` expression should be above the `if_chain!` + --> $DIR/if_chain_style.rs:40:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: this `if_chain!` can be merged with the outer `if_chain!` + --> $DIR/if_chain_style.rs:46:13 + | +LL | / if_chain! { +LL | | if true; +LL | | if true; +LL | | then {} +LL | | } + | |_____________^ + | +help: these `let` statements can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:43:13 + | +LL | / let x = ""; +LL | | let x = ""; + | |_______________________^ + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 827d6aaad4a7ec24dd3191160cefe0c1e0b654b9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 30 Mar 2021 14:59:59 -0500 Subject: Eat dogfood --- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/bytecount.rs | 82 ++++++++-------- clippy_lints/src/checked_conversions.rs | 6 +- clippy_lints/src/collapsible_match.rs | 2 +- clippy_lints/src/default.rs | 43 +++++---- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/exit.rs | 16 ++-- clippy_lints/src/float_literal.rs | 2 +- clippy_lints/src/formatting.rs | 3 +- clippy_lints/src/if_let_mutex.rs | 22 ++--- clippy_lints/src/loops/manual_flatten.rs | 3 +- clippy_lints/src/loops/manual_memcpy.rs | 4 +- clippy_lints/src/loops/needless_collect.rs | 105 ++++++++++----------- clippy_lints/src/loops/needless_range_loop.rs | 74 +++++++-------- clippy_lints/src/loops/same_item_push.rs | 2 +- .../src/loops/while_immutable_condition.rs | 3 +- clippy_lints/src/manual_non_exhaustive.rs | 2 +- clippy_lints/src/map_clone.rs | 4 +- clippy_lints/src/map_unit_fn.rs | 16 ++-- clippy_lints/src/matches.rs | 4 +- clippy_lints/src/mem_replace.rs | 49 +++++----- clippy_lints/src/methods/bytes_nth.rs | 50 +++++----- .../src/methods/from_iter_instead_of_collect.rs | 2 +- clippy_lints/src/methods/mod.rs | 7 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 18 ++-- clippy_lints/src/misc.rs | 102 ++++++++++---------- clippy_lints/src/new_without_default.rs | 96 +++++++++---------- clippy_lints/src/non_copy_const.rs | 22 ++--- clippy_lints/src/panic_unimplemented.rs | 7 +- clippy_lints/src/ptr.rs | 23 +++-- clippy_lints/src/ranges.rs | 49 +++++----- clippy_lints/src/redundant_closure_call.rs | 4 +- clippy_lints/src/suspicious_trait_impl.rs | 7 +- .../src/transmute/transmute_float_to_int.rs | 2 +- clippy_lints/src/try_err.rs | 11 +-- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unit_types/unit_arg.rs | 4 +- clippy_lints/src/unnamed_address.rs | 4 +- clippy_lints/src/unused_self.rs | 25 +++-- clippy_lints/src/unwrap_in_result.rs | 44 ++++----- clippy_lints/src/useless_conversion.rs | 41 ++++---- clippy_lints/src/utils/internal_lints.rs | 4 +- clippy_lints/src/vec_init_then_push.rs | 31 +++--- 43 files changed, 469 insertions(+), 532 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3cef8d2a78b..0ce46e1c214 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -371,8 +371,8 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { if meta_item.path.segments.len() > 1; if let tool_name = meta_item.path.segments[0].ident; if tool_name.name == sym::clippy; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; then { + let lint_name = meta_item.path.segments.last().unwrap().ident.name; return Some(lint_name.as_str()); } } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 846ac08e93a..1546d823167 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -45,52 +45,48 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { if filter.ident.name == sym!(filter); if filter_args.len() == 2; if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind; + let body = cx.tcx.hir().body(body_id); + if body.params.len() == 1; + if let Some(argname) = get_pat_name(&body.params[0].pat); + if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; + if op.node == BinOpKind::Eq; + if match_type(cx, + cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), + &paths::SLICE_ITER); then { - let body = cx.tcx.hir().body(body_id); - if_chain! { - if body.params.len() == 1; - if let Some(argname) = get_pat_name(&body.params[0].pat); - if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; - if op.node == BinOpKind::Eq; - if match_type(cx, - cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), - &paths::SLICE_ITER); - then { - let needle = match get_path_name(l) { - Some(name) if check_arg(name, argname, r) => r, - _ => match get_path_name(r) { - Some(name) if check_arg(name, argname, l) => l, - _ => { return; } - } - }; - 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, _) = - filter_args[0].kind { - let p = path.ident.name; - if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { - &args[0] - } else { - &filter_args[0] - } - } else { - &filter_args[0] - }; - let mut applicability = Applicability::MaybeIncorrect; - span_lint_and_sugg( - cx, - NAIVE_BYTECOUNT, - expr.span, - "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)), - applicability, - ); + let needle = match get_path_name(l) { + Some(name) if check_arg(name, argname, r) => r, + _ => match get_path_name(r) { + Some(name) if check_arg(name, argname, l) => l, + _ => { return; } } }; + 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, _) = + filter_args[0].kind { + let p = path.ident.name; + if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { + &args[0] + } else { + &filter_args[0] + } + } else { + &filter_args[0] + }; + let mut applicability = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + NAIVE_BYTECOUNT, + expr.span, + "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)), + applicability, + ); } }; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index ed46cac493a..b38c70bcc41 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -321,9 +321,8 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: if path.ident.name.as_str() == function; if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind; if let [int] = &*tp.segments; - let name = &int.ident.name.as_str(); - then { + let name = &int.ident.name.as_str(); candidates.iter().find(|c| name == *c).cloned() } else { None @@ -336,9 +335,8 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { if let QPath::Resolved(_, ref path) = *path; if let [ty] = &*path.segments; - let name = &ty.ident.name.as_str(); - then { + let name = &ty.ident.name.as_str(); INTS.iter().find(|c| name == *c).cloned() } else { None diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index e2b3686ddf0..04fff237bb4 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { } fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) { + let expr = strip_singleton_blocks(arm.body); if_chain! { - let expr = strip_singleton_blocks(arm.body); if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; // the outer arm pattern and the inner match if expr_in.span.ctxt() == arm.pat.span.ctxt(); diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 568a174445c..8a2146d93e8 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -84,22 +84,21 @@ impl LateLintPass<'_> for Default { 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; + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind(); then { - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind() { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } + // 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 + ); } } } @@ -202,14 +201,14 @@ impl LateLintPass<'_> for Default { let binding_type = if_chain! { if let ty::Adt(adt_def, substs) = binding_type.kind(); if !substs.is_empty(); - let adt_def_ty_name = cx.tcx.item_name(adt_def.did); - let generic_args = substs.iter().collect::>(); - let tys_str = generic_args - .iter() - .map(ToString::to_string) - .collect::>() - .join(", "); then { + let adt_def_ty_name = cx.tcx.item_name(adt_def.did); + let generic_args = substs.iter().collect::>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); format!("{}::<{}>", adt_def_ty_name, &tys_str) } else { binding_type.to_string() diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index d136db9373c..42bafc3442e 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -130,8 +130,8 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { }, ExprKind::Struct(_, fields, base) => { + let ty = self.cx.typeck_results().expr_ty(expr); if_chain! { - let ty = self.cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); if adt_def.is_struct(); if let Some(variant) = adt_def.variants.iter().next(); diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 635b6d83eab..84fac998662 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -32,16 +32,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit { if let ExprKind::Path(ref path) = path_expr.kind; if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::EXIT); + let parent = cx.tcx.hir().get_parent_item(e.hir_id); + if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent); + // If the next item up is a function we check if it is an entry point + // and only then emit a linter warning + let def_id = cx.tcx.hir().local_def_id(parent); + if !is_entrypoint_fn(cx, def_id.to_def_id()); then { - let parent = cx.tcx.hir().get_parent_item(e.hir_id); - if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) { - // If the next item up is a function we check if it is an entry point - // and only then emit a linter warning - let def_id = cx.tcx.hir().local_def_id(parent); - if !is_entrypoint_fn(cx, def_id.to_def_id()) { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } - } + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } } } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 1ca5c685a75..7968e7b764d 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -61,8 +61,8 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let ty = cx.typeck_results().expr_ty(expr); if_chain! { - let ty = cx.typeck_results().expr_ty(expr); if let ty::Float(fty) = *ty.kind(); if let hir::ExprKind::Lit(ref lit) = expr.kind; if let LitKind::Float(sym, lit_float_ty) = lit.node; diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index b10e83c0ea8..48612befc68 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -217,9 +217,8 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { if let Some(else_snippet) = snippet_opt(cx, else_span); if let Some(else_pos) = else_snippet.find("else"); if else_snippet[else_pos..].contains('\n'); - let else_desc = if is_if(else_) { "if" } else { "{..}" }; - then { + let else_desc = if is_if(else_) { "if" } else { "{..}" }; span_lint_and_note( cx, SUSPICIOUS_ELSE_FORMATTING, diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 4aab43256bf..56dfcc6a506 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -94,13 +94,10 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr); - then { - self.found_mutex = Some(mutex); - self.mutex_lock_called = true; - return; - } + if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { + self.found_mutex = Some(mutex); + self.mutex_lock_called = true; + return; } visit::walk_expr(self, expr); } @@ -121,13 +118,10 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr); - then { - self.found_mutex = Some(mutex); - self.mutex_lock_called = true; - return; - } + if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { + self.found_mutex = Some(mutex); + self.mutex_lock_called = true; + return; } visit::walk_expr(self, expr); } diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 8d2b9cccba4..574ad8c0f29 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -46,9 +46,8 @@ pub(super) fn check<'tcx>( let some_ctor = is_some_ctor(cx, path.res); let ok_ctor = is_ok_ctor(cx, path.res); if some_ctor || ok_ctor; - let if_let_type = if some_ctor { "Some" } else { "Ok" }; - then { + let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index af6d56a89af..3c5abb7a3c4 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -61,8 +61,8 @@ pub(super) fn check<'tcx>( if_chain! { 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 is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); + if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); 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); diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 5594fc7b046..d23d3c508fa 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -26,60 +26,59 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); if let Some(ref generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + 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::BTREEMAP) + || is_type_diagnostic_item(cx, ty, sym::hashmap_type); then { - 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::BTREEMAP) || - is_type_diagnostic_item(cx, ty, sym::hashmap_type) { - if method.ident.name == sym!(len) { - let span = shorten_needless_collect_span(expr); - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - "replace with", - "count()".to_string(), - Applicability::MachineApplicable, - ); - } - if method.ident.name == sym!(is_empty) { - let span = shorten_needless_collect_span(expr); - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - "replace with", - "next().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); - span_lint_and_then( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - |diag| { - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - diag.span_suggestion( - span, - "replace with", - format!( - "any(|{}| x == {})", - arg, pred - ), - Applicability::MachineApplicable, - ); - } - ); - } + if method.ident.name == sym!(len) { + let span = shorten_needless_collect_span(expr); + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + "replace with", + "count()".to_string(), + Applicability::MachineApplicable, + ); + } + if method.ident.name == sym!(is_empty) { + let span = shorten_needless_collect_span(expr); + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + "replace with", + "next().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); + span_lint_and_then( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + |diag| { + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); + diag.span_suggestion( + span, + "replace with", + format!( + "any(|{}| x == {})", + arg, pred + ), + Applicability::MachineApplicable, + ); + } + ); } } } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 3c40d54cb42..60afa449a45 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -256,49 +256,47 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { if let ExprKind::Path(ref seqpath) = seqexpr.kind; if let QPath::Resolved(None, ref seqvar) = *seqpath; if seqvar.segments.len() == 1; + let index_used_directly = path_to_local_id(idx, self.var); + let indexed_indirectly = { + let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var); + walk_expr(&mut used_visitor, idx); + used_visitor.used + }; + if indexed_indirectly || index_used_directly; then { - let index_used_directly = path_to_local_id(idx, self.var); - let indexed_indirectly = { - let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var); - walk_expr(&mut used_visitor, idx); - used_visitor.used - }; - - if indexed_indirectly || index_used_directly { - if self.prefer_mutable { - self.indexed_mut.insert(seqvar.segments[0].ident.name); + if self.prefer_mutable { + self.indexed_mut.insert(seqvar.segments[0].ident.name); + } + let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); + match res { + Res::Local(hir_id) => { + let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); + let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); + let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); + if indexed_indirectly { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + } + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } + return false; // no need to walk further *on the variable* } - let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); - match res { - Res::Local(hir_id) => { - let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); - let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); - } - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } - return false; // no need to walk further *on the variable* + Res::Def(DefKind::Static | DefKind::Const, ..) => { + if indexed_indirectly { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } - Res::Def(DefKind::Static | DefKind::Const, ..) => { - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); - } - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } - return false; // no need to walk further *on the variable* + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); } - _ => (), + return false; // no need to walk further *on the variable* } + _ => (), } } } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 849d7ec718c..62c131968e7 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -63,8 +63,8 @@ pub(super) fn check<'tcx>( match cx.qpath_res(qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant Res::Local(hir_id) => { + let node = cx.tcx.hir().get(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); diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index cad9ff8489a..7d697b2bf00 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -103,9 +103,8 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { if_chain! { if let ExprKind::Path(ref qpath) = ex.kind; if let QPath::Resolved(None, _) = *qpath; - let res = self.cx.qpath_res(qpath, ex.hir_id); then { - match res { + match self.cx.qpath_res(qpath, ex.hir_id) { Res::Local(hir_id) => { self.ids.insert(hir_id); }, diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 95261580b8e..5a3f0f27bc6 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -113,8 +113,8 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } } + let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); if_chain! { - let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); if let Some(marker) = markers.next(); if markers.count() == 0 && variants.len() > 1; then { diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index cfcc705eabc..9f1ab1f695d 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -58,9 +58,9 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym::option_type) || is_trait_method(cx, e, sym::Iterator); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); then { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); match closure_body.params[0].pat.kind { hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, .., name, None diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index d4764b5ccff..5cc16244a0d 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -168,17 +168,15 @@ fn unit_closure<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { - if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind { + if_chain! { + if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind; let body = cx.tcx.hir().body(inner_expr_id); let body_expr = &body.value; - - if_chain! { - if decl.inputs.len() == 1; - if is_unit_expression(cx, body_expr); - if let Some(binding) = iter_input_pats(&decl, body).next(); - then { - return Some((binding, body_expr)); - } + if decl.inputs.len() == 1; + if is_unit_expression(cx, body_expr); + if let Some(binding) = iter_input_pats(&decl, body).next(); + then { + return Some((binding, body_expr)); } } None diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8c6c30a1881..4a3be67dd75 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -732,8 +732,8 @@ fn report_single_match_single_pattern( format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) }); + let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); let (msg, sugg) = if_chain! { - let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); @@ -1480,8 +1480,8 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A /// Returns true if the `ex` match expression is in a local (`let`) statement fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> { + let map = &cx.tcx.hir(); if_chain! { - let map = &cx.tcx.hir(); if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)); if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id)); then { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 426c108d89f..0418c616efa 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -225,34 +225,33 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< } fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if let ExprKind::Call(ref repl_func, _) = src.kind { - if_chain! { - if !in_external_macro(cx.tcx.sess, expr_span); - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); + if_chain! { + if let ExprKind::Call(ref repl_func, _) = src.kind; + if !in_external_macro(cx.tcx.sess, expr_span); + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - then { - span_lint_and_then( - cx, - MEM_REPLACE_WITH_DEFAULT, - expr_span, - "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", - |diag| { - if !in_macro(expr_span) { - let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); + then { + span_lint_and_then( + cx, + MEM_REPLACE_WITH_DEFAULT, + expr_span, + "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + |diag| { + if !in_macro(expr_span) { + let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); - diag.span_suggestion( - expr_span, - "consider using", - suggestion, - Applicability::MachineApplicable - ); - } + diag.span_suggestion( + expr_span, + "consider using", + suggestion, + Applicability::MachineApplicable + ); } - ); - } + } + ); } } } diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 77f140510b6..2ad3e673c57 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -10,31 +9,26 @@ use rustc_span::sym; use super::BYTES_NTH; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) { - if_chain! { - let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let caller_type = if is_type_diagnostic_item(cx, ty, sym::string_type) { - Some("String") - } else if ty.is_str() { - Some("str") - } else { - None - }; - if let Some(caller_type) = caller_type; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_NTH, - expr.span, - &format!("called `.byte().nth()` on a `{}`", caller_type), - "try", - format!( - "{}.as_bytes().get({})", - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - snippet_with_applicability(cx, n_arg.span, "..", &mut applicability) - ), - applicability, - ); - } - } + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + let caller_type = if ty.is_str() { + "str" + } else if is_type_diagnostic_item(cx, ty, sym::string_type) { + "String" + } else { + return; + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_NTH, + expr.span, + &format!("called `.byte().nth()` on a `{}`", caller_type), + "try", + format!( + "{}.as_bytes().get({})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + snippet_with_applicability(cx, n_arg.span, "..", &mut applicability) + ), + applicability, + ); } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 15cf5674313..707c54f7a3c 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -40,8 +40,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { + let call_site = expr.span.source_callsite(); if_chain! { - let call_site = expr.span.source_callsite(); if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); let snippet_split = snippet.split("::").collect::>(); if let Some((_, elements)) = snippet_split.split_last(); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9a95354db7a..70acf9f6e73 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1882,11 +1882,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); - let first_arg_span = first_arg_ty.span; - let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty(); - then { + let first_arg_span = first_arg_ty.span; + let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, &item.ident.name.as_str(), diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index dac7e039e37..8e637e12393 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::usage::mutated_variables; use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_lint::LateContext; @@ -54,18 +53,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match &expr.kind { hir::ExprKind::Call(ref func, ref args) => { - if_chain! { - if let hir::ExprKind::Path(ref path) = func.kind; - then { - if match_qpath(path, &paths::OPTION_SOME) { - if path_to_local_id(&args[0], arg_id) { - return (false, false) - } - return (true, false); + if let hir::ExprKind::Path(ref path) = func.kind { + if match_qpath(path, &paths::OPTION_SOME) { + if path_to_local_id(&args[0], arg_id) { + return (false, false); } - // We don't know. It might do anything. - return (true, true); + return (true, false); } + // We don't know. It might do anything. + return (true, true); } (true, true) }, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 026ea50936a..c23643cb2f5 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -308,46 +308,45 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if let PatKind::Binding(an, .., name, None) = local.pat.kind; if let Some(ref init) = local.init; if !higher::is_from_for_desugar(local); + if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut; then { - if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { - // 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, "..") - }; - let (mutopt, initref) = if an == BindingAnnotation::RefMut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ref ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!( - "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, ".."), - tyopt=tyopt, - initref=initref, - ), - Applicability::MachineApplicable, - ); - } - ); - } + // 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, "..") + }; + let (mutopt, initref) = if an == BindingAnnotation::RefMut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ref ty) = local.ty { + format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!( + "let {name}{tyopt} = {initref};", + name=snippet(cx, name.span, ".."), + tyopt=tyopt, + initref=initref, + ), + Applicability::MachineApplicable, + ); + } + ); } }; if_chain! { @@ -462,21 +461,18 @@ fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) { if_chain! { if !in_constant(cx, cmp_expr.hir_id); if let Some((value, _)) = constant(cx, cx.typeck_results(), expr); + if match value { + Constant::F32(num) => num.is_nan(), + Constant::F64(num) => num.is_nan(), + _ => false, + }; then { - let needs_lint = match value { - Constant::F32(num) => num.is_nan(), - Constant::F64(num) => num.is_nan(), - _ => false, - }; - - if needs_lint { - span_lint( - cx, - CMP_NAN, - cmp_expr.span, - "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", - ); - } + span_lint( + cx, + CMP_NAN, + cmp_expr.span, + "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", + ); } } } diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 3789572ad43..a5f91eb035f 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -97,60 +97,60 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // impl of `Default` return; } - if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) { + if_chain! { + if sig.decl.inputs.is_empty(); + if name == sym::new; + if cx.access_levels.is_reachable(id); let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); let self_ty = cx.tcx.type_of(self_def_id); - if_chain! { - 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() { - let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { - if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { - if let Some(local_def_id) = ty_def.did.as_local() { - impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); - } - } - }); - self.impling_types = Some(impls); - } - - // Check if a Default implementation exists for the Self type, regardless of - // generics - if_chain! { - if let Some(ref impling_types) = self.impling_types; - 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_local_did); - if impling_types.contains(&self_id) { - return; + 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() { + let mut impls = HirIdSet::default(); + cx.tcx.for_each_impl(default_trait_id, |d| { + if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { + if let Some(local_def_id) = ty_def.did.as_local() { + impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); } } - } + }); + self.impling_types = Some(impls); + } - let generics_sugg = snippet(cx, generics.span, ""); - 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, &generics_sugg), - Applicability::MaybeIncorrect, - ); - }, - ); + // Check if a Default implementation exists for the Self type, regardless of + // generics + if_chain! { + if let Some(ref impling_types) = self.impling_types; + 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(); + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); + if impling_types.contains(&self_id); + then { + return; + } } + + let generics_sugg = snippet(cx, generics.span, ""); + 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, &generics_sugg), + Applicability::MaybeIncorrect, + ); + }, + ); } } } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index aa1d8fbe300..d775cd7c7f7 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -307,19 +307,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + if is_unfrozen(cx, normalized); + if is_value_unfrozen_poly(cx, *body_id, normalized); then { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if is_unfrozen(cx, normalized) - && is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint( - cx, - Source::Assoc { - item: impl_item.span, - }, - ); - } + lint( + cx, + Source::Assoc { + item: impl_item.span, + }, + ); } } }, diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index d06e7f8fe1e..1e946858947 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -97,11 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn get_outer_span(expr: &Expr<'_>) -> Span { if_chain! { if expr.span.from_expansion(); - let first = expr.span.ctxt().outer_expn_data(); - if first.call_site.from_expansion(); - let second = first.call_site.ctxt().outer_expn_data(); + let first = expr.span.ctxt().outer_expn_data().call_site; + if first.from_expansion(); then { - second.call_site + first.ctxt().outer_expn_data().call_site } else { expr.span } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index be686b1b0cd..6e530d0ffb0 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -271,19 +271,18 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: GenericArg::Type(ty) => Some(ty), _ => None, }); + let replacement = snippet_opt(cx, inner.span); + if let Some(r) = replacement; then { - let replacement = snippet_opt(cx, inner.span); - if let Some(r) = replacement { - span_lint_and_sugg( - cx, - PTR_ARG, - arg.span, - "using a reference to `Cow` is not recommended", - "change this to", - "&".to_owned() + &r, - Applicability::Unspecified, - ); - } + span_lint_and_sugg( + cx, + PTR_ARG, + arg.span, + "using a reference to `Cow` is not recommended", + "change this to", + "&".to_owned() + &r, + Applicability::Unspecified, + ); } } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 95b21489eb5..79692abb6ac 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -320,32 +320,29 @@ fn match_ident(e: &Expr<'_>) -> Option { } 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, "_")) - ); - } + if_chain! { + if path.ident.as_str() == "zip"; + if let [iter, zip_arg] = args; + // `.iter()` call + if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = iter.kind; + 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, "_")) + ); } } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 2977a108d14..5429d389610 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -113,8 +113,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.path.segments[0].ident == path.segments[0].ident - && self.path.res == path.res; + if self.path.segments[0].ident == path.segments[0].ident; + if self.path.res == path.res; then { self.count += 1; } diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 99e3d818b79..512abde11a6 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -68,14 +68,13 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { // Check for more than one binary operation in the implemented function // Linting when multiple operations are involved can result in false positives + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); 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 { + let body = cx.tcx.hir().body(body_id); + let mut visitor = BinaryExprVisitor { nb_binops: 0 }; walk_expr(&mut visitor, &body.value); if visitor.nb_binops > 1 { return; diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 72489f27cd3..1a6124e9ddb 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -36,10 +36,10 @@ pub(super) fn check<'tcx>( if_chain! { // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - let op = format!("{}{}", arg, float_ty.name_str()).into(); if let ExprKind::Lit(lit) = &expr.kind; if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; then { + let op = format!("{}{}", arg, float_ty.name_str()).into(); match arg { sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op), _ => arg = sugg::Sugg::NonParen(op) diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e61058c2749..e4799790d35 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -138,9 +138,8 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< 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) + Some(ready_subst.type_at(1)) } else { None } @@ -179,10 +176,8 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> 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) + Some(some_subst.type_at(1)) } else { None } diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index bad3e488bb6..cdc65abe47c 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -116,8 +116,8 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa let ty = cx.tcx.erase_late_bound_regions(ret_ty); if ty.is_unit(); then { + let body = cx.tcx.hir().body(body_id); 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(); diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 925ab577099..f77d811c283 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -19,9 +19,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if is_questionmark_desugar_marked_call(expr) { return; } + let map = &cx.tcx.hir(); + let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); if_chain! { - let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; if is_questionmark_desugar_marked_call(parent_expr); then { diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index d5bc3de6698..9ceb2c3ffe2 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -116,8 +116,8 @@ impl LateLintPass<'_> for UnnamedAddress { if_chain! { if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; if is_comparison(binop.node); - if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() && - cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); + if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr(); + if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); if is_fn_def(cx, left) || is_fn_def(cx, right); then { span_lint( diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index aef4ce75915..15343cf90f2 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -49,21 +49,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { if assoc_item.fn_has_self_parameter; if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; let body = cx.tcx.hir().body(*body_id); - if !body.params.is_empty(); + if let [self_param, ..] = body.params; + let self_hir_id = self_param.pat.hir_id; + if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body); then { - let self_param = &body.params[0]; - let self_hir_id = self_param.pat.hir_id; - if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body) { - span_lint_and_help( - cx, - UNUSED_SELF, - self_param.span, - "unused `self` argument", - None, - "consider refactoring to a associated function", - ); - return; - } + span_lint_and_help( + cx, + UNUSED_SELF, + self_param.span, + "unused `self` argument", + None, + "consider refactoring to a associated function", + ); } } } diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 0d745813beb..d17aa6d8424 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -110,31 +110,27 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { } 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 { + let body = cx.tcx.hir().body(body_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 let ImplItemKind::Fn(_, body_id) = impl_item.kind; - then { - let body = cx.tcx.hir().body(body_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)"); - }); - } + // 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/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 3e1b69e676b..d893b271a20 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -103,25 +103,23 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { - if_chain! { - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, a, sym::result_type); - if let ty::Adt(_, substs) = a.kind(); - if let Some(a_type) = substs.types().next(); - if TyS::same_type(a_type, b); - - then { - span_lint_and_help( - cx, - USELESS_CONVERSION, - e.span, - &format!("useless conversion to the same type: `{}`", b), - None, - "consider removing `.try_into()`", - ); - } + if_chain! { + if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into"; + let a = cx.typeck_results().expr_ty(e); + let b = cx.typeck_results().expr_ty(&args[0]); + if is_type_diagnostic_item(cx, a, sym::result_type); + if let ty::Adt(_, substs) = a.kind(); + if let Some(a_type) = substs.types().next(); + if TyS::same_type(a_type, b); + then { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + &format!("useless conversion to the same type: `{}`", b), + None, + "consider removing `.try_into()`", + ); } } }, @@ -131,10 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if args.len() == 1; if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(&args[0]); - then { + let a = cx.typeck_results().expr_ty(e); + let b = cx.typeck_results().expr_ty(&args[0]); if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym::result_type); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 266b88beeec..d5a13c135b1 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -588,9 +588,9 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if stmts.len() == 1 && block.expr.is_none(); 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).deny_side_effects(); then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); match &*ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 8b696ed1c84..c7190e2f979 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -108,22 +108,21 @@ impl LateLintPass<'_> for VecInitThenPush { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if self.searcher.is_none() { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(id) = path_to_local(left); - if let Some(init_kind) = get_vec_init_kind(cx, right); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: false, - lhs_span: left.span, - err_span: expr.span, - found: 0, - }); - } + if_chain! { + if self.searcher.is_none(); + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Assign(left, right, _) = expr.kind; + if let Some(id) = path_to_local(left); + if let Some(init_kind) = get_vec_init_kind(cx, right); + then { + self.searcher = Some(VecPushSearcher { + local_id: id, + init: init_kind, + lhs_is_local: false, + lhs_span: left.span, + err_span: expr.span, + found: 0, + }); } } } -- cgit 1.4.1-3-g733a5 From aaba9b78a210bc20dfdb1aff24d93acc22677a47 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 31 Mar 2021 14:58:17 -0400 Subject: Fix `redundant_clone` fp where the cloned value is modified while the clone is in use. --- clippy_lints/src/redundant_clone.rs | 252 +++++++++++++++++++++--------------- tests/ui/redundant_clone.fixed | 24 ++++ tests/ui/redundant_clone.rs | 24 ++++ tests/ui/redundant_clone.stderr | 20 +-- 4 files changed, 207 insertions(+), 113 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 37678fac1d2..9656ee64c81 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -199,79 +199,72 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { (local, deref_clone_ret) }; - let is_temp = mir.local_kind(ret_local) == mir::LocalKind::Temp; - - // 1. `local` can be moved out if it is not used later. - // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone` - // call anyway. - let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold( - (false, !is_temp), - |(used, consumed), (tbb, tdata)| { - // Short-circuit - if (used && consumed) || - // Give up on loops - tdata.terminator().successors().any(|s| *s == bb) - { - return (true, true); + let clone_usage = if local == ret_local { + CloneUsage { + cloned_used: false, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, + } + } else { + let clone_usage = visit_clone_usage(local, ret_local, &mir, bb); + if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { + // cloned value is used, and the clone is modified or moved + continue; + } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { + // cloned value is mutated, and the clone is alive. + if possible_borrower.is_alive_at(ret_local, loc) { + continue; } + } + clone_usage + }; - let mut vis = LocalUseVisitor { - used: (local, false), - consumed_or_mutated: (ret_local, false), - }; - vis.visit_basic_block_data(tbb, tdata); - (used || vis.used.1, consumed || vis.consumed_or_mutated.1) - }, - ); - - if !used || !consumed_or_mutated { - let span = terminator.source_info.span; - let scope = terminator.source_info.scope; - let node = mir.source_scopes[scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - - if_chain! { - if let Some(snip) = snippet_opt(cx, span); - if let Some(dot) = snip.rfind('.'); - then { - let sugg_span = span.with_lo( - span.lo() + BytePos(u32::try_from(dot).unwrap()) - ); - let mut app = Applicability::MaybeIncorrect; - - let call_snip = &snip[dot + 1..]; - // Machine applicable when `call_snip` looks like `foobar()` - 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; - } + let span = terminator.source_info.span; + let scope = terminator.source_info.scope; + let node = mir.source_scopes[scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + if_chain! { + if let Some(snip) = snippet_opt(cx, span); + if let Some(dot) = snip.rfind('.'); + then { + let sugg_span = span.with_lo( + span.lo() + BytePos(u32::try_from(dot).unwrap()) + ); + let mut app = Applicability::MaybeIncorrect; + + let call_snip = &snip[dot + 1..]; + // Machine applicable when `call_snip` looks like `foobar()` + 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; } + } - span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { - diag.span_suggestion( - sugg_span, - "remove this", - String::new(), - app, + span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { + diag.span_suggestion( + sugg_span, + "remove this", + String::new(), + app, + ); + if clone_usage.cloned_used { + diag.span_note( + span, + "cloned value is neither consumed nor mutated", ); - if used { - diag.span_note( - span, - "cloned value is neither consumed nor mutated", - ); - } else { - diag.span_note( - span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), - "this value is dropped without further use", - ); - } - }); - } else { - span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); - } + } else { + diag.span_note( + span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), + "this value is dropped without further use", + ); + } + }); + } else { + span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); } } } @@ -365,49 +358,97 @@ fn base_local_and_movability<'tcx>( (local, deref || field || slice) } -struct LocalUseVisitor { - used: (mir::Local, bool), - consumed_or_mutated: (mir::Local, bool), +#[derive(Default)] +struct CloneUsage { + /// Whether the cloned value is used after the clone. + cloned_used: bool, + /// The first location where the cloned value is consumed or mutated, if any. + cloned_consume_or_mutate_loc: Option, + /// Whether the clone value is mutated. + clone_consumed_or_mutated: bool, } - -impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { + struct V { + cloned: mir::Local, + clone: mir::Local, + result: CloneUsage, } + impl<'tcx> mir::visit::Visitor<'tcx> for V { + fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { + let statements = &data.statements; + for (statement_index, statement) in statements.iter().enumerate() { + self.visit_statement(statement, mir::Location { block, statement_index }); + } - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, _: mir::Location) { - let local = place.local; - - if local == self.used.0 - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.used.1 = true; + self.visit_terminator( + data.terminator(), + mir::Location { + block, + statement_index: statements.len(), + }, + ); } - if local == self.consumed_or_mutated.0 { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.consumed_or_mutated.1 = true; - }, - _ => {}, + fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { + let local = place.local; + + if local == self.cloned + && !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) + { + self.result.cloned_used = true; + self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { + matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) + .then(|| loc) + }); + } else if local == self.clone { + match ctx { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + self.result.clone_consumed_or_mutated = true; + }, + _ => {}, + } } } } + + let init = CloneUsage { + cloned_used: false, + cloned_consume_or_mutate_loc: None, + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, + }; + traversal::ReversePostorder::new(&mir, bb) + .skip(1) + .fold(init, |usage, (tbb, tdata)| { + // Short-circuit + if (usage.cloned_used && usage.clone_consumed_or_mutated) || + // Give up on loops + tdata.terminator().successors().any(|s| *s == bb) + { + return CloneUsage { + cloned_used: true, + clone_consumed_or_mutated: true, + ..usage + }; + } + + let mut v = V { + cloned, + clone, + result: usage, + }; + v.visit_basic_block_data(tbb, tdata); + v.result + }) } /// Determines liveness of each local purely based on `StorageLive`/`Dead`. @@ -623,4 +664,9 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } + + fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } } diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index ec309109ed5..f5da703cd1d 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -54,6 +54,7 @@ fn main() { not_consumed(); issue_5405(); manually_drop(); + clone_then_move_cloned(); } #[derive(Clone)] @@ -182,3 +183,26 @@ fn manually_drop() { Arc::from_raw(p); } } + +fn clone_then_move_cloned() { + // issue #5973 + let x = Some(String::new()); + // ok, x is moved while the clone is in use. + assert_eq!(x.clone(), None, "not equal {}", x.unwrap()); + + // issue #5595 + fn foo(_: &Alpha, _: F) {} + let x = Alpha; + // ok, data is moved while the clone is in use. + foo(&x.clone(), move || { + let _ = x; + }); + + // issue #6998 + struct S(String); + impl S { + fn m(&mut self) {} + } + let mut x = S(String::new()); + x.0.clone().chars().for_each(|_| x.m()); +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index b57027456e0..fd7f31a1cc5 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -54,6 +54,7 @@ fn main() { not_consumed(); issue_5405(); manually_drop(); + clone_then_move_cloned(); } #[derive(Clone)] @@ -182,3 +183,26 @@ fn manually_drop() { Arc::from_raw(p); } } + +fn clone_then_move_cloned() { + // issue #5973 + let x = Some(String::new()); + // ok, x is moved while the clone is in use. + assert_eq!(x.clone(), None, "not equal {}", x.unwrap()); + + // issue #5595 + fn foo(_: &Alpha, _: F) {} + let x = Alpha; + // ok, data is moved while the clone is in use. + foo(&x.clone(), move || { + let _ = x; + }); + + // issue #6998 + struct S(String); + impl S { + fn m(&mut self) {} + } + let mut x = S(String::new()); + x.0.clone().chars().for_each(|_| x.m()); +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 821e7934be8..529a6de91e2 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:62:25 + --> $DIR/redundant_clone.rs:63:25 | LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:62:24 + --> $DIR/redundant_clone.rs:63:24 | LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } | ^ error: redundant clone - --> $DIR/redundant_clone.rs:119:15 + --> $DIR/redundant_clone.rs:120:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:119:14 + --> $DIR/redundant_clone.rs:120:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:120:15 + --> $DIR/redundant_clone.rs:121:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:120:14 + --> $DIR/redundant_clone.rs:121:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:130:19 + --> $DIR/redundant_clone.rs:131:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:130:18 + --> $DIR/redundant_clone.rs:131:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:142:14 + --> $DIR/redundant_clone.rs:143:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:142:13 + --> $DIR/redundant_clone.rs:143:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 08a8ad3a366ff28e4d89997de69ddb3a6ceb1a18 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 1 Apr 2021 12:15:47 -0500 Subject: Remove paths from bind_instead_of_map --- clippy_lints/src/methods/bind_instead_of_map.rs | 159 ++++++++++++------------ 1 file changed, 78 insertions(+), 81 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 8ccb8f4268c..46d4c674648 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,80 +1,66 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::ty::match_type; -use clippy_utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions}; +use clippy_utils::{in_macro, remove_blocks, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{LangItem, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty::DefIdTree; 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 VARIANT_LANG_ITEM: LangItem = LangItem::OptionSome; 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 VARIANT_LANG_ITEM: LangItem = LangItem::ResultOk; 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 VARIANT_LANG_ITEM: LangItem = LangItem::ResultErr; 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 VARIANT_LANG_ITEM: LangItem; 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!( + fn no_op_msg(cx: &LateContext<'_>) -> Option { + let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; + let item_id = cx.tcx.parent(variant_id)?; + Some(format!( "using `{}.{}({})`, which is a no-op", - Self::TYPE_NAME, + cx.tcx.item_name(item_id), Self::BAD_METHOD_NAME, - Self::BAD_VARIANT_NAME - ) + cx.tcx.item_name(variant_id), + )) } - fn lint_msg() -> String { - format!( + fn lint_msg(cx: &LateContext<'_>) -> Option { + let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; + let item_id = cx.tcx.parent(variant_id)?; + Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", - Self::TYPE_NAME, + cx.tcx.item_name(item_id), Self::BAD_METHOD_NAME, - Self::BAD_VARIANT_NAME, + cx.tcx.item_name(variant_id), Self::GOOD_METHOD_NAME - ) + )) } fn lint_closure_autofixable( @@ -85,17 +71,12 @@ pub(crate) trait BindInsteadOfMap { 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; + if let hir::ExprKind::Call(ref some_expr, [inner_expr]) = closure_expr.kind; + if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind; + if Self::is_variant(cx, path.res); + if !contains_return(inner_expr); + if let Some(msg) = Self::lint_msg(cx); 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 { @@ -109,7 +90,7 @@ pub(crate) trait BindInsteadOfMap { cx, BIND_INSTEAD_OF_MAP, expr.span, - Self::lint_msg().as_ref(), + &msg, "try this", note, Applicability::MachineApplicable, @@ -126,41 +107,46 @@ pub(crate) trait BindInsteadOfMap { 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; - 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]); + if let hir::ExprKind::Call(ref func_path, [arg]) = ret_expr.kind; + if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind; + if Self::is_variant(cx, path.res); + if !contains_return(arg); then { - suggs.push((ret_expr.span, args[0].span.source_callsite())); + suggs.push((ret_expr.span, arg.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())), - ), - ) - }); - } - can_sugg + let (span, msg) = if_chain! { + if can_sugg; + if let hir::ExprKind::MethodCall(_, span, ..) = expr.kind; + if let Some(msg) = Self::lint_msg(cx); + then { (span, msg) } else { return false; } + }; + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| { + multispan_sugg_with_applicability( + diag, + "try this", + Applicability::MachineApplicable, + std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ), + ) + }); + true } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { - if !match_type(cx, cx.typeck_results().expr_ty(recv), Self::TYPE_QPATH) { - return false; + if_chain! { + if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); + if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM); + if Some(adt.did) == cx.tcx.parent(vid); + then {} else { return false; } } match arg.kind { @@ -175,19 +161,30 @@ pub(crate) trait BindInsteadOfMap { } }, // `_.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, recv.span, "..").into(), - Applicability::MachineApplicable, - ); + hir::ExprKind::Path(QPath::Resolved(_, path)) if Self::is_variant(cx, path.res) => { + if let Some(msg) = Self::no_op_msg(cx) { + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + &msg, + "use the expression directly", + snippet(cx, recv.span, "..").into(), + Applicability::MachineApplicable, + ); + } true }, _ => false, } } + + fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) { + return cx.tcx.parent(id) == Some(variant_id); + } + } + false + } } -- cgit 1.4.1-3-g733a5 From 9f6f001988ae32e06b3abee5d869ad921fd614ae Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 2 Apr 2021 13:45:38 +0900 Subject: same_item_push: Don't trigger same_item_push if the vec is used in the loop body --- clippy_lints/src/loops/same_item_push.rs | 137 +++++++++++++++++++------------ tests/ui/same_item_push.rs | 7 ++ 2 files changed, 91 insertions(+), 53 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 62c131968e7..b3d784474c8 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,11 +1,13 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::symbol::sym; @@ -41,59 +43,55 @@ pub(super) fn check<'tcx>( } // Determine whether it is safe to lint the body - let mut same_item_push_visitor = SameItemPushVisitor { - should_lint: true, - vec_push: None, - cx, - }; + let mut same_item_push_visitor = SameItemPushVisitor::new(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 { - 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 - match pushed_item.kind { - ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, pushed_item.hir_id) { - // immutable bindings that are initialized with literal or constant - Res::Local(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; - 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 Some(init) = parent_let_expr.init; - then { - match init.kind { - // immutable bindings that are initialized with literal - 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, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item); - } + if_chain! { + if same_item_push_visitor.should_lint(); + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push; + 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, &[])); + then { + // Make sure that the push does not involve possibly mutating values + match pushed_item.kind { + ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(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; + 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 Some(init) = parent_let_expr.init; + then { + match init.kind { + // immutable bindings that are initialized with literal + 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, ..) = cx.qpath_res(path, init.hir_id) { + emit_lint(cx, vec, pushed_item); } - _ => {}, } + _ => {}, } } - }, - // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), - _ => {}, - } - }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), - _ => {}, - } + } + }, + // constant + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), + _ => {}, + } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, } } } @@ -101,10 +99,38 @@ pub(super) fn check<'tcx>( // Scans the body of the for loop and determines whether lint should be given struct SameItemPushVisitor<'a, 'tcx> { - should_lint: bool, + non_deterministic_expr: bool, + multiple_pushes: 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>, + used_locals: FxHashSet, +} + +impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + non_deterministic_expr: false, + multiple_pushes: false, + vec_push: None, + cx, + used_locals: FxHashSet::default(), + } + } + + fn should_lint(&self) -> bool { + if_chain! { + if !self.non_deterministic_expr; + if !self.multiple_pushes; + if let Some((vec, _)) = self.vec_push; + if let Some(hir_id) = path_to_local(vec); + then { + !self.used_locals.contains(&hir_id) + } else { + false + } + } + } } impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { @@ -113,9 +139,14 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, '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::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, ExprKind::Block(block, _) => self.visit_block(block), - _ => {}, + _ => { + if let Some(hir_id) = path_to_local(expr) { + self.used_locals.insert(hir_id); + } + walk_expr(self, expr); + }, } } @@ -140,7 +171,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { self.vec_push = vec_push_option; } else { // There are multiple pushes ... don't lint - self.should_lint = false; + self.multiple_pushes = true; } } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index a37c8782ec3..9d420ec672a 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -148,4 +148,11 @@ fn main() { }; vec.push(item); } + + // Fix #6987 + let mut vec = Vec::new(); + for _ in 0..10 { + vec.push(1); + vec.extend(&[2]); + } } -- cgit 1.4.1-3-g733a5 From a064534b9e9754e18429a05db79fcba0d70292b9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 08:37:03 -0500 Subject: Refactor needless_collect --- clippy_lints/src/loops/needless_collect.rs | 80 ++++++++---------------------- 1 file changed, 21 insertions(+), 59 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index d23d3c508fa..5378b018970 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { 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; + if let ExprKind::MethodCall(ref chain_method, method0_span, _, _) = args[0].kind; if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); if let Some(ref generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); @@ -31,55 +31,28 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym::hashmap_type); - then { - if method.ident.name == sym!(len) { - let span = shorten_needless_collect_span(expr); - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - "replace with", - "count()".to_string(), - Applicability::MachineApplicable, - ); - } - if method.ident.name == sym!(is_empty) { - let span = shorten_needless_collect_span(expr); - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - "replace with", - "next().is_none()".to_string(), - Applicability::MachineApplicable, - ); - } - if method.ident.name == sym!(contains) { + if let Some(sugg) = match &*method.ident.name.as_str() { + "len" => Some("count()".to_string()), + "is_empty" => Some("next().is_none()".to_string()), + "contains" => { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_needless_collect_span(expr); - span_lint_and_then( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - |diag| { - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - diag.span_suggestion( - span, - "replace with", - format!( - "any(|{}| x == {})", - arg, pred - ), - Applicability::MachineApplicable, - ); - } - ); + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); + Some(format!("any(|{}| x == {})", arg, pred)) } + _ => None, + }; + then { + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + method0_span.with_hi(expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + Applicability::MachineApplicable, + ); } } } @@ -269,14 +242,3 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) visitor.visit_block(block); if visitor.seen_other { None } else { Some(visitor.uses) } } - -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()); - } - } - unreachable!(); -} -- cgit 1.4.1-3-g733a5 From 4356a8f8f7882e2ebef3362c5175c69242f27844 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 09:38:13 -0500 Subject: Remove redundant emit() --- clippy_lints/src/loops/needless_collect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 5378b018970..96b0f2e6cd7 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -102,7 +102,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo (iter_call.span, iter_replacement) ], Applicability::MachineApplicable,// MaybeIncorrect, - ).emit(); + ); }, ); } -- cgit 1.4.1-3-g733a5 From 33798bb0646b32680a204ef39aa1aae07c9ac0f5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 10:03:35 -0500 Subject: Improve needless_collect output --- clippy_lints/src/loops/needless_collect.rs | 11 ++++---- clippy_utils/src/diagnostics.rs | 6 ++-- tests/ui/needless_collect_indirect.stderr | 45 +++++++++++++++++------------- 3 files changed, 35 insertions(+), 27 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 96b0f2e6cd7..e0c5caf5136 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -10,8 +10,8 @@ use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Ident}; +use rustc_span::{MultiSpan, Span}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; @@ -65,7 +65,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } ) = stmt.kind; - if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; + if let ExprKind::MethodCall(ref method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator); if let Some(ref generic_args) = method_name.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); @@ -74,7 +74,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo 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; + if let [iter_call] = &*iter_calls; then { let mut used_count_visitor = UsedCountVisitor { cx, @@ -87,11 +87,12 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo } // Suggest replacing iter_call with iter_replacement, and removing stmt - let iter_call = &iter_calls[0]; + let mut span = MultiSpan::from_span(collect_span); + span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); span_lint_and_then( cx, super::NEEDLESS_COLLECT, - stmt.span.until(iter_call.span), + span, NEEDLESS_COLLECT_MSG, |diag| { let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index e9c9cb12b9d..73a2fa992b3 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -133,9 +133,11 @@ pub fn span_lint_and_note<'a, T: LintContext>( /// /// 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) +pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F) where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), + C: LintContext, + S: Into, + F: FnOnce(&mut DiagnosticBuilder<'_>), { cx.struct_span_lint(lint, sp, |diag| { let mut diag = diag.build(msg); diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 76e789d9052..c773b841f3b 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,9 +1,10 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:5:5 + --> $DIR/needless_collect_indirect.rs:5:39 | -LL | / let indirect_iter = sample.iter().collect::>(); -LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); - | |____^ +LL | let indirect_iter = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); + | ------------------------- the iterator could be used here instead | = note: `-D clippy::needless-collect` implied by `-D warnings` help: use the original Iterator instead of collecting it and then producing a new one @@ -13,11 +14,12 @@ LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:5 + --> $DIR/needless_collect_indirect.rs:7:38 | -LL | / let indirect_len = sample.iter().collect::>(); -LL | | indirect_len.len(); - | |____^ +LL | let indirect_len = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_len.len(); + | ------------------ the iterator could be used here instead | help: take the original Iterator's count instead of collecting it and finding the length | @@ -26,11 +28,12 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:5 + --> $DIR/needless_collect_indirect.rs:9:40 | -LL | / let indirect_empty = sample.iter().collect::>(); -LL | | indirect_empty.is_empty(); - | |____^ +LL | let indirect_empty = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_empty.is_empty(); + | ------------------------- the iterator could be used here instead | help: check if the original Iterator has anything instead of collecting it and seeing if it's empty | @@ -39,11 +42,12 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:5 + --> $DIR/needless_collect_indirect.rs:11:43 | -LL | / let indirect_contains = sample.iter().collect::>(); -LL | | indirect_contains.contains(&&5); - | |____^ +LL | let indirect_contains = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_contains.contains(&&5); + | ------------------------------- the iterator could be used here instead | help: check if the original Iterator contains an element instead of collecting then checking | @@ -52,11 +56,12 @@ LL | sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:23:5 + --> $DIR/needless_collect_indirect.rs:23:48 | -LL | / let non_copy_contains = sample.into_iter().collect::>(); -LL | | non_copy_contains.contains(&a); - | |____^ +LL | let non_copy_contains = sample.into_iter().collect::>(); + | ^^^^^^^ +LL | non_copy_contains.contains(&a); + | ------------------------------ the iterator could be used here instead | help: check if the original Iterator contains an element instead of collecting then checking | -- cgit 1.4.1-3-g733a5 From c05760ff90fbf4ac677db592b2efe25fab5fac6c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 22:24:52 -0400 Subject: Fix `macro_use_import` ICE --- clippy_lints/src/macro_use.rs | 1 + tests/ui/macro_use_imports.fixed | 6 +++++- tests/ui/macro_use_imports.rs | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573c297838..68cba192c4e 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -114,6 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { .iter() .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); if let Res::Def(DefKind::Mod, id) = path.res; + if !id.is_local(); then { for kid in cx.tcx.item_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 91e34c62160..51c66a46368 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -4,7 +4,7 @@ // run-rustfix // ignore-32bit -#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] @@ -40,4 +40,8 @@ mod a { } } +// issue #7015, ICE due to calling `item_children` with local `DefId` +#[macro_use] +use a as b; + fn main() {} diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 9c3c50c5d49..2011129bc94 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -4,7 +4,7 @@ // run-rustfix // ignore-32bit -#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] @@ -40,4 +40,8 @@ mod a { } } +// issue #7015, ICE due to calling `item_children` with local `DefId` +#[macro_use] +use a as b; + fn main() {} -- cgit 1.4.1-3-g733a5 From 7014340d5713b9401d95a5ca3287163fd407da46 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 11:56:32 -0500 Subject: Fix ICE --- clippy_lints/src/matches.rs | 14 ++++++++------ tests/ui/crashes/ice-7012.rs | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 tests/ui/crashes/ice-7012.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b13db4cd45c..ba49097fd3b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1046,16 +1046,18 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) path }, PatKind::TupleStruct(path, patterns, ..) => { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - let id = cx.qpath_res(path, pat.hir_id).def_id(); - missing_variants.retain(|e| e.ctor_def_id != Some(id)); + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } } path }, PatKind::Struct(path, patterns, ..) => { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { - let id = cx.qpath_res(path, pat.hir_id).def_id(); - missing_variants.retain(|e| e.def_id != id); + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { + missing_variants.retain(|e| e.def_id != id); + } } path }, diff --git a/tests/ui/crashes/ice-7012.rs b/tests/ui/crashes/ice-7012.rs new file mode 100644 index 00000000000..60bdbc4f124 --- /dev/null +++ b/tests/ui/crashes/ice-7012.rs @@ -0,0 +1,17 @@ +#![allow(clippy::all)] + +enum _MyOption { + None, + Some(()), +} + +impl _MyOption { + fn _foo(&self) { + match self { + &Self::Some(_) => {}, + _ => {}, + } + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 5102c9cc694fd6374370c00f7c9518da14c0156e Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 3 Apr 2021 22:52:48 +0200 Subject: Remove author requirement for `cargo_common_metadata` --- clippy_lints/src/cargo_common_metadata.rs | 10 ++-------- tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr | 6 ++---- .../cargo_common_metadata/fail_publish/src/main.stderr | 6 ++---- .../cargo_common_metadata/fail_publish_true/src/main.stderr | 6 ++---- tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 1 - 5 files changed, 8 insertions(+), 21 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index fce5c559672..8097a1c8326 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -20,11 +20,10 @@ declare_clippy_lint! { /// /// **Example:** /// ```toml - /// # This `Cargo.toml` is missing an authors field: + /// # This `Cargo.toml` is missing a description field: /// [package] /// name = "clippy" /// version = "0.0.212" - /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" /// license = "MIT OR Apache-2.0" @@ -32,14 +31,13 @@ declare_clippy_lint! { /// categories = ["development-tools", "development-tools::cargo-plugins"] /// ``` /// - /// Should include an authors field like: + /// Should include a description field like: /// /// ```toml /// # This `Cargo.toml` includes all common metadata /// [package] /// name = "clippy" /// version = "0.0.212" - /// authors = ["Someone "] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" @@ -97,10 +95,6 @@ impl LateLintPass<'_> for CargoCommonMetadata { // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || self.ignore_publish { - if is_empty_vec(&package.authors) { - missing_warning(cx, &package, "package.authors"); - } - if is_empty_str(&package.description) { missing_warning(cx, &package, "package.description"); } diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr index c8ae6c820df..5e9aa8dc36a 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` 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 @@ -14,5 +12,5 @@ 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 +error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr index c8ae6c820df..5e9aa8dc36a 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` 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 @@ -14,5 +12,5 @@ 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 +error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr index c8ae6c820df..5e9aa8dc36a 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` 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 @@ -14,5 +12,5 @@ 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 +error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index 737e84e963c..cb4774d43a2 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -2,7 +2,6 @@ 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" readme = "README.md" -- cgit 1.4.1-3-g733a5 From 6f31ed6c8d97cdd1d50dbbf4c9b2d7e50f5be0b1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 31 Mar 2021 16:39:09 -0500 Subject: Use DefIdMap and similar aliases --- clippy_lints/src/functions/must_use.rs | 12 ++++++------ clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs | 7 +++---- clippy_lints/src/inherent_impl.rs | 4 ++-- clippy_lints/src/len_zero.rs | 6 +++--- clippy_lints/src/loops/utils.rs | 10 +++++----- clippy_lints/src/loops/while_immutable_condition.rs | 14 +++++++------- clippy_lints/src/matches.rs | 6 +++--- clippy_lints/src/needless_pass_by_value.rs | 7 ++++--- clippy_utils/src/hir_utils.rs | 6 +++--- clippy_utils/src/usage.rs | 8 ++++---- 10 files changed, 40 insertions(+), 40 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 3825699936f..9e943c647fe 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def::Res, def_id::DefId, intravisit, QPath}; +use rustc_hir::def_id::DefIdSet; +use rustc_hir::{self as hir, def::Res, intravisit, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::{ hir::map::Map, @@ -169,11 +169,11 @@ fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { } fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { - let mut tys = FxHashSet::default(); + let mut tys = DefIdSet::default(); body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) } -fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool { if let hir::PatKind::Wild = pat.kind { return false; // ignore `_` patterns } @@ -186,7 +186,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet< static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, @@ -222,7 +222,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { } match expr.kind { Call(_, args) | MethodCall(_, _, args, _) => { - let mut tys = FxHashSet::default(); + let mut tys = DefIdSet::default(); for arg in args { if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) && is_mutable_ty( diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index ac02b60a356..cc69f4aed0c 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,5 +1,4 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{self as hir, intravisit}; +use rustc_hir::{self as hir, intravisit, HirIdSet}; use rustc_lint::LateContext; use rustc_middle::{hir::map::Map, ty}; @@ -44,7 +43,7 @@ fn check_raw_ptr( let raw_ptrs = iter_input_pats(decl, body) .zip(decl.inputs.iter()) .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) - .collect::>(); + .collect::(); if !raw_ptrs.is_empty() { let typeck_results = cx.tcx.typeck_body(body.id()); @@ -69,7 +68,7 @@ fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { struct DerefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - ptrs: FxHashSet, + ptrs: HirIdSet, typeck_results: &'a ty::TypeckResults<'tcx>, } diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 5b2e70e3ce9..c31013e49be 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_macro; -use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -43,7 +43,7 @@ declare_clippy_lint! { #[allow(clippy::module_name_repetitions)] #[derive(Default)] pub struct MultipleInherentImpl { - impls: FxHashMap, + impls: DefIdMap, } impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index fb522be2f1a..78152ad9019 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -3,8 +3,8 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{ def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, TraitItemRef, TyKind, @@ -199,7 +199,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } // fill the set with current and super traits - fn fill_trait_set(traitt: DefId, set: &mut FxHashSet, cx: &LateContext<'_>) { + fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) { if set.insert(traitt) { for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) { fill_trait_set(supertrait, set, cx); @@ -208,7 +208,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } if cx.access_levels.is_exported(visited_trait.hir_id()) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) { - let mut current_and_super_traits = FxHashSet::default(); + let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); let is_empty_method_found = current_and_super_traits diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index bb409c48532..fc287d51249 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,9 +1,9 @@ use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; +use rustc_hir::HirIdMap; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -20,9 +20,9 @@ enum IncrementVisitorVarState { /// Scan a for loop for variables that are incremented exactly once and not used after that. pub(super) 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: HirIdMap, // incremented variables + depth: u32, // depth of conditional expressions done: bool, } @@ -30,7 +30,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { pub(super) fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - states: FxHashMap::default(), + states: HirIdMap::default(), depth: 0, done: false, } diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 7d697b2bf00..de267cc77d2 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -3,13 +3,13 @@ use crate::consts::constant; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use if_chain::if_chain; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefIdMap; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{def_id, Expr, ExprKind, HirId, QPath}; +use rustc_hir::HirIdSet; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use std::iter::Iterator; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { if constant(cx, cx.typeck_results(), cond).is_some() { @@ -19,8 +19,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' let mut var_visitor = VarCollectorVisitor { cx, - ids: FxHashSet::default(), - def_ids: FxHashMap::default(), + ids: HirIdSet::default(), + def_ids: DefIdMap::default(), skip: false, }; var_visitor.visit_expr(cond); @@ -93,8 +93,8 @@ impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { /// All variables definition IDs are collected struct VarCollectorVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - ids: FxHashSet, - def_ids: FxHashMap, + ids: HirIdSet, + def_ids: DefIdMap, skip: bool, } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b13db4cd45c..75999468e28 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -13,13 +13,13 @@ use clippy_utils::{ use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::{ self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, }; +use rustc_hir::{HirIdMap, HirIdSet}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty, TyS, VariantDef}; @@ -1978,7 +1978,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { let min_index = usize::min(lindex, rindex); let max_index = usize::max(lindex, rindex); - let mut local_map: FxHashMap = FxHashMap::default(); + let mut local_map: HirIdMap = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { if_chain! { if let Some(a_id) = path_to_local(a); @@ -2062,7 +2062,7 @@ fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool { } /// Returns true if all the bindings in the `Pat` are in `ids` and vice versa -fn bindings_eq(pat: &Pat<'_>, mut ids: FxHashSet) -> bool { +fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { let mut result = true; pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id)); result && ids.is_empty() diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index e70c248e87b..7370ba39922 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -5,10 +5,11 @@ use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::FnKind; use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind}; +use rustc_hir::{HirIdMap, HirIdSet}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -310,10 +311,10 @@ fn requires_exact_signature(attrs: &[Attribute]) -> bool { #[derive(Default)] struct MovedVariablesCtxt { - moved_vars: FxHashSet, + moved_vars: HirIdSet, /// Spans which need to be prefixed with `*` for dereferencing the /// suggested additional reference. - spans_need_deref: FxHashMap>, + spans_need_deref: HirIdMap>, } impl MovedVariablesCtxt { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 618d33545a4..0c3b8b89171 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -2,9 +2,9 @@ use crate::consts::{constant_context, constant_simple}; use crate::differing_macro_contexts; use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::Res; +use rustc_hir::HirIdMap; use rustc_hir::{ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, @@ -61,7 +61,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, - locals: FxHashMap::default(), + locals: HirIdMap::default(), } } @@ -88,7 +88,7 @@ struct HirEqInterExpr<'a, 'b, 'tcx> { // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, // these blocks are considered equal since `x` is mapped to `y`. - locals: FxHashMap, + locals: HirIdMap, } impl HirEqInterExpr<'_, '_, '_> { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 54f110988d7..650b70c63af 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,9 +1,9 @@ use crate as utils; -use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -13,9 +13,9 @@ use rustc_middle::ty; 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<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option> { +pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { let mut delegate = MutVarsDelegate { - used_mutably: FxHashSet::default(), + used_mutably: HirIdSet::default(), skip: false, }; cx.tcx.infer_ctxt().enter(|infcx| { @@ -44,7 +44,7 @@ pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<' } struct MutVarsDelegate { - used_mutably: FxHashSet, + used_mutably: HirIdSet, skip: bool, } -- cgit 1.4.1-3-g733a5 From 2f81e4e34f96ee674072cbd1ffe55b68da5cc425 Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Sun, 4 Apr 2021 13:03:05 +0200 Subject: modification not working: fixing --- clippy_lints/src/doc.rs | 5 +++++ clippy_utils/src/lib.rs | 2 -- clippy_utils/src/paths.rs | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 69800f9d331..ea4f4fda0dd 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -721,6 +721,11 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { } } + // check for `assert_eq` or `assert_ne` + if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() { + self.panic_span = Some(expr.span); + } + // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index aade3409ebe..d6364625e70 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1172,7 +1172,6 @@ pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> O .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC)) .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT)) .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_ASSERT_FAILED_INNER)) } pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { @@ -1182,7 +1181,6 @@ pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { || match_def_path(cx, did, &paths::PANICKING_PANIC) || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT) || match_def_path(cx, did, &paths::PANICKING_PANIC_STR) - || match_def_path(cx, did, &paths::PANICKING_ASSERT_FAILED_INNER) } /// Returns the list of condition expressions and the list of blocks in a diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 287da83babd..7c83a9fe4e2 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -86,7 +86,6 @@ pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; -pub(super) const PANICKING_ASSERT_FAILED_INNER: [&str; 3] = ["core", "panicking", "assert_failed_inner"]; pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"]; pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"]; pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"]; -- cgit 1.4.1-3-g733a5 From 81dfb9ecfb13f67a56176bd82c846530858b8ef5 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 4 Apr 2021 14:21:02 +0200 Subject: Check path imports per module --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 150 +++++++++++----------- tests/ui/single_component_path_imports.fixed | 7 + tests/ui/single_component_path_imports.rs | 7 + tests/ui/single_component_path_imports.stderr | 12 +- 5 files changed, 96 insertions(+), 82 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97557fefe3e..f013613119c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1232,7 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box as_conversions::AsConversions); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); - store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports::default()); + store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); let max_fn_params_bools = conf.max_fn_params_bools; 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)); diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 901f7642a12..adf0d7998f8 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; -use rustc_ast::{Crate, Item, ItemKind, ModKind, UseTreeKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::edition::Edition; -use rustc_span::symbol::kw; -use rustc_span::{Span, Symbol}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -36,94 +34,96 @@ declare_clippy_lint! { "imports with single component path are redundant" } -#[derive(Default)] -pub struct SingleComponentPathImports { - /// keep track of imports reused with `self` keyword, - /// such as `self::crypto_hash` in the example below - /// - /// ```rust,ignore - /// use self::crypto_hash::{Algorithm, Hasher}; - /// ``` - imports_reused_with_self: Vec, - /// keep track of single use statements - /// such as `crypto_hash` in the example below - /// - /// ```rust,ignore - /// use crypto_hash; - /// ``` - single_use_usages: Vec<(Symbol, Span)>, -} - -impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess.opts.edition < Edition::Edition2018 { return; } - for item in &krate.items { - self.track_uses(&item); - } - for single_use in &self.single_use_usages { - if !self.imports_reused_with_self.contains(&single_use.0) { - span_lint_and_sugg( - cx, - SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, - "this import is redundant", - "remove it entirely", - String::new(), - Applicability::MachineApplicable, - ); - } - } + check_mod(cx, &krate.items); } } -impl SingleComponentPathImports { - fn track_uses(&mut self, item: &Item) { - if in_macro(item.span) || item.vis.kind.is_pub() { - return; +fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, + // such as `self::crypto_hash` in the example below + // ```rust,ignore + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); + + // keep track of single use statements + // such as `crypto_hash` in the example below + // ```rust,ignore + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + for item in items { + track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); + } + + for single_use in &single_use_usages { + if !imports_reused_with_self.contains(&single_use.0) { + span_lint_and_sugg( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + "remove it entirely", + String::new(), + Applicability::MachineApplicable, + ); } + } +} + +fn track_uses( + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec<(Symbol, Span)>, +) { + if in_macro(item.span) || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + check_mod(cx, &items); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; - match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - for item in items.iter() { - self.track_uses(&item); + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let ident = &segments[0].ident; + single_use_usages.push((ident.name, item.span)); } - }, - ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; + return; + } - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let ident = &segments[0].ident; - self.single_use_usages.push((ident.name, item.span)); - } + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); return; } - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - self.imports_reused_with_self.push(segments[1].ident.name); - return; - } - - // nested case such as `use self::{module1::Struct1, module2::Struct2}` - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { - let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - self.imports_reused_with_self.push(segments[0].ident.name); - } + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); } } } - }, - _ => {}, - } + } + }, + _ => {}, } } diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index 226d2b315d7..f66b445b7b6 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -25,3 +25,10 @@ mod hello_mod { #[allow(dead_code)] fn hello_mod() {} } + +mod hi_mod { + use self::regex::{Regex, RegexSet}; + use regex; + #[allow(dead_code)] + fn hi_mod() {} +} diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index 88bf7f1fc5a..09d48658595 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -25,3 +25,10 @@ mod hello_mod { #[allow(dead_code)] fn hello_mod() {} } + +mod hi_mod { + use self::regex::{Regex, RegexSet}; + use regex; + #[allow(dead_code)] + fn hi_mod() {} +} diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 646e6d3647a..7005fa8f125 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,16 +1,16 @@ error: this import is redundant - --> $DIR/single_component_path_imports.rs:6:1 + --> $DIR/single_component_path_imports.rs:24:5 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports.rs:24:5 + --> $DIR/single_component_path_imports.rs:6:1 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From a00de90cebedbf24e0cb75ca347f1752335aedca Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 5 Apr 2021 00:09:13 -0400 Subject: Fix ICE in `missing_panics_doc` --- clippy_lints/src/doc.rs | 5 ++++- tests/ui/doc.rs | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 69800f9d331..69d47850091 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -735,6 +735,9 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { intravisit::walk_expr(self, expr); } + // Panics in const blocks will cause compilation to fail. + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index d2c666bd290..c946a047f1b 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -1,8 +1,8 @@ //! This file tests for the `DOC_MARKDOWN` lint. -#![allow(dead_code)] +#![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes)] +#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] #![rustfmt::skip] /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) @@ -202,3 +202,20 @@ fn issue_2343() {} /// This should not cause an ICE: /// __|_ _|__||_| fn pulldown_cmark_crash() {} + +// issue #7033 - const_evaluatable_checked ICE +struct S +where [(); N.checked_next_power_of_two().unwrap()]: { + arr: [T; N.checked_next_power_of_two().unwrap()], + n: usize, +} + +impl S +where [(); N.checked_next_power_of_two().unwrap()]: { + fn new() -> Self { + Self { + arr: [T::default(); N.checked_next_power_of_two().unwrap()], + n: 0, + } + } +} -- cgit 1.4.1-3-g733a5 From d1df73228a7cdadb4d635c6ed77daeeaebbe10ff Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 11 Dec 2020 22:29:53 +0000 Subject: A new lint for shared code in if blocks * Added expression check for shared_code_in_if_blocks * Finishing touches for the shared_code_in_if_blocks lint * Applying PR suggestions * Update lints yay * Moved test into subfolder --- CHANGELOG.md | 1 + clippy_lints/src/copies.rs | 305 +++++++++++++++++++-- clippy_lints/src/lib.rs | 2 + clippy_lints/src/literal_representation.rs | 40 ++- .../src/methods/manual_saturating_arithmetic.rs | 48 ++-- clippy_utils/src/hir_utils.rs | 9 + tests/ui/checked_unwrap/complex_conditionals.rs | 2 +- tests/ui/if_same_then_else.rs | 3 +- tests/ui/if_same_then_else.stderr | 88 +++--- tests/ui/if_same_then_else2.rs | 3 +- tests/ui/if_same_then_else2.stderr | 80 +++--- tests/ui/let_if_seq.rs | 3 +- tests/ui/let_if_seq.stderr | 8 +- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 47 ++++ tests/ui/shared_code_in_if_blocks/shared_at_top.rs | 83 ++++++ .../shared_at_top_and_bot.rs | 22 ++ .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 157 +++++++++++ 17 files changed, 737 insertions(+), 164 deletions(-) create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.rs create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.rs create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs create mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index fb4a0b500f2..790bca4ac18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2455,6 +2455,7 @@ Released 2018-09-13 [`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 [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated +[`shared_code_in_if_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#shared_code_in_if_blocks [`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 46093a16571..29ff1970705 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -3,7 +3,10 @@ use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHas use clippy_utils::{get_parent_expr, if_sequence}; use rustc_hir::{Block, Expr, ExprKind}; 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 std::borrow::Cow; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -104,7 +107,45 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); +declare_clippy_lint! { + /// **What it does:** Checks if the `if` and `else` block contain shared code that can be + /// moved out of the blocks. + /// + /// **Why is this bad?** Duplicate code is less maintainable. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// let foo = if … { + /// println!("Hello World"); + /// 13 + /// } else { + /// println!("Hello World"); + /// 42 + /// }; + /// ``` + /// + /// Could be written as: + /// ```ignore + /// println!("Hello World"); + /// let foo = if … { + /// 13 + /// } else { + /// 42 + /// }; + /// ``` + pub SHARED_CODE_IN_IF_BLOCKS, + pedantic, + "`if` statement with shared code in all blocks" +} + +declare_lint_pass!(CopyAndPaste => [ + IFS_SAME_COND, + SAME_FUNCTIONS_IN_IF_CONDITION, + IF_SAME_THEN_ELSE, + SHARED_CODE_IN_IF_BLOCKS +]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -121,30 +162,256 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { } let (conds, blocks) = if_sequence(expr); - lint_same_then_else(cx, &blocks); + // Conditions lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); + // Block duplication + lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); } } } -/// Implementation of `IF_SAME_THEN_ELSE`. -fn lint_same_then_else(cx: &LateContext<'_>, blocks: &[&Block<'_>]) { - let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) }; +/// Implementation of `SHARED_CODE_IN_IF_BLOCKS` and `IF_SAME_THEN_ELSE` if the blocks are equal. +fn lint_same_then_else<'tcx>( + cx: &LateContext<'tcx>, + blocks: &[&Block<'tcx>], + has_unconditional_else: bool, + expr: &'tcx Expr<'_>, +) { + // We only lint ifs with multiple blocks + // TODO xFrednet 2021-01-01: Check if it's an else if block + if blocks.len() < 2 { + return; + } - if let Some((i, j)) = search_same_sequenced(blocks, eq) { - span_lint_and_note( + let has_expr = blocks[0].expr.is_some(); + + // Check if each block has shared code + let mut start_eq = usize::MAX; + let mut end_eq = usize::MAX; + let mut expr_eq = true; + for (index, win) in blocks.windows(2).enumerate() { + let l_stmts = win[0].stmts; + let r_stmts = win[1].stmts; + + let mut evaluator = SpanlessEq::new(cx); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); + let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { + evaluator.eq_stmt(l, r) + }); + let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); + + // IF_SAME_THEN_ELSE + // We only lint the first two blocks (index == 0). Further blocks will be linted when that if + // statement is checked + if index == 0 && block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + win[0].span, + "this `if` has identical blocks", + Some(win[1].span), + "same as this", + ); + + return; + } + + start_eq = start_eq.min(current_start_eq); + end_eq = end_eq.min(current_end_eq); + expr_eq &= block_expr_eq; + + // We can return if the eq count is 0 from both sides or if it has no unconditional else case + if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + return; + } + } + + if has_expr && !expr_eq { + end_eq = 0; + } + + // Check if the regions are overlapping. Set `end_eq` to prevent the overlap + let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); + if (start_eq + end_eq) > min_block_size { + end_eq = min_block_size - start_eq; + } + + // Only the start is the same + if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { + emit_shared_code_in_if_blocks_lint(cx, start_eq, 0, false, blocks, expr); + } else if end_eq != 0 && (!has_expr || !expr_eq) { + let block = blocks[blocks.len() - 1]; + let stmts = block.stmts.split_at(start_eq).1; + let (block_stmts, moved_stmts) = stmts.split_at(stmts.len() - end_eq); + + // Scan block + let mut walker = SymbolFinderVisitor::new(cx); + for stmt in block_stmts { + intravisit::walk_stmt(&mut walker, stmt); + } + let mut block_defs = walker.defs; + + // Scan moved stmts + let mut moved_start: Option = None; + let mut walker = SymbolFinderVisitor::new(cx); + for (index, stmt) in moved_stmts.iter().enumerate() { + intravisit::walk_stmt(&mut walker, stmt); + + for value in &walker.uses { + // Well we can't move this and all prev statements. So reset + if block_defs.contains(&value) { + moved_start = Some(index + 1); + walker.defs.drain().for_each(|x| { + block_defs.insert(x); + }); + } + } + + walker.uses.clear(); + } + + if let Some(moved_start) = moved_start { + end_eq -= moved_start; + } + + let mut end_linable = true; + if let Some(expr) = block.expr { + intravisit::walk_expr(&mut walker, expr); + end_linable = walker.uses.iter().any(|x| !block_defs.contains(x)); + } + + emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); + } +} + +fn emit_shared_code_in_if_blocks_lint( + cx: &LateContext<'tcx>, + start_stmts: usize, + end_stmts: usize, + lint_end: bool, + blocks: &[&Block<'tcx>], + if_expr: &'tcx Expr<'_>, +) { + if start_stmts == 0 && !lint_end { + return; + } + + // (help, span, suggestion) + let mut suggestions: Vec<(&str, Span, String)> = vec![]; + + if start_stmts > 0 { + let block = blocks[0]; + let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); + let span_end = block.stmts[start_stmts - 1].span.source_callsite(); + + let cond_span = first_line_of_span(cx, if_expr.span).until(block.span); + let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_span = block.stmts[0].span.source_callsite().to(span_end); + let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); + + let span = span_start.to(span_end); + suggestions.push(("START HELP", span, suggestion.to_string())); + } + + if lint_end { + let block = blocks[blocks.len() - 1]; + let span_end = block.span.shrink_to_hi(); + + let moved_start = if end_stmts == 0 && block.expr.is_some() { + block.expr.unwrap().span + } else { + block.stmts[block.stmts.len() - end_stmts].span + } + .source_callsite(); + let moved_end = if let Some(expr) = block.expr { + expr.span + } else { + block.stmts[block.stmts.len() - 1].span + } + .source_callsite(); + + let moved_span = moved_start.to(moved_end); + let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let indent = indent_of(cx, if_expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); + + let span = moved_start.to(span_end); + suggestions.push(("END_RANGE", span, suggestion.to_string())); + } + + if suggestions.len() == 1 { + let (_, span, sugg) = &suggestions[0]; + span_lint_and_sugg( cx, - IF_SAME_THEN_ELSE, - j.span, - "this `if` has identical blocks", - Some(i.span), - "same as this", + SHARED_CODE_IN_IF_BLOCKS, + *span, + "All code blocks contain the same code", + "Consider moving the code out like this", + sugg.clone(), + Applicability::Unspecified, + ); + } else { + span_lint_and_then( + cx, + SHARED_CODE_IN_IF_BLOCKS, + if_expr.span, + "All if blocks contain the same code", + move |diag| { + for (help, span, sugg) in suggestions { + diag.span_suggestion(span, help, sugg, Applicability::Unspecified); + } + }, ); } } +pub struct SymbolFinderVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + defs: FxHashSet, + uses: FxHashSet, +} + +impl<'a, 'tcx> SymbolFinderVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + SymbolFinderVisitor { + cx, + defs: FxHashSet::default(), + uses: FxHashSet::default(), + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { + let local_id = l.pat.hir_id; + self.defs.insert(local_id); + if let Some(expr) = l.init { + intravisit::walk_expr(self, expr); + } + } + + fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { + if let rustc_hir::QPath::Resolved(_, ref path) = *qpath { + if path.segments.len() == 1 { + if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { + self.uses.insert(var); + } + } + } + } +} + /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { @@ -198,15 +465,3 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { ); } } - -fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> -where - Eq: Fn(&T, &T) -> bool, -{ - for win in exprs.windows(2) { - if eq(&win[0], &win[1]) { - return Some((&win[0], &win[1])); - } - } - None -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b93e6b5b2e8..ba98a2a2607 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -616,6 +616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, + &copies::SHARED_CODE_IN_IF_BLOCKS, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, @@ -1365,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&casts::PTR_AS_PTR), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7fd55151226..b736eeff6bf 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -464,32 +464,28 @@ impl DecimalLiteralRepresentation { { return Err(WarningType::DecimalRepresentation); } - } else if digits.len() < 4 { - // Lint for Literals with a hex-representation of 2 or 3 digits - let f = &digits[0..1]; // first digit - let s = &digits[1..]; // suffix - - // Powers of 2 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) - // Powers of 2 minus 1 - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } } else { - // Lint for Literals with a hex-representation of 4 digits or more let f = &digits[0..1]; // first digit let m = &digits[1..digits.len() - 1]; // middle digits, except last let s = &digits[1..]; // suffix - - // Powers of 2 with a margin of +15/-16 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) - // Lint for representations with only 0s and Fs, while allowing 7 as the first - // digit - || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) - { - return Err(WarningType::DecimalRepresentation); + if digits.len() < 4 { + // Powers of 2 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) + // Powers of 2 minus 1 + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } + } else { + // Powers of 2 with a margin of +15/-16 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) + // Lint for representations with only 0s and Fs, while allowing 7 as the first + // digit + || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 6c5a842a912..ecb8b72ef46 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -44,44 +44,28 @@ pub fn check( // "mul" is omitted because lhs can be negative. _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } else { match (mm, arith) { (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (), _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + super::MANUAL_SATURATING_ARITHMETIC, + expr.span, + "manual saturating arithmetic", + &format!("try using `saturating_{}`", arith), + format!( + "{}.saturating_{}({})", + snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), + arith, + snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), + ), + applicability, + ); } #[derive(PartialEq, Eq)] diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 0c3b8b89171..8b90fedbafe 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -483,6 +483,15 @@ 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)) } +/// Counts how many elements of the slices are equal as per `eq_fn`. +pub fn count_eq( + left: &mut dyn Iterator, + right: &mut dyn Iterator, + mut eq_fn: impl FnMut(&X, &X) -> bool, +) -> usize { + left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count() +} + /// 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).deny_side_effects().eq_expr(left, right) diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index c986c992a07..bb5b6f5ec04 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 9c5fe02f751..35a2e139c11 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -5,7 +5,8 @@ clippy::never_loop, clippy::no_effect, clippy::unused_unit, - clippy::zero_divided_by_zero + clippy::zero_divided_by_zero, + clippy::shared_code_in_if_blocks )] struct Foo { diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index d9fdf06fa8b..2f38052fc20 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -1,111 +1,111 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:28:12 + --> $DIR/if_same_then_else.rs:21:13 | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block +LL | if true { + | _____________^ LL | | Foo { bar: 42 }; LL | | 0..10; +LL | | ..; ... | LL | | foo(); -LL | | } +LL | | } else { | |_____^ | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else.rs:20:13 + --> $DIR/if_same_then_else.rs:29:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | Foo { bar: 42 }; LL | | 0..10; -LL | | ..; ... | LL | | foo(); -LL | | } else { +LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:66:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | 0.0 -LL | | }; - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:64:21 + --> $DIR/if_same_then_else.rs:65:21 | LL | let _ = if true { | _____________________^ LL | | 0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:73:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:67:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | -0.0 +LL | | 0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:71:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:72:21 | LL | let _ = if true { | _____________________^ LL | | -0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:89:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:74:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | 42 +LL | | -0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:87:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:88:21 | LL | let _ = if true { | _____________________^ LL | | 42 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:101:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:90:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block +LL | | 42 +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:95:13 + | +LL | if true { + | _____________^ LL | | let bar = if true { 42 } else { 43 }; LL | | +LL | | while foo() { ... | LL | | bar + 1; -LL | | } +LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:94:13 + --> $DIR/if_same_then_else.rs:102:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | let bar = if true { 42 } else { 43 }; LL | | -LL | | while foo() { ... | LL | | bar + 1; -LL | | } else { +LL | | } | |_____^ error: aborting due to 5 previous errors diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index a2ff1b741ca..8164b125570 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -5,7 +5,8 @@ clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, - clippy::single_element_loop + clippy::single_element_loop, + clippy::shared_code_in_if_blocks )] 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 454322d8aac..941b92f23f3 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -24,18 +24,22 @@ LL | | if foo.is_some() { LL | | } LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:35:12 + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else2.rs:21:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let Some(a) = Some(42) {} +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::(42); +... | +LL | | } LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { @@ -43,18 +47,18 @@ LL | if true { LL | | if let Some(a) = Some(42) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:42:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | if let Some(a) = Some(42) {} LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { @@ -62,18 +66,18 @@ LL | if true { LL | | if let (1, .., 3) = (1, 2, 3) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:92:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | f32::NAN -LL | | }; +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { @@ -81,18 +85,18 @@ LL | let _ = if true { LL | | f32::NAN LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:99:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | Ok("foo")?; -LL | | } +LL | | f32::NAN +LL | | }; | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { @@ -100,18 +104,18 @@ LL | if true { LL | | Ok("foo")?; LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:124:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); +LL | | //~ ERROR same body as `if` block +LL | | Ok("foo")?; LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { @@ -120,6 +124,16 @@ LL | | let foo = ""; LL | | return Ok(&foo[0..]); LL | | } else { | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:124:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ error: aborting due to 6 previous errors diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 32a67f181df..9fd3f875a5f 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -2,7 +2,8 @@ unused_variables, unused_assignments, clippy::similar_names, - clippy::blacklisted_name + clippy::blacklisted_name, + clippy::shared_code_in_if_blocks )] #![warn(clippy::useless_let_if_seq)] diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index 7de560c7348..9cf2e10a5ee 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:64:5 + --> $DIR/let_if_seq.rs:65: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:69:5 + --> $DIR/let_if_seq.rs:70: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:77:5 + --> $DIR/let_if_seq.rs:78: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:106:5 + --> $DIR/let_if_seq.rs:107:5 | LL | / let mut baz = 0; LL | | if f() { diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs new file mode 100644 index 00000000000..22f1fe95f79 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -0,0 +1,47 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the end of blocks + +fn simple_examples() { + // TODO xFrednet 2021-01-06: Test with const literals at the end + let x = 1; + + let _ = if x == 7 { + println!("Branch I"); + let start_value = 0; + println!("=^.^="); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + } else { + println!("Branch II"); + let start_value = 8; + println!("xD"); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + // TODO xFrednet 2021-01-12: This +} + +/// Tests where the blocks are not linted due to the used value scope +fn not_moveable_due_to_value_scope() { + // TODO xFrednet 2021-01-12: This +} + +fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs new file mode 100644 index 00000000000..0a6828127e9 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs @@ -0,0 +1,83 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the start of blocks + +fn simple_examples() { + let x = 0; + + // Simple + if true { + println!("Hello World!"); + println!("I'm branch nr: 1"); + } else { + println!("Hello World!"); + println!("I'm branch nr: 2"); + } + + // Else if + if x == 0 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I'm the true start index of arrays"); + } else if x == 1 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I start counting from 1 so my array starts from `1`"); + } else { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("Ha, Pascal allows you to start the array where you want") + } + + // Return a value + let _ = if x == 7 { + let y = 16; + println!("What can I say except: \"you're welcome?\""); + let _ = y; + x + } else { + let y = 16; + println!("Thank you"); + y + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 10; + + // Can't be automatically moved because used_value_name is getting used again + let used_value_name = 19; + if x == 10 { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 1; + } else { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 2; + } + let _ = used_value_name; + + // This can be automatically moved as `can_be_overridden` is not used again + let can_be_overridden = 8; + let _ = can_be_overridden; + if x == 11 { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 111; + } else { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 222; + } +} + +fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs new file mode 100644 index 00000000000..8de69623e5d --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs @@ -0,0 +1,22 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// shared_code_in_if_blocks at the top and bottom of the if blocks + +fn main() { + // TODO xFrednet 2021-01-12: This +} + +// General TODOs By xFrednet: + +// +// * Make a test with overlapping eq regions (else ifs) +// * Test if as function parameter, tuple constructor, index, while loop condition +// * Test where only the expression is the same +// * Test where the block only has an expression +// * Test with let on a previous line let _ = \n if... +// * Tests with unreadable formatting (Inline if, Not indented) +// * Test multiline condition if x == 9 \n x == 8 {} +// * Test if for return/break (Only move to front) +// * Test in inline closures +// * Test with structs and tuples diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs new file mode 100644 index 00000000000..1f12e9348d4 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -0,0 +1,157 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the start of blocks + +// Tests with value references are includes in "shared_code_at_bot.rs" + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + let y = 1 << 5; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 1"); + } else { + let y = 1 << 7; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 2"); + } + + // No else + if x == 2 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Howdy stranger =^.^="); + + println!("Bye Bye World"); + } else if x == 9 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Hello reviewer :D"); + + println!("Bye Bye World"); + } + + // Overlapping statements only in else if blocks -> Don't lint + if x == 0 { + println!("I'm important!") + } else if x == 17 { + println!("I share code in else if"); + + println!("x is 17"); + } else { + println!("I share code in else if"); + + println!("x is nether x nor 17"); + } + + // Mutability is different + if x == 13 { + let mut y = 9; + println!("Value y is: {}", y); + y += 16; + let _z1 = y; + } else { + let y = 9; + println!("Value y is: {}", y); + let _z2 = y; + } + + // Same blocks but at start and bottom so no `if_same_then_else` lint + if x == 418 { + let y = 9; + let z = 8; + let _ = (x, y, z); + // Don't tell the programmer, my code is also in the else block + } else if x == 419 { + println!("+-----------+"); + println!("| |"); + println!("| O O |"); + println!("| ° |"); + println!("| \\_____/ |"); + println!("| |"); + println!("+-----------+"); + } else { + let y = 9; + let z = 8; + let _ = (x, y, z); + // I'm so much better than the x == 418 block. Trust me + } +} + +/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint +fn trigger_other_lint() { + let x = 0; + let y = 1; + + // Same block + if x == 0 { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } else { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } + + // More complex same blocks + if x == 17 { + #[derive(Debug)] + struct Duck { + num: u64, + }; + let pet = Duck { num: 18 }; + println!("{:?}", pet); + } else { + #[derive(Debug)] + struct Duck { + num: u64, + }; + let pet = Duck { num: 18 }; + println!("{:?}", pet); + } + + // Only same expression + let _ = if x == 6 { 7 } else { 7 }; + + // Same in else if block + let _ = if x == 67 { + println!("Well I'm the most important block"); + "I'm a pretty string" + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + + if y == 90 { + "=^.^=" + } else { + ":D" + } + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + + if y == 90 { + "=^.^=" + } else { + ":D" + } + }; +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 469ff96db3cc397be46ea9d19c72148c6a95eb6a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 13 Jan 2021 20:01:15 +0100 Subject: The shared_code_in_if_blocks lint only operats on entire if expr not else ifs --- clippy_lints/src/copies.rs | 52 ++++++---- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 107 ++++++++++++++++++++- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 10 ++ 3 files changed, 148 insertions(+), 21 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 29ff1970705..7162d809ec6 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,7 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; -use clippy_utils::{get_parent_expr, if_sequence}; -use rustc_hir::{Block, Expr, ExprKind}; +use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; +use crate::utils::{ + first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, + snippet, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, +}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -136,7 +141,7 @@ declare_clippy_lint! { /// }; /// ``` pub SHARED_CODE_IN_IF_BLOCKS, - pedantic, + nursery, "`if` statement with shared code in all blocks" } @@ -180,7 +185,7 @@ fn lint_same_then_else<'tcx>( ) { // We only lint ifs with multiple blocks // TODO xFrednet 2021-01-01: Check if it's an else if block - if blocks.len() < 2 { + if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { return; } @@ -190,7 +195,7 @@ fn lint_same_then_else<'tcx>( let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; - for (index, win) in blocks.windows(2).enumerate() { + for win in blocks.windows(2) { let l_stmts = win[0].stmts; let r_stmts = win[1].stmts; @@ -202,9 +207,7 @@ fn lint_same_then_else<'tcx>( let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); // IF_SAME_THEN_ELSE - // We only lint the first two blocks (index == 0). Further blocks will be linted when that if - // statement is checked - if index == 0 && block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { + if block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { span_lint_and_note( cx, IF_SAME_THEN_ELSE, @@ -215,16 +218,24 @@ fn lint_same_then_else<'tcx>( ); return; + } else { + println!( + "{:?}\n - expr_eq: {:10}, l_stmts.len(): {:10}, r_stmts.len(): {:10}", + win[0].span, + block_expr_eq, + l_stmts.len(), + r_stmts.len() + ) } start_eq = start_eq.min(current_start_eq); end_eq = end_eq.min(current_end_eq); expr_eq &= block_expr_eq; + } - // We can return if the eq count is 0 from both sides or if it has no unconditional else case - if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { - return; - } + // SHARED_CODE_IN_IF_BLOCKS prerequisites + if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + return; } if has_expr && !expr_eq { @@ -275,11 +286,14 @@ fn lint_same_then_else<'tcx>( end_eq -= moved_start; } - let mut end_linable = true; - if let Some(expr) = block.expr { + let end_linable = if let Some(expr) = block.expr { intravisit::walk_expr(&mut walker, expr); - end_linable = walker.uses.iter().any(|x| !block_defs.contains(x)); - } + walker.uses.iter().any(|x| !block_defs.contains(x)) + } else if end_eq == 0 { + false + } else { + true + }; emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); } @@ -351,7 +365,7 @@ fn emit_shared_code_in_if_blocks_lint( SHARED_CODE_IN_IF_BLOCKS, *span, "All code blocks contain the same code", - "Consider moving the code out like this", + "Consider moving the statements out like this", sugg.clone(), Applicability::Unspecified, ); diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index 22f1fe95f79..b85bb2e1f96 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -32,16 +32,119 @@ fn simple_examples() { println!("Block end!"); result }; + + if x == 9 { + println!("The index is: 6"); + + println!("Same end of block"); + } else if x == 8 { + println!("The index is: 4"); + + println!("Same end of block"); + } else { + println!("Same end of block"); + } + + // TODO xFrednet 2021-01.13: Fix lint for `if let` + let index = Some(8); + if let Some(index) = index { + println!("The index is: {}", index); + + println!("Same end of block"); + } else { + println!("Same end of block"); + } + + if x == 9 { + if x == 8 { + // No parent!! + println!("Hello World"); + println!("---"); + } else { + println!("Hello World"); + } + } } /// Simple examples where the move can cause some problems due to moved values fn simple_but_suggestion_is_invalid() { - // TODO xFrednet 2021-01-12: This + let x = 16; + + // Local value + let later_used_value = 17; + if x == 9 { + let _ = 9; + let later_used_value = "A string value"; + println!("{}", later_used_value); + } else { + let later_used_value = "A string value"; + println!("{}", later_used_value); + // I'm expecting a note about this + } + println!("{}", later_used_value); + + // outer function + if x == 78 { + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } else { + println!("Separator print statement"); + + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } + simple_examples(); } /// Tests where the blocks are not linted due to the used value scope fn not_moveable_due_to_value_scope() { - // TODO xFrednet 2021-01-12: This + let x = 18; + + // Using a local value in the moved code + if x == 9 { + let y = 18; + println!("y is: `{}`", y); + } else { + let y = "A string"; + println!("y is: `{}`", y); + } + + // Using a local value in the expression + let _ = if x == 0 { + let mut result = x + 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + } else { + let mut result = x - 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + }; + + let _ = if x == 7 { + let z1 = 100; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + } else { + let z1 = 300; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + }; } fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index 1f12e9348d4..480758777f1 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -152,6 +152,16 @@ fn trigger_other_lint() { ":D" } }; + + if x == 0 { + println!("I'm single"); + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + } } fn main() {} -- cgit 1.4.1-3-g733a5 From 8efc6acc05387738313798069b8553d0ab9c92e5 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 16 Jan 2021 14:04:14 +0100 Subject: Improved shared_code_in_if_blocks output readability and added tests --- clippy_lints/src/copies.rs | 69 +++++++++------ tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 35 ++++++++ .../shared_at_top_and_bot.rs | 98 ++++++++++++++++++---- 3 files changed, 161 insertions(+), 41 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 7162d809ec6..089f8c3ab0d 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,16 +1,16 @@ use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{ - first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, - snippet, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, + first_line_of_span, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, snippet, + snippet_opt, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, HirId}; +use rustc_hir::{Block, Expr, ExprKind, HirId}; 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::{source_map::Span, BytePos}; use std::borrow::Cow; declare_clippy_lint! { @@ -218,14 +218,6 @@ fn lint_same_then_else<'tcx>( ); return; - } else { - println!( - "{:?}\n - expr_eq: {:10}, l_stmts.len(): {:10}, r_stmts.len(): {:10}", - win[0].span, - block_expr_eq, - l_stmts.len(), - r_stmts.len() - ) } start_eq = start_eq.min(current_start_eq); @@ -328,7 +320,7 @@ fn emit_shared_code_in_if_blocks_lint( let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); let span = span_start.to(span_end); - suggestions.push(("START HELP", span, suggestion.to_string())); + suggestions.push(("start", span, suggestion.to_string())); } if lint_end { @@ -354,31 +346,56 @@ fn emit_shared_code_in_if_blocks_lint( let suggestion = "}\n".to_string() + &moved_snipped; let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); - let span = moved_start.to(span_end); - suggestions.push(("END_RANGE", span, suggestion.to_string())); + let mut span = moved_start.to(span_end); + // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt()); + if snippet_opt(cx, test_span) + .map(|snip| snip == " ") + .unwrap_or_default() + { + span = span.with_lo(test_span.lo()); + } + + suggestions.push(("end", span, suggestion.to_string())); } if suggestions.len() == 1 { - let (_, span, sugg) = &suggestions[0]; + let (place_str, span, sugg) = suggestions.pop().unwrap(); + let msg = format!("All if blocks contain the same code at the {}", place_str); + let help = format!("Consider moving the {} statements out like this", place_str); span_lint_and_sugg( cx, SHARED_CODE_IN_IF_BLOCKS, - *span, - "All code blocks contain the same code", - "Consider moving the statements out like this", - sugg.clone(), + span, + msg.as_str(), + help.as_str(), + sugg, Applicability::Unspecified, ); - } else { + } else if suggestions.len() == 2 { + let (_, end_span, end_sugg) = suggestions.pop().unwrap(); + let (_, start_span, start_sugg) = suggestions.pop().unwrap(); span_lint_and_then( cx, SHARED_CODE_IN_IF_BLOCKS, - if_expr.span, - "All if blocks contain the same code", + start_span, + "All if blocks contain the same code at the start and the end. Here at the start:", move |diag| { - for (help, span, sugg) in suggestions { - diag.span_suggestion(span, help, sugg, Applicability::Unspecified); - } + diag.span_note(end_span, "And here at the end:"); + + diag.span_suggestion( + start_span, + "Consider moving the start statements out like this:", + start_sugg, + Applicability::Unspecified, + ); + + diag.span_suggestion( + end_span, + "And consider moving the end statements out like this:", + end_sugg, + Applicability::Unspecified, + ); }, ); } diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index b85bb2e1f96..a586a1c9d45 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -33,6 +33,7 @@ fn simple_examples() { result }; + // Else if block if x == 9 { println!("The index is: 6"); @@ -40,9 +41,32 @@ fn simple_examples() { } else if x == 8 { println!("The index is: 4"); + // We should only get a lint trigger for the last statement + println!("This is also eq with the else block"); println!("Same end of block"); } else { println!("Same end of block"); + println!("This is also eq with the else block"); + } + + // Use of outer scope value + let outer_scope_value = "I'm outside the if block"; + if x < 99 { + let z = "How are you"; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } else { + let z = 45678000; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); } // TODO xFrednet 2021-01.13: Fix lint for `if let` @@ -147,4 +171,15 @@ fn not_moveable_due_to_value_scope() { }; } +#[rustfmt::skip] +fn test_suggestion_with_weird_formatting() { + let x = 9; + let mut a = 0; + let mut b = 0; + + // The error message still looks weird tbh but this is the best I can do + // for weird formatting + if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs index 8de69623e5d..70f969aaf2e 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs @@ -3,20 +3,88 @@ // shared_code_in_if_blocks at the top and bottom of the if blocks -fn main() { - // TODO xFrednet 2021-01-12: This +struct DataPack { + id: u32, + name: String, + some_data: Vec, } -// General TODOs By xFrednet: - -// -// * Make a test with overlapping eq regions (else ifs) -// * Test if as function parameter, tuple constructor, index, while loop condition -// * Test where only the expression is the same -// * Test where the block only has an expression -// * Test with let on a previous line let _ = \n if... -// * Tests with unreadable formatting (Inline if, Not indented) -// * Test multiline condition if x == 9 \n x == 8 {} -// * Test if for return/break (Only move to front) -// * Test in inline closures -// * Test with structs and tuples +fn overlapping_eq_regions() { + let x = 9; + + // Overlap with separator + if x == 7 { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } else { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + println!("Overlap separator"); + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } + + // Overlap with separator + if x == 99 { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } else { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } +} + +fn complexer_example() { + fn gen_id(x: u32, y: u32) -> u32 { + let x = x & 0x0000_ffff; + let y = (y & 0xffff_0000) << 16; + x | y + } + + fn process_data(data: DataPack) { + let _ = data; + } + + let x = 8; + let y = 9; + if (x > 7 && y < 13) || (x + y) % 2 == 1 { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("From the a `{}` to the b `{}`", a, b); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } else { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("The new ID is '{}'", e_id); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From b1d26e544f10b814d9793294d0c05dd2ace5d0dc Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 16 Jan 2021 20:37:50 +0100 Subject: Improved shared_code_in_if_blocks message and added test stderrs --- clippy_lints/src/copies.rs | 169 ++++++++++++++++----- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/literal_representation.rs | 40 ++--- tests/ui/if_same_then_else2.stderr | 22 +-- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 19 +++ .../shared_code_in_if_blocks/shared_at_bot.stderr | 131 ++++++++++++++++ .../shared_code_in_if_blocks/shared_at_top.stderr | 83 ++++++++++ .../shared_at_top_and_bot.rs | 29 ++++ .../shared_at_top_and_bot.stderr | 154 +++++++++++++++++++ .../valid_if_blocks.stderr | 107 +++++++++++++ 10 files changed, 692 insertions(+), 64 deletions(-) create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.stderr create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr create mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 089f8c3ab0d..47732f4766d 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,16 +1,16 @@ use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{ - first_line_of_span, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, snippet, - snippet_opt, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, + first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, + reindent_multiline, snippet, snippet_opt, span_lint_and_note, span_lint_and_then, ContainsName, }; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, HirId}; 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, BytePos}; +use rustc_span::{source_map::Span, symbol::Symbol, BytePos}; use std::borrow::Cow; declare_clippy_lint! { @@ -184,7 +184,6 @@ fn lint_same_then_else<'tcx>( expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks - // TODO xFrednet 2021-01-01: Check if it's an else if block if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { return; } @@ -242,36 +241,61 @@ fn lint_same_then_else<'tcx>( // Only the start is the same if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { - emit_shared_code_in_if_blocks_lint(cx, start_eq, 0, false, blocks, expr); - } else if end_eq != 0 && (!has_expr || !expr_eq) { + let block = blocks[0]; + let start_stmts = block.stmts.split_at(start_eq).0; + + let mut start_walker = UsedValueFinderVisitor::new(cx); + for stmt in start_stmts { + intravisit::walk_stmt(&mut start_walker, stmt); + } + + emit_shared_code_in_if_blocks_lint( + cx, + start_eq, + 0, + false, + check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr), + blocks, + expr, + ); + } else if end_eq != 0 || (has_expr && expr_eq) { let block = blocks[blocks.len() - 1]; - let stmts = block.stmts.split_at(start_eq).1; - let (block_stmts, moved_stmts) = stmts.split_at(stmts.len() - end_eq); + let (start_stmts, block_stmts) = block.stmts.split_at(start_eq); + let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq); + + // Scan start + let mut start_walker = UsedValueFinderVisitor::new(cx); + for stmt in start_stmts { + intravisit::walk_stmt(&mut start_walker, stmt); + } + let mut moved_syms = start_walker.def_symbols; // Scan block - let mut walker = SymbolFinderVisitor::new(cx); + let mut block_walker = UsedValueFinderVisitor::new(cx); for stmt in block_stmts { - intravisit::walk_stmt(&mut walker, stmt); + intravisit::walk_stmt(&mut block_walker, stmt); } - let mut block_defs = walker.defs; + let mut block_defs = block_walker.defs; // Scan moved stmts let mut moved_start: Option = None; - let mut walker = SymbolFinderVisitor::new(cx); - for (index, stmt) in moved_stmts.iter().enumerate() { - intravisit::walk_stmt(&mut walker, stmt); + let mut end_walker = UsedValueFinderVisitor::new(cx); + for (index, stmt) in end_stmts.iter().enumerate() { + intravisit::walk_stmt(&mut end_walker, stmt); - for value in &walker.uses { + for value in &end_walker.uses { // Well we can't move this and all prev statements. So reset if block_defs.contains(&value) { moved_start = Some(index + 1); - walker.defs.drain().for_each(|x| { + end_walker.defs.drain().for_each(|x| { block_defs.insert(x); }); + + end_walker.def_symbols.clear(); } } - walker.uses.clear(); + end_walker.uses.clear(); } if let Some(moved_start) = moved_start { @@ -279,15 +303,65 @@ fn lint_same_then_else<'tcx>( } let end_linable = if let Some(expr) = block.expr { - intravisit::walk_expr(&mut walker, expr); - walker.uses.iter().any(|x| !block_defs.contains(x)) + intravisit::walk_expr(&mut end_walker, expr); + end_walker.uses.iter().any(|x| !block_defs.contains(x)) } else if end_eq == 0 { false } else { true }; - emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); + if end_linable { + end_walker.def_symbols.drain().for_each(|x| { + moved_syms.insert(x); + }); + } + + emit_shared_code_in_if_blocks_lint( + cx, + start_eq, + end_eq, + end_linable, + check_for_warn_of_moved_symbol(cx, &moved_syms, expr), + blocks, + expr, + ); + } +} + +fn check_for_warn_of_moved_symbol( + cx: &LateContext<'tcx>, + symbols: &FxHashSet, + if_expr: &'tcx Expr<'_>, +) -> bool { + // Obs true as we include the current if block + if let Some(block) = get_enclosing_block(cx, if_expr.hir_id) { + let ignore_span = block.span.shrink_to_lo().to(if_expr.span); + + symbols + .iter() + .filter(|sym| !sym.as_str().starts_with('_')) + .any(move |sym| { + let mut walker = ContainsName { + name: *sym, + result: false, + }; + + // Scan block + block + .stmts + .iter() + .filter(|stmt| !ignore_span.overlaps(stmt.span)) + .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + + if let Some(expr) = block.expr { + intravisit::walk_expr(&mut walker, expr); + } + + walker.result + }) + } else { + false } } @@ -296,6 +370,7 @@ fn emit_shared_code_in_if_blocks_lint( start_stmts: usize, end_stmts: usize, lint_end: bool, + warn_about_moved_symbol: bool, blocks: &[&Block<'tcx>], if_expr: &'tcx Expr<'_>, ) { @@ -305,7 +380,9 @@ fn emit_shared_code_in_if_blocks_lint( // (help, span, suggestion) let mut suggestions: Vec<(&str, Span, String)> = vec![]; + let mut add_expr_note = false; + // Construct suggestions if start_stmts > 0 { let block = blocks[0]; let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); @@ -357,21 +434,29 @@ fn emit_shared_code_in_if_blocks_lint( } suggestions.push(("end", span, suggestion.to_string())); + add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit() } + let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { + if add_expr_note { + diag.note("The end suggestion probably needs some adjustments to use the expression result correctly."); + } + + if warn_about_moved_symbol { + diag.warn("Some moved values might need to be renamed to avoid wrong references."); + } + }; + + // Emit lint if suggestions.len() == 1 { let (place_str, span, sugg) = suggestions.pop().unwrap(); let msg = format!("All if blocks contain the same code at the {}", place_str); let help = format!("Consider moving the {} statements out like this", place_str); - span_lint_and_sugg( - cx, - SHARED_CODE_IN_IF_BLOCKS, - span, - msg.as_str(), - help.as_str(), - sugg, - Applicability::Unspecified, - ); + span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| { + diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); + + add_optional_msgs(diag); + }); } else if suggestions.len() == 2 { let (_, end_span, end_sugg) = suggestions.pop().unwrap(); let (_, start_span, start_sugg) = suggestions.pop().unwrap(); @@ -396,28 +481,39 @@ fn emit_shared_code_in_if_blocks_lint( end_sugg, Applicability::Unspecified, ); + + add_optional_msgs(diag); }, ); } } -pub struct SymbolFinderVisitor<'a, 'tcx> { +/// This visitor collects HirIds and Symbols of defined symbols and HirIds of used values. +struct UsedValueFinderVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + + /// The `HirId`s of defined values in the scanned statements defs: FxHashSet, + + /// The Symbols of the defined symbols in the scanned statements + def_symbols: FxHashSet, + + /// The `HirId`s of the used values uses: FxHashSet, } -impl<'a, 'tcx> SymbolFinderVisitor<'a, 'tcx> { +impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { - SymbolFinderVisitor { + UsedValueFinderVisitor { cx, defs: FxHashSet::default(), + def_symbols: FxHashSet::default(), uses: FxHashSet::default(), } } } -impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -427,6 +523,11 @@ impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { let local_id = l.pat.hir_id; self.defs.insert(local_id); + + if let Some(sym) = l.pat.simple_ident() { + self.def_symbols.insert(sym.name); + } + if let Some(expr) = l.init { intravisit::walk_expr(self, expr); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ba98a2a2607..9afbf95a342 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1366,7 +1366,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&casts::PTR_AS_PTR), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), @@ -2064,6 +2063,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(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b736eeff6bf..7fd55151226 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -464,28 +464,32 @@ impl DecimalLiteralRepresentation { { return Err(WarningType::DecimalRepresentation); } + } else if digits.len() < 4 { + // Lint for Literals with a hex-representation of 2 or 3 digits + let f = &digits[0..1]; // first digit + let s = &digits[1..]; // suffix + + // Powers of 2 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) + // Powers of 2 minus 1 + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } } else { + // Lint for Literals with a hex-representation of 4 digits or more let f = &digits[0..1]; // first digit let m = &digits[1..digits.len() - 1]; // middle digits, except last let s = &digits[1..]; // suffix - if digits.len() < 4 { - // Powers of 2 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) - // Powers of 2 minus 1 - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } - } else { - // Powers of 2 with a margin of +15/-16 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) - // Lint for representations with only 0s and Fs, while allowing 7 as the first - // digit - || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } + + // Powers of 2 with a margin of +15/-16 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) + // Lint for representations with only 0s and Fs, while allowing 7 as the first + // digit + || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) + { + return Err(WarningType::DecimalRepresentation); } } diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index 941b92f23f3..b31eb46a982 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -27,7 +27,7 @@ LL | | } else { | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:21:12 + --> $DIR/if_same_then_else2.rs:22:12 | LL | } else { | ____________^ @@ -40,7 +40,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:33:13 + --> $DIR/if_same_then_else2.rs:34:13 | LL | if true { | _____________^ @@ -49,7 +49,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:35:12 + --> $DIR/if_same_then_else2.rs:36:12 | LL | } else { | ____________^ @@ -59,7 +59,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:40:13 + --> $DIR/if_same_then_else2.rs:41:13 | LL | if true { | _____________^ @@ -68,7 +68,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:42:12 + --> $DIR/if_same_then_else2.rs:43:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:90:21 + --> $DIR/if_same_then_else2.rs:91:21 | LL | let _ = if true { | _____________________^ @@ -87,7 +87,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:92:12 + --> $DIR/if_same_then_else2.rs:93:12 | LL | } else { | ____________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:97:13 + --> $DIR/if_same_then_else2.rs:98:13 | LL | if true { | _____________^ @@ -106,7 +106,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:99:12 + --> $DIR/if_same_then_else2.rs:100:12 | LL | } else { | ____________^ @@ -116,7 +116,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:121:20 + --> $DIR/if_same_then_else2.rs:122:20 | LL | } else if true { | ____________________^ @@ -126,7 +126,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:124:12 + --> $DIR/if_same_then_else2.rs:125:12 | LL | } else { | ____________^ diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index a586a1c9d45..5392da7ac7a 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -171,6 +171,25 @@ fn not_moveable_due_to_value_scope() { }; } +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + x << 2 + } else { + let _ = 6; + x << 2 + }; + + if x == 9 { + x * 4 + } else { + let _ = 17; + x * 4 + } +} + #[rustfmt::skip] fn test_suggestion_with_weird_formatting() { let x = 9; diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr new file mode 100644 index 00000000000..62a95157d61 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr @@ -0,0 +1,131 @@ +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:31:5 + | +LL | / let result = false; +LL | | println!("Block end!"); +LL | | result +LL | | }; + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_bot.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the end statements out like this + | +LL | } +LL | let result = false; +LL | println!("Block end!"); +LL | result; + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:66:5 + | +LL | / println!( +LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | | outer_scope_value +LL | | ); +LL | | } + | |_____^ + | +help: Consider moving the end statements out like this + | +LL | } +LL | println!( +LL | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | outer_scope_value +LL | ); + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_bot.rs:83:9 + | +LL | / if x == 8 { +LL | | // No parent!! +LL | | println!("Hello World"); + | |____________________________________^ + | +help: Consider moving the start statements out like this + | +LL | println!("Hello World"); +LL | if x == 8 { + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:104:5 + | +LL | / let later_used_value = "A string value"; +LL | | println!("{}", later_used_value); +LL | | // I'm expecting a note about this +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the end statements out like this + | +LL | } +LL | let later_used_value = "A string value"; +LL | println!("{}", later_used_value); + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:117:5 + | +LL | / let simple_examples = "I now identify as a &str :)"; +LL | | println!("This is the new simple_example: {}", simple_examples); +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the end statements out like this + | +LL | } +LL | let simple_examples = "I now identify as a &str :)"; +LL | println!("This is the new simple_example: {}", simple_examples); + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:182:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:189:5 + | +LL | / x * 4 +LL | | } + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:201:44 + | +LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } + | ^^^^^^^^^^^ + | +help: Consider moving the end statements out like this + | +LL | if x == 17 { b = 1; a = 0x99; } else { } +LL | a = 0x99; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr new file mode 100644 index 00000000000..c2bd8a070ed --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -0,0 +1,83 @@ +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:10:5 + | +LL | / if true { +LL | | println!("Hello World!"); + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: Consider moving the start statements out like this + | +LL | println!("Hello World!"); +LL | if true { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:19:5 + | +LL | / if x == 0 { +LL | | let y = 9; +LL | | println!("The value y was set to: `{}`", y); +LL | | let _z = y; + | |___________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this + | +LL | let y = 9; +LL | println!("The value y was set to: `{}`", y); +LL | let _z = y; +LL | if x == 0 { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:40:5 + | +LL | / let _ = if x == 7 { +LL | | let y = 16; + | |___________________^ + | +help: Consider moving the start statements out like this + | +LL | let y = 16; +LL | let _ = if x == 7 { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:58:5 + | +LL | / if x == 10 { +LL | | let used_value_name = "Different type"; +LL | | println!("Str: {}", used_value_name); + | |_____________________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this + | +LL | let used_value_name = "Different type"; +LL | println!("Str: {}", used_value_name); +LL | if x == 10 { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:72:5 + | +LL | / if x == 11 { +LL | | let can_be_overridden = "Move me"; +LL | | println!("I'm also moveable"); + | |______________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this + | +LL | let can_be_overridden = "Move me"; +LL | println!("I'm also moveable"); +LL | if x == 11 { + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs index 70f969aaf2e..46a8f931aaf 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs @@ -87,4 +87,33 @@ fn complexer_example() { } } +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + let _ = 19; + + let _splitter = 6; + + x << 2 + } else { + let _ = 19; + + x << 2 + }; + + if x == 9 { + let _ = 17; + + let _splitter = 6; + + x * 4 + } else { + let _ = 17; + + x * 4 + } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr new file mode 100644 index 00000000000..1ba7211b469 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr @@ -0,0 +1,154 @@ +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:16:5 + | +LL | / if x == 7 { +LL | | let t = 7; +LL | | let _overlap_start = t * 2; +LL | | let _overlap_end = 2 * t; + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top_and_bot.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:28:5 + | +LL | / let _u = 9; +LL | | } + | |_____^ +help: Consider moving the start statements out like this: + | +LL | let t = 7; +LL | let _overlap_start = t * 2; +LL | let _overlap_end = 2 * t; +LL | if x == 7 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | let _u = 9; + | + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:32:5 + | +LL | / if x == 99 { +LL | | let r = 7; +LL | | let _overlap_start = r; +LL | | let _overlap_middle = r * r; + | |____________________________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:43:5 + | +LL | / let _overlap_end = r * r * r; +LL | | let z = "end"; +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this: + | +LL | let r = 7; +LL | let _overlap_start = r; +LL | let _overlap_middle = r * r; +LL | if x == 99 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | let _overlap_end = r * r * r; +LL | let z = "end"; + | + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:61:5 + | +LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { +LL | | let a = 0xcafe; +LL | | let b = 0xffff00ff; +LL | | let e_id = gen_id(a, b); + | |________________________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:81:5 + | +LL | / let pack = DataPack { +LL | | id: e_id, +LL | | name: "Player 1".to_string(), +LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | | }; +LL | | process_data(pack); +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this: + | +LL | let a = 0xcafe; +LL | let b = 0xffff00ff; +LL | let e_id = gen_id(a, b); +LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | let pack = DataPack { +LL | id: e_id, +LL | name: "Player 1".to_string(), +LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | }; + ... + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:94:5 + | +LL | / let _ = if x == 7 { +LL | | let _ = 19; + | |___________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:103:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the start statements out like this: + | +LL | let _ = 19; +LL | let _ = if x == 7 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | x << 2; + | + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:106:5 + | +LL | / if x == 9 { +LL | | let _ = 17; + | |___________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:115:5 + | +LL | / x * 4 +LL | | } + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the start statements out like this: + | +LL | let _ = 17; +LL | if x == 9 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | x * 4 + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr new file mode 100644 index 00000000000..003c060f072 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -0,0 +1,107 @@ +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:102:15 + | +LL | if x == 0 { + | _______________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/valid_if_blocks.rs:106:12 + | +LL | } else { + | ____________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } + | |_____^ + +error: All if blocks contain the same code at the end + --> $DIR/valid_if_blocks.rs:125:5 + | +LL | / let pet = Duck { num: 18 }; +LL | | println!("{:?}", pet); +LL | | } + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: Consider moving the end statements out like this + | +LL | } +LL | let pet = Duck { num: 18 }; +LL | println!("{:?}", pet); + | + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:130:23 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:130:34 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:136:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | +... | +LL | | } +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:145:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | +... | +LL | | } +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:158:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:161:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 65ed5a632f5a5fc99a015e7ad0537c401fb61023 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 16 Jan 2021 21:04:47 +0100 Subject: Updated code for dogfood --- clippy_lints/src/copies.rs | 142 +++++++++++---------- tests/ui/shared_code_in_if_blocks/shared_at_top.rs | 20 +++ .../shared_code_in_if_blocks/shared_at_top.stderr | 40 +++++- 3 files changed, 134 insertions(+), 68 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 47732f4766d..4a701dc8898 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,8 +1,9 @@ -use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{ - first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, - reindent_multiline, snippet, snippet_opt, span_lint_and_note, span_lint_and_then, ContainsName, + both, count_eq, eq_expr_value, first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, in_macro, + indent_of, parent_node_is_if_expr, reindent_multiline, run_lints, search_same, snippet, snippet_opt, + span_lint_and_note, span_lint_and_then, ContainsName, SpanlessEq, SpanlessHash, }; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; @@ -188,57 +189,15 @@ fn lint_same_then_else<'tcx>( return; } - let has_expr = blocks[0].expr.is_some(); - // Check if each block has shared code - let mut start_eq = usize::MAX; - let mut end_eq = usize::MAX; - let mut expr_eq = true; - for win in blocks.windows(2) { - let l_stmts = win[0].stmts; - let r_stmts = win[1].stmts; - - let mut evaluator = SpanlessEq::new(cx); - let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); - let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { - evaluator.eq_stmt(l, r) - }); - let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); - - // IF_SAME_THEN_ELSE - if block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - win[0].span, - "this `if` has identical blocks", - Some(win[1].span), - "same as this", - ); - - return; - } - - start_eq = start_eq.min(current_start_eq); - end_eq = end_eq.min(current_end_eq); - expr_eq &= block_expr_eq; - } + let has_expr = blocks[0].expr.is_some(); + let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); // SHARED_CODE_IN_IF_BLOCKS prerequisites if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { return; } - if has_expr && !expr_eq { - end_eq = 0; - } - - // Check if the regions are overlapping. Set `end_eq` to prevent the overlap - let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); - if (start_eq + end_eq) > min_block_size { - end_eq = min_block_size - start_eq; - } - // Only the start is the same if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { let block = blocks[0]; @@ -302,14 +261,13 @@ fn lint_same_then_else<'tcx>( end_eq -= moved_start; } - let end_linable = if let Some(expr) = block.expr { - intravisit::walk_expr(&mut end_walker, expr); - end_walker.uses.iter().any(|x| !block_defs.contains(x)) - } else if end_eq == 0 { - false - } else { - true - }; + let end_linable = block.expr.map_or_else( + || end_eq != 0, + |expr| { + intravisit::walk_expr(&mut end_walker, expr); + end_walker.uses.iter().any(|x| !block_defs.contains(x)) + }, + ); if end_linable { end_walker.def_symbols.drain().for_each(|x| { @@ -329,13 +287,67 @@ fn lint_same_then_else<'tcx>( } } +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { + let mut start_eq = usize::MAX; + let mut end_eq = usize::MAX; + let mut expr_eq = true; + for win in blocks.windows(2) { + let l_stmts = win[0].stmts; + let r_stmts = win[1].stmts; + + let mut evaluator = SpanlessEq::new(cx); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); + let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { + evaluator.eq_stmt(l, r) + }); + let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); + + // IF_SAME_THEN_ELSE + if_chain! { + if block_expr_eq; + if l_stmts.len() == r_stmts.len(); + if l_stmts.len() == current_start_eq; + if run_lints(cx, &[IF_SAME_THEN_ELSE], win[0].hir_id); + if run_lints(cx, &[IF_SAME_THEN_ELSE], win[1].hir_id); + then { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + win[0].span, + "this `if` has identical blocks", + Some(win[1].span), + "same as this", + ); + + return (0, 0, false); + } + } + + start_eq = start_eq.min(current_start_eq); + end_eq = end_eq.min(current_end_eq); + expr_eq &= block_expr_eq; + } + + let has_expr = blocks[0].expr.is_some(); + if has_expr && !expr_eq { + end_eq = 0; + } + + // Check if the regions are overlapping. Set `end_eq` to prevent the overlap + let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); + if (start_eq + end_eq) > min_block_size { + end_eq = min_block_size - start_eq; + } + + (start_eq, end_eq, expr_eq) +} + fn check_for_warn_of_moved_symbol( cx: &LateContext<'tcx>, symbols: &FxHashSet, if_expr: &'tcx Expr<'_>, ) -> bool { - // Obs true as we include the current if block - if let Some(block) = get_enclosing_block(cx, if_expr.hir_id) { + get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); symbols @@ -360,9 +372,7 @@ fn check_for_warn_of_moved_symbol( walker.result }) - } else { - false - } + }) } fn emit_shared_code_in_if_blocks_lint( @@ -410,12 +420,10 @@ fn emit_shared_code_in_if_blocks_lint( block.stmts[block.stmts.len() - end_stmts].span } .source_callsite(); - let moved_end = if let Some(expr) = block.expr { - expr.span - } else { - block.stmts[block.stmts.len() - 1].span - } - .source_callsite(); + let moved_end = block + .expr + .map_or_else(|| block.stmts[block.stmts.len() - 1].span, |expr| expr.span) + .source_callsite(); let moved_span = moved_start.to(moved_end); let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); @@ -488,7 +496,7 @@ fn emit_shared_code_in_if_blocks_lint( } } -/// This visitor collects HirIds and Symbols of defined symbols and HirIds of used values. +/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values. struct UsedValueFinderVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs index 0a6828127e9..496939f2a5e 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs @@ -80,4 +80,24 @@ fn simple_but_suggestion_is_invalid() { } } +/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. +fn check_if_same_than_else_mask() { + let x = 2021; + + #[allow(clippy::if_same_then_else)] + if x == 2020 { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } else { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } + + if x == 2019 { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } else { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr index c2bd8a070ed..d217083c413 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -79,5 +79,43 @@ LL | println!("I'm also moveable"); LL | if x == 11 { | -error: aborting due to 5 previous errors +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:88:5 + | +LL | / if x == 2020 { +LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + | |________________________________________________________________^ + | +help: Consider moving the start statements out like this + | +LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); +LL | if x == 2020 { + | + +error: this `if` has identical blocks + --> $DIR/shared_at_top.rs:96:18 + | +LL | if x == 2019 { + | __________________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/shared_at_top.rs:98:12 + | +LL | } else { + | ____________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } + | |_____^ + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From c74e49eab911ce5d55b3692324dea64905323a88 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 23 Feb 2021 21:16:19 +0100 Subject: Adapted the lint to use the new SpanlessEq --- clippy_lints/src/copies.rs | 28 ++++++--- clippy_utils/src/hir_utils.rs | 9 +-- tests/ui/if_same_then_else.stderr | 28 +-------- tests/ui/if_same_then_else2.stderr | 40 +----------- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 15 +---- .../shared_code_in_if_blocks/shared_at_bot.stderr | 72 +++++++++++++--------- .../shared_code_in_if_blocks/shared_at_top.stderr | 24 ++++---- .../shared_at_top_and_bot.stderr | 40 ++++++------ .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 17 ----- .../valid_if_blocks.stderr | 34 +++------- 10 files changed, 111 insertions(+), 196 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 4a701dc8898..7a9f299d8e0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -295,11 +295,21 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, let l_stmts = win[0].stmts; let r_stmts = win[1].stmts; + // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. + // The comparison therefor needs to be done in a way that builds the correct context. let mut evaluator = SpanlessEq::new(cx); + let mut evaluator = evaluator.inter_expr(); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); - let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { - evaluator.eq_stmt(l, r) - }); + + let current_end_eq = { + // We skip the middle statements which can't be equal + let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq; + let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count); + let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count); + it1.zip(it2) + .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) + }; let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); // IF_SAME_THEN_ELSE @@ -458,8 +468,8 @@ fn emit_shared_code_in_if_blocks_lint( // Emit lint if suggestions.len() == 1 { let (place_str, span, sugg) = suggestions.pop().unwrap(); - let msg = format!("All if blocks contain the same code at the {}", place_str); - let help = format!("Consider moving the {} statements out like this", place_str); + let msg = format!("all if blocks contain the same code at the {}", place_str); + let help = format!("consider moving the {} statements out like this", place_str); span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| { diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); @@ -472,20 +482,20 @@ fn emit_shared_code_in_if_blocks_lint( cx, SHARED_CODE_IN_IF_BLOCKS, start_span, - "All if blocks contain the same code at the start and the end. Here at the start:", + "all if blocks contain the same code at the start and the end. Here at the start", move |diag| { - diag.span_note(end_span, "And here at the end:"); + diag.span_note(end_span, "and here at the end"); diag.span_suggestion( start_span, - "Consider moving the start statements out like this:", + "consider moving the start statements out like this", start_sugg, Applicability::Unspecified, ); diag.span_suggestion( end_span, - "And consider moving the end statements out like this:", + "and consider moving the end statements out like this", end_sugg, Applicability::Unspecified, ); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 8b90fedbafe..000c249bb0f 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -58,13 +58,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. - fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { + pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, locals: HirIdMap::default(), } } + #[allow(dead_code)] pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { self.inter_expr().eq_block(left, right) } @@ -82,7 +83,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } -struct HirEqInterExpr<'a, 'b, 'tcx> { +pub struct HirEqInterExpr<'a, 'b, 'tcx> { inner: &'a mut SpanlessEq<'b, 'tcx>, // When binding are declared, the binding ID in the left expression is mapped to the one on the @@ -92,7 +93,7 @@ struct HirEqInterExpr<'a, 'b, 'tcx> { } impl HirEqInterExpr<'_, '_, '_> { - fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { self.eq_pat(&l.pat, &r.pat) @@ -159,7 +160,7 @@ impl HirEqInterExpr<'_, '_, '_> { } #[allow(clippy::similar_names)] - fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index 2f38052fc20..74b11bac487 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -82,31 +82,5 @@ LL | | 42 LL | | }; | |_____^ -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:95:13 - | -LL | if true { - | _____________^ -LL | | let bar = if true { 42 } else { 43 }; -LL | | -LL | | while foo() { -... | -LL | | bar + 1; -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:102:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | let bar = if true { 42 } else { 43 }; -LL | | -... | -LL | | bar + 1; -LL | | } - | |_____^ - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index b31eb46a982..a0e636e3a61 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -1,19 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:21:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | for _ in &[42] { -LL | | let bar: &Option<_> = &Some::(42); -... | -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::if-same-then-else` implied by `-D warnings` -note: same as this - --> $DIR/if_same_then_else2.rs:12:13 + --> $DIR/if_same_then_else2.rs:13:13 | LL | if true { | _____________^ @@ -33,7 +19,7 @@ LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block LL | | for _ in &[42] { -LL | | let foo: &Option<_> = &Some::(42); +LL | | let bar: &Option<_> = &Some::(42); ... | LL | | } LL | | } @@ -115,25 +101,5 @@ LL | | Ok("foo")?; LL | | } | |_____^ -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:122:20 - | -LL | } else if true { - | ____________________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:125:12 - | -LL | } else { - | ____________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); -LL | | } - | |_____^ - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index 5392da7ac7a..7974ea2f59c 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -4,7 +4,6 @@ // This tests the shared_code_in_if_blocks lint at the end of blocks fn simple_examples() { - // TODO xFrednet 2021-01-06: Test with const literals at the end let x = 1; let _ = if x == 7 { @@ -45,8 +44,8 @@ fn simple_examples() { println!("This is also eq with the else block"); println!("Same end of block"); } else { - println!("Same end of block"); println!("This is also eq with the else block"); + println!("Same end of block"); } // Use of outer scope value @@ -69,21 +68,11 @@ fn simple_examples() { ); } - // TODO xFrednet 2021-01.13: Fix lint for `if let` - let index = Some(8); - if let Some(index) = index { - println!("The index is: {}", index); - - println!("Same end of block"); - } else { - println!("Same end of block"); - } - if x == 9 { if x == 8 { // No parent!! - println!("Hello World"); println!("---"); + println!("Hello World"); } else { println!("Hello World"); } diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr index 62a95157d61..5ecf47bf920 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr @@ -1,5 +1,5 @@ -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:31:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:30:5 | LL | / let result = false; LL | | println!("Block end!"); @@ -13,7 +13,7 @@ note: the lint level is defined here LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | let result = false; @@ -21,8 +21,21 @@ LL | println!("Block end!"); LL | result; | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:66:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:48:5 + | +LL | / println!("Same end of block"); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Same end of block"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:65:5 | LL | / println!( LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", @@ -31,7 +44,7 @@ LL | | ); LL | | } | |_____^ | -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | println!( @@ -40,22 +53,21 @@ LL | outer_scope_value LL | ); | -error: All if blocks contain the same code at the start - --> $DIR/shared_at_bot.rs:83:9 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:77:9 | -LL | / if x == 8 { -LL | | // No parent!! -LL | | println!("Hello World"); - | |____________________________________^ +LL | / println!("Hello World"); +LL | | } + | |_________^ | -help: Consider moving the start statements out like this +help: consider moving the end statements out like this | +LL | } LL | println!("Hello World"); -LL | if x == 8 { | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:104:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:93:5 | LL | / let later_used_value = "A string value"; LL | | println!("{}", later_used_value); @@ -64,15 +76,15 @@ LL | | } | |_____^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | let later_used_value = "A string value"; LL | println!("{}", later_used_value); | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:117:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:106:5 | LL | / let simple_examples = "I now identify as a &str :)"; LL | | println!("This is the new simple_example: {}", simple_examples); @@ -80,52 +92,52 @@ LL | | } | |_____^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | let simple_examples = "I now identify as a &str :)"; LL | println!("This is the new simple_example: {}", simple_examples); | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:182:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:171:5 | LL | / x << 2 LL | | }; | |_____^ | = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | x << 2; | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:189:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:178:5 | LL | / x * 4 LL | | } | |_____^ | = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | x * 4 | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:201:44 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:190:44 | LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ | -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | if x == 17 { b = 1; a = 0x99; } else { } LL | a = 0x99; | -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr index d217083c413..1ad924aba6a 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -1,4 +1,4 @@ -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:10:5 | LL | / if true { @@ -10,13 +10,13 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | println!("Hello World!"); LL | if true { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:19:5 | LL | / if x == 0 { @@ -26,7 +26,7 @@ LL | | let _z = y; | |___________________^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let y = 9; LL | println!("The value y was set to: `{}`", y); @@ -34,20 +34,20 @@ LL | let _z = y; LL | if x == 0 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:40:5 | LL | / let _ = if x == 7 { LL | | let y = 16; | |___________________^ | -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let y = 16; LL | let _ = if x == 7 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:58:5 | LL | / if x == 10 { @@ -56,14 +56,14 @@ LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let used_value_name = "Different type"; LL | println!("Str: {}", used_value_name); LL | if x == 10 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:72:5 | LL | / if x == 11 { @@ -72,14 +72,14 @@ LL | | println!("I'm also moveable"); | |______________________________________^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let can_be_overridden = "Move me"; LL | println!("I'm also moveable"); LL | if x == 11 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:88:5 | LL | / if x == 2020 { @@ -87,7 +87,7 @@ LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); | |________________________________________________________________^ | -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr index 1ba7211b469..9f675a20a6d 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr @@ -1,4 +1,4 @@ -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:16:5 | LL | / if x == 7 { @@ -12,26 +12,26 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:28:5 | LL | / let _u = 9; LL | | } | |_____^ -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let t = 7; LL | let _overlap_start = t * 2; LL | let _overlap_end = 2 * t; LL | if x == 7 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | let _u = 9; | -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:32:5 | LL | / if x == 99 { @@ -40,7 +40,7 @@ LL | | let _overlap_start = r; LL | | let _overlap_middle = r * r; | |____________________________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:43:5 | LL | / let _overlap_end = r * r * r; @@ -48,21 +48,21 @@ LL | | let z = "end"; LL | | } | |_____^ = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let r = 7; LL | let _overlap_start = r; LL | let _overlap_middle = r * r; LL | if x == 99 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | let _overlap_end = r * r * r; LL | let z = "end"; | -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:61:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { @@ -71,7 +71,7 @@ LL | | let b = 0xffff00ff; LL | | let e_id = gen_id(a, b); | |________________________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:81:5 | LL | / let pack = DataPack { @@ -83,14 +83,14 @@ LL | | process_data(pack); LL | | } | |_____^ = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let a = 0xcafe; LL | let b = 0xffff00ff; LL | let e_id = gen_id(a, b); LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | let pack = DataPack { @@ -100,51 +100,51 @@ LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], LL | }; ... -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:94:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:103:5 | LL | / x << 2 LL | | }; | |_____^ = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let _ = 19; LL | let _ = if x == 7 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | x << 2; | -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:106:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:115:5 | LL | / x * 4 LL | | } | |_____^ = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let _ = 17; LL | if x == 9 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | x * 4 diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index 480758777f1..a564b30cb1c 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -109,23 +109,6 @@ fn trigger_other_lint() { let _ = "This is a string"; } - // More complex same blocks - if x == 17 { - #[derive(Debug)] - struct Duck { - num: u64, - }; - let pet = Duck { num: 18 }; - println!("{:?}", pet); - } else { - #[derive(Debug)] - struct Duck { - num: u64, - }; - let pet = Duck { num: 18 }; - println!("{:?}", pet); - } - // Only same expression let _ = if x == 6 { 7 } else { 7 }; diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr index 003c060f072..d290c65822b 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -25,40 +25,20 @@ LL | | let _ = "This is a string"; LL | | } | |_____^ -error: All if blocks contain the same code at the end - --> $DIR/valid_if_blocks.rs:125:5 - | -LL | / let pet = Duck { num: 18 }; -LL | | println!("{:?}", pet); -LL | | } - | |_____^ - | -note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: Consider moving the end statements out like this - | -LL | } -LL | let pet = Duck { num: 18 }; -LL | println!("{:?}", pet); - | - error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:130:23 + --> $DIR/valid_if_blocks.rs:113:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:130:34 + --> $DIR/valid_if_blocks.rs:113:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:136:23 + --> $DIR/valid_if_blocks.rs:119:23 | LL | } else if x == 68 { | _______________________^ @@ -71,7 +51,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:145:12 + --> $DIR/valid_if_blocks.rs:128:12 | LL | } else { | ____________^ @@ -84,7 +64,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:158:23 + --> $DIR/valid_if_blocks.rs:141:23 | LL | } else if x == 68 { | _______________________^ @@ -94,7 +74,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:161:12 + --> $DIR/valid_if_blocks.rs:144:12 | LL | } else { | ____________^ @@ -103,5 +83,5 @@ LL | | println!("I'm a doppelgänger"); LL | | } | |_____^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 617c65baa90cdbd9228cacad4f2079a9d868d070 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 25 Feb 2021 23:33:46 +0100 Subject: Moving shared_code_in_if_blocks to clippy::complexity and running lintcheck --- clippy_lints/src/copies.rs | 6 +-- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/hir_utils.rs | 6 ++- clippy_utils/src/lib.rs | 18 ++++++-- .../checked_unwrap/complex_conditionals_nested.rs | 2 +- tests/ui/checked_unwrap/simple_conditionals.rs | 2 +- tests/ui/default_numeric_fallback.rs | 1 + tests/ui/default_numeric_fallback.stderr | 48 +++++++++++----------- tests/ui/if_same_then_else.stderr | 28 ++++++++++++- tests/ui/if_same_then_else2.stderr | 22 +++++++++- tests/ui/needless_bool/simple.rs | 3 +- tests/ui/needless_bool/simple.stderr | 8 ++-- tests/ui/needless_return.fixed | 9 +++- tests/ui/needless_return.rs | 9 +++- tests/ui/needless_return.stderr | 36 ++++++++-------- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 16 ++++++++ .../shared_code_in_if_blocks/shared_at_bot.stderr | 10 ++--- .../shared_code_in_if_blocks/shared_at_top.stderr | 6 +-- .../shared_at_top_and_bot.stderr | 8 ++-- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 8 ++++ .../valid_if_blocks.stderr | 16 ++++---- 21 files changed, 180 insertions(+), 84 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 7a9f299d8e0..8b94ac96880 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -142,7 +142,7 @@ declare_clippy_lint! { /// }; /// ``` pub SHARED_CODE_IN_IF_BLOCKS, - nursery, + complexity, "`if` statement with shared code in all blocks" } @@ -457,11 +457,11 @@ fn emit_shared_code_in_if_blocks_lint( let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { if add_expr_note { - diag.note("The end suggestion probably needs some adjustments to use the expression result correctly."); + diag.note("The end suggestion probably needs some adjustments to use the expression result correctly"); } if warn_about_moved_symbol { - diag.warn("Some moved values might need to be renamed to avoid wrong references."); + diag.warn("Some moved values might need to be renamed to avoid wrong references"); } }; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9afbf95a342..3f5c1c7c526 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1485,6 +1485,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), + LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), @@ -2063,7 +2064,6 @@ 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(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 000c249bb0f..571eec9e530 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -96,9 +96,11 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { - self.eq_pat(&l.pat, &r.pat) + // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that + // these only get added if the init and type is equal. + both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) - && both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) + && self.eq_pat(&l.pat, &r.pat) }, (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => { self.eq_expr(l, r) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d6364625e70..f9576068dd6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -609,9 +609,9 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option { } } -struct ContainsName { - name: Symbol, - result: bool, +pub struct ContainsName { + pub name: Symbol, + pub result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName { @@ -1216,6 +1216,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } +/// This function returns true if the given expression is the `else` or `if else` part of an if +/// statement pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); @@ -1326,6 +1328,16 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } +/// This function checks if any of the lints in the slice is enabled for the provided `HirId`. +/// A lint counts as enabled with any of the levels: `Level::Forbid` | `Level::Deny` | `Level::Warn` +/// +/// ```ignore +/// #[deny(clippy::YOUR_AWESOME_LINT)] +/// println!("Hello, World!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == true +/// +/// #[allow(clippy::YOUR_AWESOME_LINT)] +/// println!("See you soon!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == false +/// ``` pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { lints.iter().any(|lint| { matches!( diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 2307996a48f..99e8fb95466 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] fn test_nested() { fn nested() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 36967630834..0459100d88a 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] macro_rules! m { ($a:expr) => { diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 0b3758952ac..de89f806c58 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -3,6 +3,7 @@ #![allow(clippy::never_loop)] #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::shared_code_in_if_blocks)] mod basic_expr { fn test() { diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index b31aa4ebcf8..d1c4c8203dd 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:10:17 + --> $DIR/default_numeric_fallback.rs:11:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,139 +7,139 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:18 + --> $DIR/default_numeric_fallback.rs:12:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:21 + --> $DIR/default_numeric_fallback.rs:12:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:24 + --> $DIR/default_numeric_fallback.rs:12:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:28 + --> $DIR/default_numeric_fallback.rs:13:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:31 + --> $DIR/default_numeric_fallback.rs:13:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:44 + --> $DIR/default_numeric_fallback.rs:13:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:47 + --> $DIR/default_numeric_fallback.rs:13:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:23 + --> $DIR/default_numeric_fallback.rs:14:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:13 + --> $DIR/default_numeric_fallback.rs:15:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:18 + --> $DIR/default_numeric_fallback.rs:15:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:18 + --> $DIR/default_numeric_fallback.rs:16:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:19:17 + --> $DIR/default_numeric_fallback.rs:20:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:37:21 + --> $DIR/default_numeric_fallback.rs:38:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:45:21 + --> $DIR/default_numeric_fallback.rs:46:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:51:21 + --> $DIR/default_numeric_fallback.rs:52:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:63:9 + --> $DIR/default_numeric_fallback.rs:64:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:69:27 + --> $DIR/default_numeric_fallback.rs:70:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:73:29 + --> $DIR/default_numeric_fallback.rs:74:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:87:21 + --> $DIR/default_numeric_fallback.rs:88:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:90:32 + --> $DIR/default_numeric_fallback.rs:91:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:108:28 + --> $DIR/default_numeric_fallback.rs:109:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:111:36 + --> $DIR/default_numeric_fallback.rs:112:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:131:23 + --> $DIR/default_numeric_fallback.rs:132:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index 74b11bac487..2f38052fc20 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -82,5 +82,31 @@ LL | | 42 LL | | }; | |_____^ -error: aborting due to 4 previous errors +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:95:13 + | +LL | if true { + | _____________^ +LL | | let bar = if true { 42 } else { 43 }; +LL | | +LL | | while foo() { +... | +LL | | bar + 1; +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:102:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | let bar = if true { 42 } else { 43 }; +LL | | +... | +LL | | bar + 1; +LL | | } + | |_____^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index a0e636e3a61..6524be0af85 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -101,5 +101,25 @@ LL | | Ok("foo")?; LL | | } | |_____^ -error: aborting due to 5 previous errors +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:122:20 + | +LL | } else if true { + | ____________________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:125:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/needless_bool/simple.rs b/tests/ui/needless_bool/simple.rs index e9f1428fc3a..678040fa98b 100644 --- a/tests/ui/needless_bool/simple.rs +++ b/tests/ui/needless_bool/simple.rs @@ -4,7 +4,8 @@ dead_code, clippy::no_effect, clippy::if_same_then_else, - clippy::needless_return + clippy::needless_return, + clippy::shared_code_in_if_blocks )] fn main() { diff --git a/tests/ui/needless_bool/simple.stderr b/tests/ui/needless_bool/simple.stderr index c57a8a042fb..0ccc9416bcd 100644 --- a/tests/ui/needless_bool/simple.stderr +++ b/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> $DIR/simple.rs:13:5 + --> $DIR/simple.rs:14:5 | LL | / if x { LL | | true @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::needless-bool` implied by `-D warnings` error: this if-then-else expression will always return false - --> $DIR/simple.rs:18:5 + --> $DIR/simple.rs:19:5 | LL | / if x { LL | | false @@ -21,7 +21,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> $DIR/simple.rs:33:5 + --> $DIR/simple.rs:34:5 | LL | / if x { LL | | return true; @@ -31,7 +31,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> $DIR/simple.rs:41:5 + --> $DIR/simple.rs:42:5 | LL | / if x { LL | | return false; diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 990475fcb58..ebf74cfef12 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,7 +1,12 @@ // run-rustfix -#![allow(unused, clippy::needless_bool)] -#![allow(clippy::if_same_then_else, clippy::single_match)] +#![allow(unused)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::shared_code_in_if_blocks, + clippy::needless_bool +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index dec3d84a020..2bebccdabca 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,7 +1,12 @@ // run-rustfix -#![allow(unused, clippy::needless_bool)] -#![allow(clippy::if_same_then_else, clippy::single_match)] +#![allow(unused)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::shared_code_in_if_blocks, + clippy::needless_bool +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index ae31d607541..075db22456f 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:18:5 + --> $DIR/needless_return.rs:23:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` @@ -7,103 +7,103 @@ LL | return true; = note: `-D clippy::needless-return` implied by `-D warnings` error: unneeded `return` statement - --> $DIR/needless_return.rs:22:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:27:9 + --> $DIR/needless_return.rs:32:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:29:9 + --> $DIR/needless_return.rs:34:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:17 + --> $DIR/needless_return.rs:40:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:13 + --> $DIR/needless_return.rs:42:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:44:9 + --> $DIR/needless_return.rs:49:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:46:16 + --> $DIR/needless_return.rs:51:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:5 + --> $DIR/needless_return.rs:59:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:59:9 + --> $DIR/needless_return.rs:64:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:61:9 + --> $DIR/needless_return.rs:66:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:68:14 + --> $DIR/needless_return.rs:73:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:83:9 + --> $DIR/needless_return.rs:88:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:85:9 + --> $DIR/needless_return.rs:90:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:106:32 + --> $DIR/needless_return.rs:111:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:111:13 + --> $DIR/needless_return.rs:116:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:113:20 + --> $DIR/needless_return.rs:118:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:119:32 + --> $DIR/needless_return.rs:124:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index 7974ea2f59c..6ff362cb752 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -190,4 +190,20 @@ fn test_suggestion_with_weird_formatting() { if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } } +fn fp_test() { + let x = 17; + + if x == 18 { + let y = 19; + if y < x { + println!("Trigger") + } + } else { + let z = 166; + if z < x { + println!("Trigger") + } + } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr index 5ecf47bf920..2268d8f5365 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr @@ -12,7 +12,7 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the end statements out like this | LL | } @@ -75,7 +75,7 @@ LL | | // I'm expecting a note about this LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the end statements out like this | LL | } @@ -91,7 +91,7 @@ LL | | println!("This is the new simple_example: {}", simple_examples); LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the end statements out like this | LL | } @@ -106,7 +106,7 @@ LL | / x << 2 LL | | }; | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the end statements out like this | LL | } @@ -120,7 +120,7 @@ LL | / x * 4 LL | | } | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the end statements out like this | LL | } diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr index 1ad924aba6a..76898a6ff02 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -25,7 +25,7 @@ LL | | println!("The value y was set to: `{}`", y); LL | | let _z = y; | |___________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let y = 9; @@ -55,7 +55,7 @@ LL | | let used_value_name = "Different type"; LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let used_value_name = "Different type"; @@ -71,7 +71,7 @@ LL | | let can_be_overridden = "Move me"; LL | | println!("I'm also moveable"); | |______________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let can_be_overridden = "Move me"; diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr index 9f675a20a6d..75c3d397f2e 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr @@ -47,7 +47,7 @@ LL | / let _overlap_end = r * r * r; LL | | let z = "end"; LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let r = 7; @@ -82,7 +82,7 @@ LL | | }; LL | | process_data(pack); LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let a = 0xcafe; @@ -113,7 +113,7 @@ note: and here at the end LL | / x << 2 LL | | }; | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the start statements out like this | LL | let _ = 19; @@ -138,7 +138,7 @@ note: and here at the end LL | / x * 4 LL | | } | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the start statements out like this | LL | let _ = 17; diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index a564b30cb1c..cd397db47d0 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -91,6 +91,14 @@ fn valid_examples() { let _ = (x, y, z); // I'm so much better than the x == 418 block. Trust me } + + let x = 1; + if true { + println!("{}", x); + } else { + let x = 2; + println!("{}", x); + } } /// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr index d290c65822b..2061cc25d21 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:102:15 + --> $DIR/valid_if_blocks.rs:110:15 | LL | if x == 0 { | _______________^ @@ -15,7 +15,7 @@ note: the lint level is defined here LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: same as this - --> $DIR/valid_if_blocks.rs:106:12 + --> $DIR/valid_if_blocks.rs:114:12 | LL | } else { | ____________^ @@ -26,19 +26,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:113:23 + --> $DIR/valid_if_blocks.rs:121:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:113:34 + --> $DIR/valid_if_blocks.rs:121:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:119:23 + --> $DIR/valid_if_blocks.rs:127:23 | LL | } else if x == 68 { | _______________________^ @@ -51,7 +51,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:128:12 + --> $DIR/valid_if_blocks.rs:136:12 | LL | } else { | ____________^ @@ -64,7 +64,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:141:23 + --> $DIR/valid_if_blocks.rs:149:23 | LL | } else if x == 68 { | _______________________^ @@ -74,7 +74,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:144:12 + --> $DIR/valid_if_blocks.rs:152:12 | LL | } else { | ____________^ -- cgit 1.4.1-3-g733a5 From 8c0b4d7f65802031f0131bc56de8fb8b275d70d7 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 26 Feb 2021 21:29:55 +0100 Subject: Only running shared_code_in_if_blocks only for if statements --- clippy_lints/src/copies.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 8b94ac96880..ff99a9a09b3 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -156,23 +156,25 @@ declare_lint_pass!(CopyAndPaste => [ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() { - // skip ifs directly in else, it will be checked in the parent if - if let Some(&Expr { - kind: ExprKind::If(_, _, Some(ref else_expr)), - .. - }) = get_parent_expr(cx, expr) - { - if else_expr.hir_id == expr.hir_id { - return; + if let ExprKind::If(_, _, _) = expr.kind { + // skip ifs directly in else, it will be checked in the parent if + if let Some(&Expr { + kind: ExprKind::If(_, _, Some(ref else_expr)), + .. + }) = get_parent_expr(cx, expr) + { + if else_expr.hir_id == expr.hir_id { + return; + } } - } - let (conds, blocks) = if_sequence(expr); - // Conditions - lint_same_cond(cx, &conds); - lint_same_fns_in_if_cond(cx, &conds); - // Block duplication - lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); + let (conds, blocks) = if_sequence(expr); + // Conditions + lint_same_cond(cx, &conds); + lint_same_fns_in_if_cond(cx, &conds); + // Block duplication + lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); + } } } } -- cgit 1.4.1-3-g733a5 From a6f54f5dfdfdf0017ffecfbcd6f43352b8b71ca1 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 1 Apr 2021 18:30:47 +0200 Subject: Renaming the lint to branches_sharing_code and fixing typos --- CHANGELOG.md | 2 +- clippy_lints/src/copies.rs | 35 ++-- clippy_lints/src/lib.rs | 5 +- clippy_utils/src/lib.rs | 2 +- tests/ui/branches_sharing_code/shared_at_bottom.rs | 209 +++++++++++++++++++++ .../branches_sharing_code/shared_at_bottom.stderr | 143 ++++++++++++++ tests/ui/branches_sharing_code/shared_at_top.rs | 103 ++++++++++ .../ui/branches_sharing_code/shared_at_top.stderr | 121 ++++++++++++ .../shared_at_top_and_bottom.rs | 119 ++++++++++++ .../shared_at_top_and_bottom.stderr | 154 +++++++++++++++ tests/ui/branches_sharing_code/valid_if_blocks.rs | 155 +++++++++++++++ .../branches_sharing_code/valid_if_blocks.stderr | 101 ++++++++++ tests/ui/checked_unwrap/complex_conditionals.rs | 2 +- .../checked_unwrap/complex_conditionals_nested.rs | 2 +- tests/ui/checked_unwrap/simple_conditionals.rs | 2 +- tests/ui/default_numeric_fallback.rs | 2 +- tests/ui/if_same_then_else.rs | 2 +- tests/ui/if_same_then_else2.rs | 2 +- tests/ui/let_if_seq.rs | 2 +- tests/ui/needless_bool/simple.rs | 2 +- tests/ui/needless_return.fixed | 2 +- tests/ui/needless_return.rs | 2 +- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 209 --------------------- .../shared_code_in_if_blocks/shared_at_bot.stderr | 143 -------------- tests/ui/shared_code_in_if_blocks/shared_at_top.rs | 103 ---------- .../shared_code_in_if_blocks/shared_at_top.stderr | 121 ------------ .../shared_at_top_and_bot.rs | 119 ------------ .../shared_at_top_and_bot.stderr | 154 --------------- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 155 --------------- .../valid_if_blocks.stderr | 101 ---------- 30 files changed, 1138 insertions(+), 1136 deletions(-) create mode 100644 tests/ui/branches_sharing_code/shared_at_bottom.rs create mode 100644 tests/ui/branches_sharing_code/shared_at_bottom.stderr create mode 100644 tests/ui/branches_sharing_code/shared_at_top.rs create mode 100644 tests/ui/branches_sharing_code/shared_at_top.stderr create mode 100644 tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs create mode 100644 tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr create mode 100644 tests/ui/branches_sharing_code/valid_if_blocks.rs create mode 100644 tests/ui/branches_sharing_code/valid_if_blocks.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 790bca4ac18..73997192ae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2129,6 +2129,7 @@ Released 2018-09-13 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata @@ -2455,7 +2456,6 @@ Released 2018-09-13 [`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 [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated -[`shared_code_in_if_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#shared_code_in_if_blocks [`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index ff99a9a09b3..1b982221b06 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,7 +1,8 @@ -use crate::utils::{ - both, count_eq, eq_expr_value, first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, in_macro, - indent_of, parent_node_is_if_expr, reindent_multiline, run_lints, search_same, snippet, snippet_opt, - span_lint_and_note, span_lint_and_then, ContainsName, SpanlessEq, SpanlessHash, +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; +use clippy_utils::{ + both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr, + run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash, }; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -141,7 +142,7 @@ declare_clippy_lint! { /// 42 /// }; /// ``` - pub SHARED_CODE_IN_IF_BLOCKS, + pub BRANCHES_SHARING_CODE, complexity, "`if` statement with shared code in all blocks" } @@ -150,7 +151,7 @@ declare_lint_pass!(CopyAndPaste => [ IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, - SHARED_CODE_IN_IF_BLOCKS + BRANCHES_SHARING_CODE ]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { @@ -173,17 +174,17 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); // Block duplication - lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); + lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr); } } } } -/// Implementation of `SHARED_CODE_IN_IF_BLOCKS` and `IF_SAME_THEN_ELSE` if the blocks are equal. +/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal. fn lint_same_then_else<'tcx>( cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>], - has_unconditional_else: bool, + has_conditional_else: bool, expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks @@ -195,8 +196,8 @@ fn lint_same_then_else<'tcx>( let has_expr = blocks[0].expr.is_some(); let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); - // SHARED_CODE_IN_IF_BLOCKS prerequisites - if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + // BRANCHES_SHARING_CODE prerequisites + if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { return; } @@ -210,7 +211,7 @@ fn lint_same_then_else<'tcx>( intravisit::walk_stmt(&mut start_walker, stmt); } - emit_shared_code_in_if_blocks_lint( + emit_branches_sharing_code_lint( cx, start_eq, 0, @@ -277,7 +278,7 @@ fn lint_same_then_else<'tcx>( }); } - emit_shared_code_in_if_blocks_lint( + emit_branches_sharing_code_lint( cx, start_eq, end_eq, @@ -298,7 +299,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, let r_stmts = win[1].stmts; // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. - // The comparison therefor needs to be done in a way that builds the correct context. + // The comparison therefore needs to be done in a way that builds the correct context. let mut evaluator = SpanlessEq::new(cx); let mut evaluator = evaluator.inter_expr(); @@ -387,7 +388,7 @@ fn check_for_warn_of_moved_symbol( }) } -fn emit_shared_code_in_if_blocks_lint( +fn emit_branches_sharing_code_lint( cx: &LateContext<'tcx>, start_stmts: usize, end_stmts: usize, @@ -472,7 +473,7 @@ fn emit_shared_code_in_if_blocks_lint( let (place_str, span, sugg) = suggestions.pop().unwrap(); let msg = format!("all if blocks contain the same code at the {}", place_str); let help = format!("consider moving the {} statements out like this", place_str); - span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| { + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| { diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); add_optional_msgs(diag); @@ -482,7 +483,7 @@ fn emit_shared_code_in_if_blocks_lint( let (_, start_span, start_sugg) = suggestions.pop().unwrap(); span_lint_and_then( cx, - SHARED_CODE_IN_IF_BLOCKS, + BRANCHES_SHARING_CODE, start_span, "all if blocks contain the same code at the start and the end. Here at the start", move |diag| { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3f5c1c7c526..43e89c2475f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -613,10 +613,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &collapsible_if::COLLAPSIBLE_IF, &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, + &copies::BRANCHES_SHARING_CODE, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, - &copies::SHARED_CODE_IN_IF_BLOCKS, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, @@ -1483,9 +1483,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&copies::BRANCHES_SHARING_CODE), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), @@ -1872,6 +1872,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&casts::CHAR_LIT_AS_U8), LintId::of(&casts::UNNECESSARY_CAST), + LintId::of(&copies::BRANCHES_SHARING_CODE), LintId::of(&double_comparison::DOUBLE_COMPARISONS), LintId::of(&double_parens::DOUBLE_PARENS), LintId::of(&duration_subsec::DURATION_SUBSEC), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f9576068dd6..7ce90ffd9f0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -47,7 +47,7 @@ pub mod usage; pub mod visitors; pub use self::attrs::*; -pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs new file mode 100644 index 00000000000..c389c243d44 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -0,0 +1,209 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests the branches_sharing_code lint at the end of blocks + +fn simple_examples() { + let x = 1; + + let _ = if x == 7 { + println!("Branch I"); + let start_value = 0; + println!("=^.^="); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + } else { + println!("Branch II"); + let start_value = 8; + println!("xD"); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + }; + + // Else if block + if x == 9 { + println!("The index is: 6"); + + println!("Same end of block"); + } else if x == 8 { + println!("The index is: 4"); + + // We should only get a lint trigger for the last statement + println!("This is also eq with the else block"); + println!("Same end of block"); + } else { + println!("This is also eq with the else block"); + println!("Same end of block"); + } + + // Use of outer scope value + let outer_scope_value = "I'm outside the if block"; + if x < 99 { + let z = "How are you"; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } else { + let z = 45678000; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } + + if x == 9 { + if x == 8 { + // No parent!! + println!("---"); + println!("Hello World"); + } else { + println!("Hello World"); + } + } +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 16; + + // Local value + let later_used_value = 17; + if x == 9 { + let _ = 9; + let later_used_value = "A string value"; + println!("{}", later_used_value); + } else { + let later_used_value = "A string value"; + println!("{}", later_used_value); + // I'm expecting a note about this + } + println!("{}", later_used_value); + + // outer function + if x == 78 { + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } else { + println!("Separator print statement"); + + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } + simple_examples(); +} + +/// Tests where the blocks are not linted due to the used value scope +fn not_moveable_due_to_value_scope() { + let x = 18; + + // Using a local value in the moved code + if x == 9 { + let y = 18; + println!("y is: `{}`", y); + } else { + let y = "A string"; + println!("y is: `{}`", y); + } + + // Using a local value in the expression + let _ = if x == 0 { + let mut result = x + 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + } else { + let mut result = x - 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + }; + + let _ = if x == 7 { + let z1 = 100; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + } else { + let z1 = 300; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + }; +} + +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + x << 2 + } else { + let _ = 6; + x << 2 + }; + + if x == 9 { + x * 4 + } else { + let _ = 17; + x * 4 + } +} + +#[rustfmt::skip] +fn test_suggestion_with_weird_formatting() { + let x = 9; + let mut a = 0; + let mut b = 0; + + // The error message still looks weird tbh but this is the best I can do + // for weird formatting + if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } +} + +fn fp_test() { + let x = 17; + + if x == 18 { + let y = 19; + if y < x { + println!("Trigger") + } + } else { + let z = 166; + if z < x { + println!("Trigger") + } + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr new file mode 100644 index 00000000000..271fcd8b6c1 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -0,0 +1,143 @@ +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:30:5 + | +LL | / let result = false; +LL | | println!("Block end!"); +LL | | result +LL | | }; + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_bottom.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | let result = false; +LL | println!("Block end!"); +LL | result; + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:48:5 + | +LL | / println!("Same end of block"); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Same end of block"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:65:5 + | +LL | / println!( +LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | | outer_scope_value +LL | | ); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!( +LL | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | outer_scope_value +LL | ); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:77:9 + | +LL | / println!("Hello World"); +LL | | } + | |_________^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Hello World"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:93:5 + | +LL | / let later_used_value = "A string value"; +LL | | println!("{}", later_used_value); +LL | | // I'm expecting a note about this +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the end statements out like this + | +LL | } +LL | let later_used_value = "A string value"; +LL | println!("{}", later_used_value); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:106:5 + | +LL | / let simple_examples = "I now identify as a &str :)"; +LL | | println!("This is the new simple_example: {}", simple_examples); +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the end statements out like this + | +LL | } +LL | let simple_examples = "I now identify as a &str :)"; +LL | println!("This is the new simple_example: {}", simple_examples); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:171:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:178:5 + | +LL | / x * 4 +LL | | } + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:190:44 + | +LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } + | ^^^^^^^^^^^ + | +help: consider moving the end statements out like this + | +LL | if x == 17 { b = 1; a = 0x99; } else { } +LL | a = 0x99; + | + +error: aborting due to 9 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs new file mode 100644 index 00000000000..e65bcfd7873 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -0,0 +1,103 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests the branches_sharing_code lint at the start of blocks + +fn simple_examples() { + let x = 0; + + // Simple + if true { + println!("Hello World!"); + println!("I'm branch nr: 1"); + } else { + println!("Hello World!"); + println!("I'm branch nr: 2"); + } + + // Else if + if x == 0 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I'm the true start index of arrays"); + } else if x == 1 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I start counting from 1 so my array starts from `1`"); + } else { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("Ha, Pascal allows you to start the array where you want") + } + + // Return a value + let _ = if x == 7 { + let y = 16; + println!("What can I say except: \"you're welcome?\""); + let _ = y; + x + } else { + let y = 16; + println!("Thank you"); + y + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 10; + + // Can't be automatically moved because used_value_name is getting used again + let used_value_name = 19; + if x == 10 { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 1; + } else { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 2; + } + let _ = used_value_name; + + // This can be automatically moved as `can_be_overridden` is not used again + let can_be_overridden = 8; + let _ = can_be_overridden; + if x == 11 { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 111; + } else { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 222; + } +} + +/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. +fn check_if_same_than_else_mask() { + let x = 2021; + + #[allow(clippy::if_same_then_else)] + if x == 2020 { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } else { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } + + if x == 2019 { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } else { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr new file mode 100644 index 00000000000..15867e9ea02 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -0,0 +1,121 @@ +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:10:5 + | +LL | / if true { +LL | | println!("Hello World!"); + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider moving the start statements out like this + | +LL | println!("Hello World!"); +LL | if true { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:19:5 + | +LL | / if x == 0 { +LL | | let y = 9; +LL | | println!("The value y was set to: `{}`", y); +LL | | let _z = y; + | |___________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let y = 9; +LL | println!("The value y was set to: `{}`", y); +LL | let _z = y; +LL | if x == 0 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:40:5 + | +LL | / let _ = if x == 7 { +LL | | let y = 16; + | |___________________^ + | +help: consider moving the start statements out like this + | +LL | let y = 16; +LL | let _ = if x == 7 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:58:5 + | +LL | / if x == 10 { +LL | | let used_value_name = "Different type"; +LL | | println!("Str: {}", used_value_name); + | |_____________________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let used_value_name = "Different type"; +LL | println!("Str: {}", used_value_name); +LL | if x == 10 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:72:5 + | +LL | / if x == 11 { +LL | | let can_be_overridden = "Move me"; +LL | | println!("I'm also moveable"); + | |______________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let can_be_overridden = "Move me"; +LL | println!("I'm also moveable"); +LL | if x == 11 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:88:5 + | +LL | / if x == 2020 { +LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + | |________________________________________________________________^ + | +help: consider moving the start statements out like this + | +LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); +LL | if x == 2020 { + | + +error: this `if` has identical blocks + --> $DIR/shared_at_top.rs:96:18 + | +LL | if x == 2019 { + | __________________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/shared_at_top.rs:98:12 + | +LL | } else { + | ____________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } + | |_____^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs new file mode 100644 index 00000000000..deefdad32c9 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -0,0 +1,119 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// branches_sharing_code at the top and bottom of the if blocks + +struct DataPack { + id: u32, + name: String, + some_data: Vec, +} + +fn overlapping_eq_regions() { + let x = 9; + + // Overlap with separator + if x == 7 { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } else { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + println!("Overlap separator"); + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } + + // Overlap with separator + if x == 99 { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } else { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } +} + +fn complexer_example() { + fn gen_id(x: u32, y: u32) -> u32 { + let x = x & 0x0000_ffff; + let y = (y & 0xffff_0000) << 16; + x | y + } + + fn process_data(data: DataPack) { + let _ = data; + } + + let x = 8; + let y = 9; + if (x > 7 && y < 13) || (x + y) % 2 == 1 { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("From the a `{}` to the b `{}`", a, b); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } else { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("The new ID is '{}'", e_id); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } +} + +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + let _ = 19; + + let _splitter = 6; + + x << 2 + } else { + let _ = 19; + + x << 2 + }; + + if x == 9 { + let _ = 17; + + let _splitter = 6; + + x * 4 + } else { + let _ = 17; + + x * 4 + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr new file mode 100644 index 00000000000..212cfb2f1d1 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -0,0 +1,154 @@ +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:16:5 + | +LL | / if x == 7 { +LL | | let t = 7; +LL | | let _overlap_start = t * 2; +LL | | let _overlap_end = 2 * t; + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top_and_bottom.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:28:5 + | +LL | / let _u = 9; +LL | | } + | |_____^ +help: consider moving the start statements out like this + | +LL | let t = 7; +LL | let _overlap_start = t * 2; +LL | let _overlap_end = 2 * t; +LL | if x == 7 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let _u = 9; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:32:5 + | +LL | / if x == 99 { +LL | | let r = 7; +LL | | let _overlap_start = r; +LL | | let _overlap_middle = r * r; + | |____________________________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:43:5 + | +LL | / let _overlap_end = r * r * r; +LL | | let z = "end"; +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let r = 7; +LL | let _overlap_start = r; +LL | let _overlap_middle = r * r; +LL | if x == 99 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let _overlap_end = r * r * r; +LL | let z = "end"; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:61:5 + | +LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { +LL | | let a = 0xcafe; +LL | | let b = 0xffff00ff; +LL | | let e_id = gen_id(a, b); + | |________________________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:81:5 + | +LL | / let pack = DataPack { +LL | | id: e_id, +LL | | name: "Player 1".to_string(), +LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | | }; +LL | | process_data(pack); +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let a = 0xcafe; +LL | let b = 0xffff00ff; +LL | let e_id = gen_id(a, b); +LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let pack = DataPack { +LL | id: e_id, +LL | name: "Player 1".to_string(), +LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | }; + ... + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:94:5 + | +LL | / let _ = if x == 7 { +LL | | let _ = 19; + | |___________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:103:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the start statements out like this + | +LL | let _ = 19; +LL | let _ = if x == 7 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:106:5 + | +LL | / if x == 9 { +LL | | let _ = 17; + | |___________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:115:5 + | +LL | / x * 4 +LL | | } + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the start statements out like this + | +LL | let _ = 17; +LL | if x == 9 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs new file mode 100644 index 00000000000..0c70e3748ec --- /dev/null +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -0,0 +1,155 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests valid if blocks that shouldn't trigger the lint + +// Tests with value references are includes in "shared_code_at_bottom.rs" + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + let y = 1 << 5; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 1"); + } else { + let y = 1 << 7; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 2"); + } + + // No else + if x == 2 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Howdy stranger =^.^="); + + println!("Bye Bye World"); + } else if x == 9 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Hello reviewer :D"); + + println!("Bye Bye World"); + } + + // Overlapping statements only in else if blocks -> Don't lint + if x == 0 { + println!("I'm important!") + } else if x == 17 { + println!("I share code in else if"); + + println!("x is 17"); + } else { + println!("I share code in else if"); + + println!("x is nether x nor 17"); + } + + // Mutability is different + if x == 13 { + let mut y = 9; + println!("Value y is: {}", y); + y += 16; + let _z1 = y; + } else { + let y = 9; + println!("Value y is: {}", y); + let _z2 = y; + } + + // Same blocks but at start and bottom so no `if_same_then_else` lint + if x == 418 { + let y = 9; + let z = 8; + let _ = (x, y, z); + // Don't tell the programmer, my code is also in the else block + } else if x == 419 { + println!("+-----------+"); + println!("| |"); + println!("| O O |"); + println!("| ° |"); + println!("| \\_____/ |"); + println!("| |"); + println!("+-----------+"); + } else { + let y = 9; + let z = 8; + let _ = (x, y, z); + // I'm so much better than the x == 418 block. Trust me + } + + let x = 1; + if true { + println!("{}", x); + } else { + let x = 2; + println!("{}", x); + } + + // Let's test empty blocks + if false { + } else { + } +} + +/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint +fn trigger_other_lint() { + let x = 0; + let y = 1; + + // Same block + if x == 0 { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } else { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } + + // Only same expression + let _ = if x == 6 { 7 } else { 7 }; + + // Same in else if block + let _ = if x == 67 { + println!("Well I'm the most important block"); + "I'm a pretty string" + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + + if y == 90 { "=^.^=" } else { ":D" } + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + + if y == 90 { "=^.^=" } else { ":D" } + }; + + if x == 0 { + println!("I'm single"); + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr new file mode 100644 index 00000000000..a815995e717 --- /dev/null +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -0,0 +1,101 @@ +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:104:14 + | +LL | if false { + | ______________^ +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/valid_if_blocks.rs:105:12 + | +LL | } else { + | ____________^ +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:115:15 + | +LL | if x == 0 { + | _______________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:119:12 + | +LL | } else { + | ____________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:126:23 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:126:34 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:132:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | +LL | | if y == 90 { "=^.^=" } else { ":D" } +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:137:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | +LL | | if y == 90 { "=^.^=" } else { ":D" } +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:146:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:149:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index bb5b6f5ec04..ec082c73b44 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 99e8fb95466..043ea4148dc 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] fn test_nested() { fn nested() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 0459100d88a..8f23fb28827 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] macro_rules! m { ($a:expr) => { diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index de89f806c58..43468872db0 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -3,7 +3,7 @@ #![allow(clippy::never_loop)] #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::shared_code_in_if_blocks)] +#![allow(clippy::branches_sharing_code)] mod basic_expr { fn test() { diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 35a2e139c11..ef956745500 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -6,7 +6,7 @@ clippy::no_effect, clippy::unused_unit, clippy::zero_divided_by_zero, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] struct Foo { diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 8164b125570..e4dc5b647df 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -6,7 +6,7 @@ clippy::ifs_same_cond, clippy::needless_return, clippy::single_element_loop, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 9fd3f875a5f..2d8f3c2f0e7 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -3,7 +3,7 @@ unused_assignments, clippy::similar_names, clippy::blacklisted_name, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] #![warn(clippy::useless_let_if_seq)] diff --git a/tests/ui/needless_bool/simple.rs b/tests/ui/needless_bool/simple.rs index 678040fa98b..588bb88f446 100644 --- a/tests/ui/needless_bool/simple.rs +++ b/tests/ui/needless_bool/simple.rs @@ -5,7 +5,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::needless_return, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] fn main() { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ebf74cfef12..82d95cc041f 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -4,7 +4,7 @@ #![allow( clippy::if_same_then_else, clippy::single_match, - clippy::shared_code_in_if_blocks, + clippy::branches_sharing_code, clippy::needless_bool )] #![warn(clippy::needless_return)] diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 2bebccdabca..8a471f802e1 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -4,7 +4,7 @@ #![allow( clippy::if_same_then_else, clippy::single_match, - clippy::shared_code_in_if_blocks, + clippy::branches_sharing_code, clippy::needless_bool )] #![warn(clippy::needless_return)] diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs deleted file mode 100644 index 6ff362cb752..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ /dev/null @@ -1,209 +0,0 @@ -#![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// This tests the shared_code_in_if_blocks lint at the end of blocks - -fn simple_examples() { - let x = 1; - - let _ = if x == 7 { - println!("Branch I"); - let start_value = 0; - println!("=^.^="); - - // Same but not moveable due to `start_value` - let _ = start_value; - - // The rest is self contained and moveable => Only lint the rest - let result = false; - println!("Block end!"); - result - } else { - println!("Branch II"); - let start_value = 8; - println!("xD"); - - // Same but not moveable due to `start_value` - let _ = start_value; - - // The rest is self contained and moveable => Only lint the rest - let result = false; - println!("Block end!"); - result - }; - - // Else if block - if x == 9 { - println!("The index is: 6"); - - println!("Same end of block"); - } else if x == 8 { - println!("The index is: 4"); - - // We should only get a lint trigger for the last statement - println!("This is also eq with the else block"); - println!("Same end of block"); - } else { - println!("This is also eq with the else block"); - println!("Same end of block"); - } - - // Use of outer scope value - let outer_scope_value = "I'm outside the if block"; - if x < 99 { - let z = "How are you"; - println!("I'm a local because I use the value `z`: `{}`", z); - - println!( - "I'm moveable because I know: `outer_scope_value`: '{}'", - outer_scope_value - ); - } else { - let z = 45678000; - println!("I'm a local because I use the value `z`: `{}`", z); - - println!( - "I'm moveable because I know: `outer_scope_value`: '{}'", - outer_scope_value - ); - } - - if x == 9 { - if x == 8 { - // No parent!! - println!("---"); - println!("Hello World"); - } else { - println!("Hello World"); - } - } -} - -/// Simple examples where the move can cause some problems due to moved values -fn simple_but_suggestion_is_invalid() { - let x = 16; - - // Local value - let later_used_value = 17; - if x == 9 { - let _ = 9; - let later_used_value = "A string value"; - println!("{}", later_used_value); - } else { - let later_used_value = "A string value"; - println!("{}", later_used_value); - // I'm expecting a note about this - } - println!("{}", later_used_value); - - // outer function - if x == 78 { - let simple_examples = "I now identify as a &str :)"; - println!("This is the new simple_example: {}", simple_examples); - } else { - println!("Separator print statement"); - - let simple_examples = "I now identify as a &str :)"; - println!("This is the new simple_example: {}", simple_examples); - } - simple_examples(); -} - -/// Tests where the blocks are not linted due to the used value scope -fn not_moveable_due_to_value_scope() { - let x = 18; - - // Using a local value in the moved code - if x == 9 { - let y = 18; - println!("y is: `{}`", y); - } else { - let y = "A string"; - println!("y is: `{}`", y); - } - - // Using a local value in the expression - let _ = if x == 0 { - let mut result = x + 1; - - println!("1. Doing some calculations"); - println!("2. Some more calculations"); - println!("3. Setting result"); - - result - } else { - let mut result = x - 1; - - println!("1. Doing some calculations"); - println!("2. Some more calculations"); - println!("3. Setting result"); - - result - }; - - let _ = if x == 7 { - let z1 = 100; - println!("z1: {}", z1); - - let z2 = z1; - println!("z2: {}", z2); - - z2 - } else { - let z1 = 300; - println!("z1: {}", z1); - - let z2 = z1; - println!("z2: {}", z2); - - z2 - }; -} - -/// This should add a note to the lint msg since the moved expression is not `()` -fn added_note_for_expression_use() -> u32 { - let x = 9; - - let _ = if x == 7 { - x << 2 - } else { - let _ = 6; - x << 2 - }; - - if x == 9 { - x * 4 - } else { - let _ = 17; - x * 4 - } -} - -#[rustfmt::skip] -fn test_suggestion_with_weird_formatting() { - let x = 9; - let mut a = 0; - let mut b = 0; - - // The error message still looks weird tbh but this is the best I can do - // for weird formatting - if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } -} - -fn fp_test() { - let x = 17; - - if x == 18 { - let y = 19; - if y < x { - println!("Trigger") - } - } else { - let z = 166; - if z < x { - println!("Trigger") - } - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr deleted file mode 100644 index 2268d8f5365..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:30:5 - | -LL | / let result = false; -LL | | println!("Block end!"); -LL | | result -LL | | }; - | |_____^ - | -note: the lint level is defined here - --> $DIR/shared_at_bot.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this - | -LL | } -LL | let result = false; -LL | println!("Block end!"); -LL | result; - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:48:5 - | -LL | / println!("Same end of block"); -LL | | } - | |_____^ - | -help: consider moving the end statements out like this - | -LL | } -LL | println!("Same end of block"); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:65:5 - | -LL | / println!( -LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", -LL | | outer_scope_value -LL | | ); -LL | | } - | |_____^ - | -help: consider moving the end statements out like this - | -LL | } -LL | println!( -LL | "I'm moveable because I know: `outer_scope_value`: '{}'", -LL | outer_scope_value -LL | ); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:77:9 - | -LL | / println!("Hello World"); -LL | | } - | |_________^ - | -help: consider moving the end statements out like this - | -LL | } -LL | println!("Hello World"); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:93:5 - | -LL | / let later_used_value = "A string value"; -LL | | println!("{}", later_used_value); -LL | | // I'm expecting a note about this -LL | | } - | |_____^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this - | -LL | } -LL | let later_used_value = "A string value"; -LL | println!("{}", later_used_value); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:106:5 - | -LL | / let simple_examples = "I now identify as a &str :)"; -LL | | println!("This is the new simple_example: {}", simple_examples); -LL | | } - | |_____^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this - | -LL | } -LL | let simple_examples = "I now identify as a &str :)"; -LL | println!("This is the new simple_example: {}", simple_examples); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:171:5 - | -LL | / x << 2 -LL | | }; - | |_____^ - | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this - | -LL | } -LL | x << 2; - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:178:5 - | -LL | / x * 4 -LL | | } - | |_____^ - | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this - | -LL | } -LL | x * 4 - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:190:44 - | -LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } - | ^^^^^^^^^^^ - | -help: consider moving the end statements out like this - | -LL | if x == 17 { b = 1; a = 0x99; } else { } -LL | a = 0x99; - | - -error: aborting due to 9 previous errors - diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs deleted file mode 100644 index 496939f2a5e..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![allow(dead_code, clippy::eval_order_dependence)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// This tests the shared_code_in_if_blocks lint at the start of blocks - -fn simple_examples() { - let x = 0; - - // Simple - if true { - println!("Hello World!"); - println!("I'm branch nr: 1"); - } else { - println!("Hello World!"); - println!("I'm branch nr: 2"); - } - - // Else if - if x == 0 { - let y = 9; - println!("The value y was set to: `{}`", y); - let _z = y; - - println!("I'm the true start index of arrays"); - } else if x == 1 { - let y = 9; - println!("The value y was set to: `{}`", y); - let _z = y; - - println!("I start counting from 1 so my array starts from `1`"); - } else { - let y = 9; - println!("The value y was set to: `{}`", y); - let _z = y; - - println!("Ha, Pascal allows you to start the array where you want") - } - - // Return a value - let _ = if x == 7 { - let y = 16; - println!("What can I say except: \"you're welcome?\""); - let _ = y; - x - } else { - let y = 16; - println!("Thank you"); - y - }; -} - -/// Simple examples where the move can cause some problems due to moved values -fn simple_but_suggestion_is_invalid() { - let x = 10; - - // Can't be automatically moved because used_value_name is getting used again - let used_value_name = 19; - if x == 10 { - let used_value_name = "Different type"; - println!("Str: {}", used_value_name); - let _ = 1; - } else { - let used_value_name = "Different type"; - println!("Str: {}", used_value_name); - let _ = 2; - } - let _ = used_value_name; - - // This can be automatically moved as `can_be_overridden` is not used again - let can_be_overridden = 8; - let _ = can_be_overridden; - if x == 11 { - let can_be_overridden = "Move me"; - println!("I'm also moveable"); - let _ = 111; - } else { - let can_be_overridden = "Move me"; - println!("I'm also moveable"); - let _ = 222; - } -} - -/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. -fn check_if_same_than_else_mask() { - let x = 2021; - - #[allow(clippy::if_same_then_else)] - if x == 2020 { - println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); - println!("Because `IF_SAME_THEN_ELSE` is allowed here"); - } else { - println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); - println!("Because `IF_SAME_THEN_ELSE` is allowed here"); - } - - if x == 2019 { - println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); - } else { - println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr deleted file mode 100644 index 76898a6ff02..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ /dev/null @@ -1,121 +0,0 @@ -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:10:5 - | -LL | / if true { -LL | | println!("Hello World!"); - | |_________________________________^ - | -note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider moving the start statements out like this - | -LL | println!("Hello World!"); -LL | if true { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:19:5 - | -LL | / if x == 0 { -LL | | let y = 9; -LL | | println!("The value y was set to: `{}`", y); -LL | | let _z = y; - | |___________________^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let y = 9; -LL | println!("The value y was set to: `{}`", y); -LL | let _z = y; -LL | if x == 0 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:40:5 - | -LL | / let _ = if x == 7 { -LL | | let y = 16; - | |___________________^ - | -help: consider moving the start statements out like this - | -LL | let y = 16; -LL | let _ = if x == 7 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:58:5 - | -LL | / if x == 10 { -LL | | let used_value_name = "Different type"; -LL | | println!("Str: {}", used_value_name); - | |_____________________________________________^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let used_value_name = "Different type"; -LL | println!("Str: {}", used_value_name); -LL | if x == 10 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:72:5 - | -LL | / if x == 11 { -LL | | let can_be_overridden = "Move me"; -LL | | println!("I'm also moveable"); - | |______________________________________^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let can_be_overridden = "Move me"; -LL | println!("I'm also moveable"); -LL | if x == 11 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:88:5 - | -LL | / if x == 2020 { -LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); -LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); - | |________________________________________________________________^ - | -help: consider moving the start statements out like this - | -LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); -LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); -LL | if x == 2020 { - | - -error: this `if` has identical blocks - --> $DIR/shared_at_top.rs:96:18 - | -LL | if x == 2019 { - | __________________^ -LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); -LL | | } else { - | |_____^ - | -note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:9 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: same as this - --> $DIR/shared_at_top.rs:98:12 - | -LL | } else { - | ____________^ -LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); -LL | | } - | |_____^ - -error: aborting due to 7 previous errors - diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs deleted file mode 100644 index 46a8f931aaf..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs +++ /dev/null @@ -1,119 +0,0 @@ -#![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// shared_code_in_if_blocks at the top and bottom of the if blocks - -struct DataPack { - id: u32, - name: String, - some_data: Vec, -} - -fn overlapping_eq_regions() { - let x = 9; - - // Overlap with separator - if x == 7 { - let t = 7; - let _overlap_start = t * 2; - let _overlap_end = 2 * t; - let _u = 9; - } else { - let t = 7; - let _overlap_start = t * 2; - let _overlap_end = 2 * t; - println!("Overlap separator"); - let _overlap_start = t * 2; - let _overlap_end = 2 * t; - let _u = 9; - } - - // Overlap with separator - if x == 99 { - let r = 7; - let _overlap_start = r; - let _overlap_middle = r * r; - let _overlap_end = r * r * r; - let z = "end"; - } else { - let r = 7; - let _overlap_start = r; - let _overlap_middle = r * r; - let _overlap_middle = r * r; - let _overlap_end = r * r * r; - let z = "end"; - } -} - -fn complexer_example() { - fn gen_id(x: u32, y: u32) -> u32 { - let x = x & 0x0000_ffff; - let y = (y & 0xffff_0000) << 16; - x | y - } - - fn process_data(data: DataPack) { - let _ = data; - } - - let x = 8; - let y = 9; - if (x > 7 && y < 13) || (x + y) % 2 == 1 { - let a = 0xcafe; - let b = 0xffff00ff; - let e_id = gen_id(a, b); - - println!("From the a `{}` to the b `{}`", a, b); - - let pack = DataPack { - id: e_id, - name: "Player 1".to_string(), - some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], - }; - process_data(pack); - } else { - let a = 0xcafe; - let b = 0xffff00ff; - let e_id = gen_id(a, b); - - println!("The new ID is '{}'", e_id); - - let pack = DataPack { - id: e_id, - name: "Player 1".to_string(), - some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], - }; - process_data(pack); - } -} - -/// This should add a note to the lint msg since the moved expression is not `()` -fn added_note_for_expression_use() -> u32 { - let x = 9; - - let _ = if x == 7 { - let _ = 19; - - let _splitter = 6; - - x << 2 - } else { - let _ = 19; - - x << 2 - }; - - if x == 9 { - let _ = 17; - - let _splitter = 6; - - x * 4 - } else { - let _ = 17; - - x * 4 - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr deleted file mode 100644 index 75c3d397f2e..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr +++ /dev/null @@ -1,154 +0,0 @@ -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:16:5 - | -LL | / if x == 7 { -LL | | let t = 7; -LL | | let _overlap_start = t * 2; -LL | | let _overlap_end = 2 * t; - | |_________________________________^ - | -note: the lint level is defined here - --> $DIR/shared_at_top_and_bot.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:28:5 - | -LL | / let _u = 9; -LL | | } - | |_____^ -help: consider moving the start statements out like this - | -LL | let t = 7; -LL | let _overlap_start = t * 2; -LL | let _overlap_end = 2 * t; -LL | if x == 7 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | let _u = 9; - | - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:32:5 - | -LL | / if x == 99 { -LL | | let r = 7; -LL | | let _overlap_start = r; -LL | | let _overlap_middle = r * r; - | |____________________________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:43:5 - | -LL | / let _overlap_end = r * r * r; -LL | | let z = "end"; -LL | | } - | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let r = 7; -LL | let _overlap_start = r; -LL | let _overlap_middle = r * r; -LL | if x == 99 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | let _overlap_end = r * r * r; -LL | let z = "end"; - | - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:61:5 - | -LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { -LL | | let a = 0xcafe; -LL | | let b = 0xffff00ff; -LL | | let e_id = gen_id(a, b); - | |________________________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:81:5 - | -LL | / let pack = DataPack { -LL | | id: e_id, -LL | | name: "Player 1".to_string(), -LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], -LL | | }; -LL | | process_data(pack); -LL | | } - | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let a = 0xcafe; -LL | let b = 0xffff00ff; -LL | let e_id = gen_id(a, b); -LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | let pack = DataPack { -LL | id: e_id, -LL | name: "Player 1".to_string(), -LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], -LL | }; - ... - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:94:5 - | -LL | / let _ = if x == 7 { -LL | | let _ = 19; - | |___________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:103:5 - | -LL | / x << 2 -LL | | }; - | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this - | -LL | let _ = 19; -LL | let _ = if x == 7 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | x << 2; - | - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:106:5 - | -LL | / if x == 9 { -LL | | let _ = 17; - | |___________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:115:5 - | -LL | / x * 4 -LL | | } - | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this - | -LL | let _ = 17; -LL | if x == 9 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | x * 4 - | - -error: aborting due to 5 previous errors - diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs deleted file mode 100644 index e63490d5b07..00000000000 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ /dev/null @@ -1,155 +0,0 @@ -#![allow(dead_code, clippy::eval_order_dependence)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// This tests the shared_code_in_if_blocks lint at the start of blocks - -// Tests with value references are includes in "shared_code_at_bot.rs" - -fn valid_examples() { - let x = 2; - - // The edge statements are different - if x == 9 { - let y = 1 << 5; - - println!("This is the same: vvv"); - let _z = y; - println!("The block expression is different"); - - println!("Different end 1"); - } else { - let y = 1 << 7; - - println!("This is the same: vvv"); - let _z = y; - println!("The block expression is different"); - - println!("Different end 2"); - } - - // No else - if x == 2 { - println!("Hello world!"); - println!("Hello back, how are you?"); - - // This is different vvvv - println!("Howdy stranger =^.^="); - - println!("Bye Bye World"); - } else if x == 9 { - println!("Hello world!"); - println!("Hello back, how are you?"); - - // This is different vvvv - println!("Hello reviewer :D"); - - println!("Bye Bye World"); - } - - // Overlapping statements only in else if blocks -> Don't lint - if x == 0 { - println!("I'm important!") - } else if x == 17 { - println!("I share code in else if"); - - println!("x is 17"); - } else { - println!("I share code in else if"); - - println!("x is nether x nor 17"); - } - - // Mutability is different - if x == 13 { - let mut y = 9; - println!("Value y is: {}", y); - y += 16; - let _z1 = y; - } else { - let y = 9; - println!("Value y is: {}", y); - let _z2 = y; - } - - // Same blocks but at start and bottom so no `if_same_then_else` lint - if x == 418 { - let y = 9; - let z = 8; - let _ = (x, y, z); - // Don't tell the programmer, my code is also in the else block - } else if x == 419 { - println!("+-----------+"); - println!("| |"); - println!("| O O |"); - println!("| ° |"); - println!("| \\_____/ |"); - println!("| |"); - println!("+-----------+"); - } else { - let y = 9; - let z = 8; - let _ = (x, y, z); - // I'm so much better than the x == 418 block. Trust me - } - - let x = 1; - if true { - println!("{}", x); - } else { - let x = 2; - println!("{}", x); - } - - // Let's test empty blocks - if false { - } else { - } -} - -/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint -fn trigger_other_lint() { - let x = 0; - let y = 1; - - // Same block - if x == 0 { - let u = 19; - println!("How are u today?"); - let _ = "This is a string"; - } else { - let u = 19; - println!("How are u today?"); - let _ = "This is a string"; - } - - // Only same expression - let _ = if x == 6 { 7 } else { 7 }; - - // Same in else if block - let _ = if x == 67 { - println!("Well I'm the most important block"); - "I'm a pretty string" - } else if x == 68 { - println!("I'm a doppelgänger"); - // Don't listen to my clone below - - if y == 90 { "=^.^=" } else { ":D" } - } else { - // Don't listen to my clone above - println!("I'm a doppelgänger"); - - if y == 90 { "=^.^=" } else { ":D" } - }; - - if x == 0 { - println!("I'm single"); - } else if x == 68 { - println!("I'm a doppelgänger"); - // Don't listen to my clone below - } else { - // Don't listen to my clone above - println!("I'm a doppelgänger"); - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr deleted file mode 100644 index 846581456dc..00000000000 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ /dev/null @@ -1,101 +0,0 @@ -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:104:14 - | -LL | if false { - | ______________^ -LL | | } else { - | |_____^ - | -note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:9 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: same as this - --> $DIR/valid_if_blocks.rs:105:12 - | -LL | } else { - | ____________^ -LL | | } - | |_____^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:115:15 - | -LL | if x == 0 { - | _______________^ -LL | | let u = 19; -LL | | println!("How are u today?"); -LL | | let _ = "This is a string"; -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:119:12 - | -LL | } else { - | ____________^ -LL | | let u = 19; -LL | | println!("How are u today?"); -LL | | let _ = "This is a string"; -LL | | } - | |_____^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:126:23 - | -LL | let _ = if x == 6 { 7 } else { 7 }; - | ^^^^^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:126:34 - | -LL | let _ = if x == 6 { 7 } else { 7 }; - | ^^^^^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:132:23 - | -LL | } else if x == 68 { - | _______________________^ -LL | | println!("I'm a doppelgänger"); -LL | | // Don't listen to my clone below -LL | | -LL | | if y == 90 { "=^.^=" } else { ":D" } -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:137:12 - | -LL | } else { - | ____________^ -LL | | // Don't listen to my clone above -LL | | println!("I'm a doppelgänger"); -LL | | -LL | | if y == 90 { "=^.^=" } else { ":D" } -LL | | }; - | |_____^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:146:23 - | -LL | } else if x == 68 { - | _______________________^ -LL | | println!("I'm a doppelgänger"); -LL | | // Don't listen to my clone below -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:149:12 - | -LL | } else { - | ____________^ -LL | | // Don't listen to my clone above -LL | | println!("I'm a doppelgänger"); -LL | | } - | |_____^ - -error: aborting due to 5 previous errors - -- cgit 1.4.1-3-g733a5 From 1573d10325f286ffcbba290d27984ded9538a5c3 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Apr 2021 06:59:10 +0200 Subject: tabs_in_doc_comments: Fix ICE due to char indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a quick-fix for an ICE in `tabs_in_doc_comments`. The problem was that we we're indexing into possibly multi-byte characters, such as '位'. More specifically `get_chunks_of_tabs` was returning indices into multi-byte characters. Those were passed on to a `Span` creation that then caused the ICE. This fix makes sure that we don't return indices that point inside a multi-byte character. *However*, we are still iterating over unicode codepoints, not grapheme clusters. So a seemingly single character like y̆ , which actually consists of two codepoints, will probably still cause incorrect spans in the output. --- clippy_lints/src/tabs_in_doc_comments.rs | 28 +++++++++++++++++----------- tests/ui/crashes/ice-5835.rs | 8 ++++++++ tests/ui/crashes/ice-5835.stderr | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 tests/ui/crashes/ice-5835.rs create mode 100644 tests/ui/crashes/ice-5835.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 88bd2feaadd..3f9692540f7 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -104,30 +104,29 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { // tracker to decide if the last group of tabs is not closed by a non-tab character let mut is_active = false; - let chars_array: Vec<_> = the_str.chars().collect(); + let char_indices: Vec<_> = the_str.char_indices().collect(); - if chars_array == vec!['\t'] { + if char_indices.len() == 1 && char_indices.first().unwrap().1 == '\t' { return vec![(0, 1)]; } - for (index, arr) in chars_array.windows(2).enumerate() { - let index = u32::try_from(index).expect(line_length_way_to_long); - match arr { - ['\t', '\t'] => { + for entry in char_indices.windows(2) { + match entry { + [(_, '\t'), (_, '\t')] => { // either string starts with double tab, then we have to set it active, // otherwise is_active is true anyway is_active = true; }, - [_, '\t'] => { + [(_, _), (index_b, '\t')] => { // as ['\t', '\t'] is excluded, this has to be a start of a tab group, // set indices accordingly is_active = true; - current_start = index + 1; + current_start = *index_b as u32; }, - ['\t', _] => { + [(_, '\t'), (index_b, _)] => { // this now has to be an end of the group, hence we have to push a new tuple is_active = false; - spans.push((current_start, index + 1)); + spans.push((current_start, *index_b as u32)); }, _ => {}, } @@ -137,7 +136,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { if is_active { spans.push(( current_start, - u32::try_from(the_str.chars().count()).expect(line_length_way_to_long), + u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long), )); } @@ -148,6 +147,13 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { mod tests_for_get_chunks_of_tabs { use super::get_chunks_of_tabs; + #[test] + fn test_unicode_han_string() { + let res = get_chunks_of_tabs(" 位\t"); + + assert_eq!(res, vec![(4, 5)]); + } + #[test] fn test_empty_string() { let res = get_chunks_of_tabs(""); diff --git a/tests/ui/crashes/ice-5835.rs b/tests/ui/crashes/ice-5835.rs new file mode 100644 index 00000000000..209a5b1eb09 --- /dev/null +++ b/tests/ui/crashes/ice-5835.rs @@ -0,0 +1,8 @@ +#![rustfmt::skip] + +pub struct Foo { + /// 位 + pub bar: u8, +} + +fn main() {} diff --git a/tests/ui/crashes/ice-5835.stderr b/tests/ui/crashes/ice-5835.stderr new file mode 100644 index 00000000000..e286bc580ad --- /dev/null +++ b/tests/ui/crashes/ice-5835.stderr @@ -0,0 +1,20 @@ +error[E0658]: custom inner attributes are unstable + --> $DIR/ice-5835.rs:1:4 + | +LL | #![rustfmt::skip] + | ^^^^^^^^^^^^^ + | + = note: see issue #54726 for more information + = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable + +error: using tabs in doc comments is not recommended + --> $DIR/ice-5835.rs:4:10 + | +LL | /// 位 + | ^^^^ help: consider using four spaces per tab + | + = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From 8a50923da4884c30e96fce2b4408c96f5824db43 Mon Sep 17 00:00:00 2001 From: Horaci Macias Date: Mon, 5 Apr 2021 13:27:39 +0200 Subject: consider mutability on useless_vec suggestions https://github.com/rust-lang/rust-clippy/issues/7035 --- clippy_lints/src/vec.rs | 54 ++++++++++++++++++++++++++++++++++++++----------- tests/ui/vec.fixed | 16 +++++++++++++++ tests/ui/vec.rs | 16 +++++++++++++++ tests/ui/vec.stderr | 44 +++++++++++++++++++++++++++++++++------- 4 files changed, 111 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 1af9583887f..bc2eb88114e 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - self.check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, mutability, expr.span); } } @@ -70,14 +70,20 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - self.check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, Mutability::Not, span); } } } } impl UselessVec { - fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + fn check_vec_macro<'tcx>( + self, + cx: &LateContext<'tcx>, + vec_args: &higher::VecArgs<'tcx>, + mutability: Mutability, + span: Span, + ) { let mut applicability = Applicability::MachineApplicable; let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { @@ -87,11 +93,22 @@ impl UselessVec { return; } - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) + match mutability { + Mutability::Mut => { + format!( + "&mut [{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + }, + Mutability::Not => { + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + }, + } } else { return; } @@ -104,9 +121,22 @@ impl UselessVec { } let span = args[0].span.to(last.span); - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + match mutability { + Mutability::Mut => { + format!( + "&mut [{}]", + snippet_with_applicability(cx, span, "..", &mut applicability) + ) + }, + Mutability::Not => { + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + }, + } } else { - "&[]".into() + match mutability { + Mutability::Mut => "&mut []".into(), + Mutability::Not => "&[]".into(), + } } }, }; diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 85677159620..da35f2e5c1b 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -6,9 +6,14 @@ struct NonCopy; fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + #[allow(clippy::ptr_arg)] fn on_vec(_: &Vec) {} +fn on_mut_vec(_: &mut Vec) {} + struct Line { length: usize, } @@ -22,28 +27,38 @@ impl Line { fn main() { on_slice(&[]); on_slice(&[]); + on_mut_slice(&mut []); on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); #[rustfmt::skip] on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); on_slice(&[1; 2]); on_slice(&[1; 2]); + on_mut_slice(&mut [1; 2]); on_vec(&vec![]); on_vec(&vec![1, 2]); on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); // Now with non-constant expressions let line = Line { length: 2 }; on_slice(&vec![2; line.length]); on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); for a in &[1, 2, 3] { println!("{:?}", a); @@ -54,6 +69,7 @@ fn main() { } on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` // Ok for a in vec![1; 201] { diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 03b8ee81665..e9ed83e5c5a 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -6,9 +6,14 @@ struct NonCopy; fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + #[allow(clippy::ptr_arg)] fn on_vec(_: &Vec) {} +fn on_mut_vec(_: &mut Vec) {} + struct Line { length: usize, } @@ -22,28 +27,38 @@ impl Line { fn main() { on_slice(&vec![]); on_slice(&[]); + on_mut_slice(&mut vec![]); on_slice(&vec![1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); on_slice(&vec![1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); #[rustfmt::skip] on_slice(&vec!(1, 2)); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); on_slice(&vec![1; 2]); on_slice(&[1; 2]); + on_mut_slice(&mut vec![1; 2]); on_vec(&vec![]); on_vec(&vec![1, 2]); on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); // Now with non-constant expressions let line = Line { length: 2 }; on_slice(&vec![2; line.length]); on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); for a in vec![1, 2, 3] { println!("{:?}", a); @@ -54,6 +69,7 @@ fn main() { } on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` // Ok for a in vec![1; 201] { diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index 37e28ebddb5..7d1de05a5c8 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -1,5 +1,5 @@ error: useless use of `vec!` - --> $DIR/vec.rs:23:14 + --> $DIR/vec.rs:28:14 | LL | on_slice(&vec![]); | ^^^^^^^ help: you can use a slice directly: `&[]` @@ -7,34 +7,64 @@ LL | on_slice(&vec![]); = note: `-D clippy::useless-vec` implied by `-D warnings` error: useless use of `vec!` - --> $DIR/vec.rs:26:14 + --> $DIR/vec.rs:30:18 + | +LL | on_mut_slice(&mut vec![]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&mut []` + +error: useless use of `vec!` + --> $DIR/vec.rs:32:14 | LL | on_slice(&vec![1, 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:29:14 + --> $DIR/vec.rs:34:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:36:14 | LL | on_slice(&vec![1, 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:32:14 + --> $DIR/vec.rs:38:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:40:14 | LL | on_slice(&vec!(1, 2)); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:35:14 + --> $DIR/vec.rs:42:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:44:14 | LL | on_slice(&vec![1; 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` error: useless use of `vec!` - --> $DIR/vec.rs:48:14 + --> $DIR/vec.rs:46:18 + | +LL | on_mut_slice(&mut vec![1; 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:63:14 | LL | for a in vec![1, 2, 3] { | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 12fce557669a0de230399cf8e6eee4f5307bf87b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 17:35:32 -0400 Subject: Fix all occurences of `needless_borrow` internally --- clippy_dev/src/fmt.rs | 2 +- clippy_dev/src/lib.rs | 16 +- clippy_lints/src/absurd_extreme_comparisons.rs | 4 +- clippy_lints/src/assertions_on_constants.rs | 12 +- clippy_lints/src/atomic_ordering.rs | 6 +- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/await_holding_invalid.rs | 2 +- clippy_lints/src/bytecount.rs | 14 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 6 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/checked_conversions.rs | 16 +- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/comparison_chain.rs | 6 +- clippy_lints/src/copies.rs | 6 +- clippy_lints/src/create_dir.rs | 2 +- clippy_lints/src/default.rs | 10 +- clippy_lints/src/derive.rs | 4 +- clippy_lints/src/doc.rs | 4 +- clippy_lints/src/double_comparison.rs | 4 +- clippy_lints/src/double_parens.rs | 6 +- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/duration_subsec.rs | 4 +- clippy_lints/src/entry.rs | 16 +- clippy_lints/src/eq_op.rs | 14 +- clippy_lints/src/erasing_op.rs | 2 +- clippy_lints/src/eta_reduction.rs | 18 +- clippy_lints/src/eval_order_dependence.rs | 18 +- clippy_lints/src/exit.rs | 2 +- clippy_lints/src/explicit_write.rs | 10 +- clippy_lints/src/fallible_impl_from.rs | 4 +- clippy_lints/src/float_equality_without_abs.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 42 +- clippy_lints/src/format.rs | 38 +- clippy_lints/src/functions/must_use.rs | 28 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 6 +- clippy_lints/src/functions/result_unit_err.rs | 8 +- clippy_lints/src/functions/too_many_arguments.rs | 2 +- clippy_lints/src/get_last_with_len.rs | 2 +- clippy_lints/src/identity_op.rs | 2 +- clippy_lints/src/if_let_mutex.rs | 6 +- clippy_lints/src/if_let_some_result.rs | 6 +- clippy_lints/src/if_then_some_else_none.rs | 12 +- clippy_lints/src/implicit_hasher.rs | 6 +- clippy_lints/src/implicit_return.rs | 4 +- clippy_lints/src/implicit_saturating_sub.rs | 16 +- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/infinite_iter.rs | 12 +- clippy_lints/src/invalid_upcast_comparisons.rs | 6 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/len_zero.rs | 15 +- clippy_lints/src/let_if_seq.rs | 24 +- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/lib.rs | 2446 ++++++++++---------- clippy_lints/src/lifetimes.rs | 20 +- clippy_lints/src/literal_representation.rs | 4 +- clippy_lints/src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/manual_flatten.rs | 4 +- clippy_lints/src/loops/manual_memcpy.rs | 10 +- clippy_lints/src/loops/mod.rs | 6 +- clippy_lints/src/loops/needless_collect.rs | 24 +- clippy_lints/src/loops/needless_range_loop.rs | 18 +- clippy_lints/src/loops/never_loop.rs | 46 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/loops/single_element_loop.rs | 4 +- clippy_lints/src/loops/utils.rs | 16 +- clippy_lints/src/loops/while_let_loop.rs | 12 +- clippy_lints/src/loops/while_let_on_iterator.rs | 12 +- clippy_lints/src/manual_strip.rs | 4 +- clippy_lints/src/map_clone.rs | 10 +- clippy_lints/src/map_err_ignore.rs | 2 +- clippy_lints/src/map_identity.rs | 12 +- clippy_lints/src/map_unit_fn.rs | 12 +- clippy_lints/src/match_on_vec_items.rs | 4 +- clippy_lints/src/matches.rs | 81 +- clippy_lints/src/mem_discriminant.rs | 4 +- clippy_lints/src/mem_forget.rs | 2 +- clippy_lints/src/mem_replace.rs | 16 +- clippy_lints/src/methods/bind_instead_of_map.rs | 4 +- clippy_lints/src/methods/chars_cmp.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 8 +- clippy_lints/src/methods/filter_map.rs | 6 +- clippy_lints/src/methods/flat_map_identity.rs | 2 +- clippy_lints/src/methods/implicit_clone.rs | 2 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/mod.rs | 22 +- clippy_lints/src/methods/option_as_ref_deref.rs | 6 +- clippy_lints/src/methods/or_fun_call.rs | 6 +- clippy_lints/src/methods/search_is_some.rs | 6 +- clippy_lints/src/methods/uninit_assumed_init.rs | 8 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 14 +- clippy_lints/src/methods/unnecessary_fold.rs | 6 +- clippy_lints/src/methods/utils.rs | 2 +- clippy_lints/src/methods/zst_offset.rs | 2 +- clippy_lints/src/minmax.rs | 4 +- clippy_lints/src/misc.rs | 26 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/mut_key.rs | 6 +- clippy_lints/src/mut_mut.rs | 4 +- clippy_lints/src/mut_reference.rs | 4 +- clippy_lints/src/needless_bool.rs | 18 +- clippy_lints/src/needless_borrow.rs | 2 +- clippy_lints/src/needless_borrowed_ref.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/needless_update.rs | 2 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 4 +- clippy_lints/src/neg_multiply.rs | 6 +- clippy_lints/src/no_effect.rs | 58 +- clippy_lints/src/non_octal_unix_permissions.rs | 4 +- clippy_lints/src/open_options.rs | 4 +- clippy_lints/src/option_if_let_else.rs | 8 +- clippy_lints/src/overflow_check_conditional.rs | 20 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/path_buf_push_overwrite.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 18 +- clippy_lints/src/ptr.rs | 22 +- clippy_lints/src/ptr_eq.rs | 8 +- clippy_lints/src/ptr_offset_with_cast.rs | 6 +- clippy_lints/src/question_mark.rs | 8 +- clippy_lints/src/ranges.rs | 24 +- clippy_lints/src/redundant_clone.rs | 10 +- clippy_lints/src/redundant_closure_call.rs | 16 +- clippy_lints/src/ref_option_ref.rs | 2 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/repeat_once.rs | 4 +- clippy_lints/src/returns.rs | 16 +- clippy_lints/src/semicolon_if_nothing_returned.rs | 2 +- clippy_lints/src/shadow.rs | 69 +- clippy_lints/src/slow_vector_initialization.rs | 42 +- clippy_lints/src/strings.rs | 14 +- clippy_lints/src/suspicious_operation_groupings.rs | 16 +- clippy_lints/src/swap.rs | 30 +- clippy_lints/src/tabs_in_doc_comments.rs | 2 +- clippy_lints/src/to_string_in_display.rs | 2 +- clippy_lints/src/trait_bounds.rs | 4 +- clippy_lints/src/transmute/mod.rs | 2 +- .../src/transmute/transmute_float_to_int.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_lints/src/transmuting_null.rs | 6 +- clippy_lints/src/try_err.rs | 14 +- clippy_lints/src/types/borrowed_box.rs | 8 +- clippy_lints/src/types/mod.rs | 28 +- clippy_lints/src/types/type_complexity.rs | 4 +- clippy_lints/src/types/utils.rs | 2 +- clippy_lints/src/types/vec_box.rs | 4 +- clippy_lints/src/undropped_manually_drops.rs | 2 +- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 4 +- clippy_lints/src/unit_types/unit_arg.rs | 2 +- clippy_lints/src/unit_types/unit_cmp.rs | 4 +- clippy_lints/src/unit_types/utils.rs | 2 +- clippy_lints/src/unnamed_address.rs | 6 +- clippy_lints/src/unnecessary_sort_by.rs | 16 +- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_lints/src/unused_io_amount.rs | 10 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/unwrap.rs | 4 +- clippy_lints/src/use_self.rs | 6 +- clippy_lints/src/useless_conversion.rs | 12 +- clippy_lints/src/utils/author.rs | 96 +- clippy_lints/src/utils/inspector.rs | 90 +- clippy_lints/src/utils/internal_lints.rs | 18 +- clippy_lints/src/vec.rs | 2 +- clippy_lints/src/vec_resize_to_zero.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 2 +- clippy_lints/src/zero_sized_map_values.rs | 2 +- src/driver.rs | 12 +- tests/compile-test.rs | 16 +- tests/ui/borrow_interior_mutable_const/others.rs | 6 +- .../ui/borrow_interior_mutable_const/others.stderr | 28 +- tests/ui/collapsible_match2.rs | 7 +- tests/ui/collapsible_match2.stderr | 20 +- tests/ui/escape_analysis.rs | 4 +- tests/ui/implicit_clone.rs | 2 +- tests/ui/into_iter_on_ref.fixed | 2 +- tests/ui/into_iter_on_ref.rs | 2 +- tests/ui/ref_option_ref.rs | 2 +- tests/ui/ref_option_ref.stderr | 2 +- tests/ui/stable_sort_primitive.fixed | 2 +- tests/ui/stable_sort_primitive.rs | 2 +- 183 files changed, 2150 insertions(+), 2162 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 111c79c332d..1517cdc9419 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -68,7 +68,7 @@ pub fn run(check: bool, verbose: bool) { continue; } - success &= rustfmt(context, &path)?; + success &= rustfmt(context, path)?; } Ok(success) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index a5e94683878..1e5a140e964 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -101,7 +101,7 @@ impl Lint { #[must_use] pub fn gen_lint_group_list<'a>(lints: impl Iterator) -> Vec { lints - .map(|l| format!(" LintId::of(&{}::{}),", l.module, l.name.to_uppercase())) + .map(|l| format!(" LintId::of({}::{}),", l.module, l.name.to_uppercase())) .sorted() .collect::>() } @@ -154,17 +154,17 @@ pub fn gen_register_lint_list<'a>( let header = " store.register_lints(&[".to_string(); let footer = " ]);".to_string(); let internal_lints = internal_lints - .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) .map(|l| { format!( - " #[cfg(feature = \"internal-lints\")]\n &{}::{},", + " #[cfg(feature = \"internal-lints\")]\n {}::{},", l.module, l.name.to_uppercase() ) }); let other_lints = usable_lints - .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) - .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) + .map(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) .sorted(); let mut lint_list = vec![header]; lint_list.extend(internal_lints); @@ -550,9 +550,9 @@ fn test_gen_lint_group_list() { Lint::new("internal", "internal_style", "abc", None, "module_name"), ]; let expected = vec![ - " LintId::of(&module_name::ABC),".to_string(), - " LintId::of(&module_name::INTERNAL),".to_string(), - " LintId::of(&module_name::SHOULD_ASSERT_EQ),".to_string(), + " LintId::of(module_name::ABC),".to_string(), + " LintId::of(module_name::INTERNAL),".to_string(), + " LintId::of(module_name::SHOULD_ASSERT_EQ),".to_string(), ]; assert_eq!(expected, gen_lint_group_list(lints.iter())); } diff --git a/clippy_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/absurd_extreme_comparisons.rs index 33c720c666e..5fbf4bdbd18 100644 --- a/clippy_lints/src/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/absurd_extreme_comparisons.rs @@ -44,7 +44,7 @@ declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { if !expr.span.from_expansion() { let msg = "this comparison involving the minimum or maximum element for this \ @@ -95,7 +95,7 @@ enum AbsurdComparisonResult { } fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + if let ExprKind::Cast(cast_exp, _) = expr.kind { let precast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 16905781c56..a0993bb6913 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { return; } if_chain! { - if let ExprKind::Unary(_, ref lit) = e.kind; + if let ExprKind::Unary(_, lit) = e.kind; if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); if is_true; then { @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { if assert_span.from_expansion() { return; } - if let Some(assert_match) = match_assert_with_message(&cx, e) { + if let Some(assert_match) = match_assert_with_message(cx, e) { match assert_match { // matched assert but not message AssertKind::WithoutMessage(false) => lint_false_without_message(), @@ -113,17 +113,17 @@ enum AssertKind { /// where `message` is any expression and `c` is a constant bool. fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { if_chain! { - if let ExprKind::If(ref cond, ref then, _) = expr.kind; - if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind; + if let ExprKind::If(cond, then, _) = expr.kind; + if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; // bind the first argument of the `assert!` macro if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); // block - if let ExprKind::Block(ref block, _) = then.kind; + if let ExprKind::Block(block, _) = then.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; // inner block is optional. unwrap it if it exists, or use the expression as is otherwise. if let Some(begin_panic_call) = match block_expr.kind { - ExprKind::Block(ref inner_block, _) => &inner_block.expr, + ExprKind::Block(inner_block, _) => &inner_block.expr, _ => &block.expr, }; // function call diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index dfb18199325..7ceb01f5590 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -85,7 +85,7 @@ fn match_ordering_def_path(cx: &LateContext<'_>, did: DefId, orderings: &[&str]) fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); if method == "load" || method == "store"; @@ -120,7 +120,7 @@ fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if ["fence", "compiler_fence"] @@ -152,7 +152,7 @@ fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(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" || method == "fetch_update"; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0ce46e1c214..382fb03d920 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -602,7 +602,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { if let NestedMetaItem::MetaItem(meta) = item { match &meta.kind { MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(&list)); + mismatched.extend(find_mismatched_target_os(list)); }, MetaItemKind::Word => { if_chain! { @@ -629,7 +629,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { then { let mess = "operating system used in target family position"; - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| { + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { // Avoid showing the unix suggestion multiple times in case // we have more than one mismatch for unix-like systems let mut unix_suggested = false; diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 68eee0520b3..1739a57a240 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -101,7 +101,7 @@ impl LateLintPass<'_> for AwaitHolding { let typeck_results = cx.tcx.typeck_body(body_id); check_interior_types( cx, - &typeck_results.generator_interior_types.as_ref().skip_binder(), + typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, ); } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 1546d823167..877ae002d36 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -38,17 +38,17 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]); impl<'tcx> LateLintPass<'tcx> for ByteCount { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref count, _, ref count_args, _) = expr.kind; + if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind; if count.ident.name == sym!(count); if count_args.len() == 1; - if let ExprKind::MethodCall(ref filter, _, ref filter_args, _) = count_args[0].kind; + if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind; if filter.ident.name == sym!(filter); if filter_args.len() == 2; if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind; let body = cx.tcx.hir().body(body_id); if body.params.len() == 1; - if let Some(argname) = get_pat_name(&body.params[0].pat); - if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; + if let Some(argname) = get_pat_name(body.params[0].pat); + if let ExprKind::Binary(ref op, l, r) = body.value.kind; if op.node == BinOpKind::Eq; if match_type(cx, cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { 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, _) = + let haystack = if let ExprKind::MethodCall(path, _, args, _) = filter_args[0].kind { let p = path.ident.name; if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { @@ -98,10 +98,10 @@ fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { fn get_path_name(expr: &Expr<'_>) -> Option { match expr.kind { - ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::Deref, ref e) => { + ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => { get_path_name(e) }, - ExprKind::Block(ref b, _) => { + ExprKind::Block(b, _) => { if b.stmts.is_empty() { b.expr.as_ref().and_then(|p| get_path_name(p)) } else { diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 5208156ffd2..62a119d662b 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -10,7 +10,7 @@ use rustc_target::abi::LayoutOf; use super::CAST_PTR_ALIGNMENT; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Cast(ref cast_expr, cast_to) = expr.kind { + if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index bf722d0a3f4..040e0ca8864 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -30,7 +30,7 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast } // Don't lint for positive constants. - let const_val = constant(cx, &cx.typeck_results(), cast_op); + let const_val = constant(cx, cx.typeck_results(), cast_op); if_chain! { if let Some((Constant::Int(n), _)) = const_val; if let ty::Int(ity) = *cast_from.kind(); @@ -41,14 +41,14 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast } // Don't lint for the result of methods that always return non-negative values. - if let ExprKind::MethodCall(ref path, _, _, _) = cast_op.kind { + if let ExprKind::MethodCall(path, _, _, _) = cast_op.kind { let mut method_name = path.ident.name.as_str(); let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; if_chain! { if method_name == "unwrap"; if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]); - if let ExprKind::MethodCall(ref inner_path, _, _, _) = &arglist[0][0].kind; + if let ExprKind::MethodCall(inner_path, _, _, _) = &arglist[0][0].kind; then { method_name = inner_path.ident.name.as_str(); } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index d9e172c01a7..ae4fdd12c41 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -372,7 +372,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } - if let ExprKind::Cast(ref cast_expr, cast_to) = expr.kind { + if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index b38c70bcc41..d7136f84cc3 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { let result = if_chain! { if !in_external_macro(cx.sess(), item.span); - if let ExprKind::Binary(op, ref left, ref right) = &item.kind; + if let ExprKind::Binary(op, left, right) = &item.kind; then { match op.node { @@ -200,7 +200,7 @@ impl ConversionType { /// Check for `expr <= (to_type::MAX as from_type)` 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 ExprKind::Binary(ref op, left, right) = &expr.kind; if let Some((candidate, check)) = normalize_le_ge(op, left, right); if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); @@ -219,7 +219,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { } // First of we need a binary containing the expression & the cast - if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind { + if let ExprKind::Binary(ref op, left, right) = &expr.kind { normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) } else { None @@ -260,7 +260,7 @@ fn get_types_from_cast<'a>( // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { // to_type::max_value(), from_type - if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; + if let ExprKind::Cast(limit, 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); @@ -275,7 +275,7 @@ fn get_types_from_cast<'a>( let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { if_chain! { // `from_type::from, to_type::max_value()` - if let ExprKind::Call(ref from_func, ref args) = &expr.kind; + if let ExprKind::Call(from_func, args) = &expr.kind; // `to_type::max_value()` if args.len() == 1; if let limit = &args[0]; @@ -317,9 +317,9 @@ fn get_types_from_cast<'a>( /// Gets the type which implements the called function fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { if_chain! { - if let QPath::TypeRelative(ref ty, ref path) = &path; + if let QPath::TypeRelative(ty, path) = &path; if path.ident.name.as_str() == function; - if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind; + if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; if let [int] = &*tp.segments; then { let name = &int.ident.name.as_str(); @@ -333,7 +333,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: /// Gets the type as a string, if it is a supported integer fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { - if let QPath::Resolved(_, ref path) = *path; + if let QPath::Resolved(_, path) = *path; if let [ty] = &*path.segments; then { let name = &ty.ident.name.as_str(); diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 4cc542f723c..f62c6a9c325 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -152,7 +152,7 @@ impl<'tcx> Visitor<'tcx> for CcHelper { ExprKind::If(_, _, _) => { self.cc += 1; }, - ExprKind::Match(_, ref arms, _) => { + ExprKind::Match(_, arms, _) => { if arms.len() > 1 { self.cc += 1; } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index d601cb7c224..31ae63b5184 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -71,10 +71,8 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } for cond in conds.windows(2) { - if let ( - &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1), - &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2), - ) = (&cond[0].kind, &cond[1].kind) + if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = + (&cond[0].kind, &cond[1].kind) { if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) { return; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1b982221b06..8b503c9a030 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { if let ExprKind::If(_, _, _) = expr.kind { // skip ifs directly in else, it will be checked in the parent if if let Some(&Expr { - kind: ExprKind::If(_, _, Some(ref else_expr)), + kind: ExprKind::If(_, _, Some(else_expr)), .. }) = get_parent_expr(cx, expr) { @@ -247,7 +247,7 @@ fn lint_same_then_else<'tcx>( for value in &end_walker.uses { // Well we can't move this and all prev statements. So reset - if block_defs.contains(&value) { + if block_defs.contains(value) { moved_start = Some(index + 1); end_walker.defs.drain().for_each(|x| { block_defs.insert(x); @@ -555,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { } fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { - if let rustc_hir::QPath::Resolved(_, ref path) = *qpath { + if let rustc_hir::QPath::Resolved(_, path) = *qpath { if path.segments.len() == 1 { if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { self.uses.insert(var); diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index ac890c90c97..7b5cce6462a 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -33,7 +33,7 @@ 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::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; 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); diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 8a2146d93e8..710da8fe9e0 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -77,7 +77,7 @@ impl LateLintPass<'_> for Default { if_chain! { // Avoid cases already linted by `field_reassign_with_default` if !self.reassigned_linted.contains(&expr.span); - if let ExprKind::Call(ref path, ..) = expr.kind; + if let ExprKind::Call(path, ..) = expr.kind; if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); @@ -246,7 +246,7 @@ impl LateLintPass<'_> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { if_chain! { - if let ExprKind::Call(ref fn_expr, _) = &expr.kind; + if let ExprKind::Call(fn_expr, _) = &expr.kind; if let ExprKind::Path(qpath) = &fn_expr.kind; if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); then { @@ -262,11 +262,11 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { // only take assignments - if let StmtKind::Semi(ref later_expr) = this.kind; - if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; + if let StmtKind::Semi(later_expr) = this.kind; + if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind; // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement - if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; + if let ExprKind::Field(binding, field_ident) = assign_lhs.kind; if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; if let Some(second_binding_name) = path.segments.last(); if second_binding_name.ident.name == binding_name; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 9fad85f8ef4..647af3bdc04 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -199,7 +199,7 @@ fn check_hash_peq<'tcx>( then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); if peq_is_automatically_derived == hash_is_automatically_derived { return; @@ -253,7 +253,7 @@ fn check_ord_partial_ord<'tcx>( then { // Look for the PartialOrd implementations for `ty` 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)); + let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index b5796ccf43f..fb53b55ebd6 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -710,8 +710,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { // check for `begin_panic` if_chain! { - if let ExprKind::Call(ref func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let ExprKind::Call(func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; if let Some(path_def_id) = path.res.opt_def_id(); if match_panic_def_id(self.cx, path_def_id); if is_expn_of(expr.span, "unreachable").is_none(); diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 1d291565ec1..58543ae6e4e 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -47,7 +47,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { + if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) { return; } macro_rules! lint_double_comparison { @@ -88,7 +88,7 @@ impl<'tcx> DoubleComparisons { impl<'tcx> LateLintPass<'tcx> for DoubleComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind { Self::check_binop(cx, kind.node, lhs, rhs, expr.span); } } diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 5afdcb3c09f..e4e4a93b011 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -50,7 +50,7 @@ impl EarlyLintPass for DoubleParens { match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint(cx, DOUBLE_PARENS, expr.span, &msg); + span_lint(cx, DOUBLE_PARENS, expr.span, msg); }, _ => {}, }, @@ -58,7 +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, &msg); + span_lint(cx, DOUBLE_PARENS, param.span, msg); } } }, @@ -66,7 +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, &msg); + span_lint(cx, DOUBLE_PARENS, param.span, msg); } } }, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 7e7ec017bbb..b5b29760636 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -113,7 +113,7 @@ declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COP impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path, ref args) = expr.kind; + if let ExprKind::Call(path, args) = expr.kind; if let ExprKind::Path(ref qpath) = path.kind; if args.len() == 1; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 746c1f6df91..529807770f3 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -43,8 +43,8 @@ declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]); impl<'tcx> LateLintPass<'tcx> for DurationSubsec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind; + if let ExprKind::MethodCall(method_path, _ , args, _) = left.kind; 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 { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 25eb048448c..a815df1691a 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -57,14 +57,14 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::If(ref check, ref then_block, ref else_block) = expr.kind { - if let ExprKind::Unary(UnOp::Not, ref check) = check.kind { + if let ExprKind::If(check, then_block, ref else_block) = expr.kind { + if let ExprKind::Unary(UnOp::Not, check) = check.kind { if let Some((ty, map, key)) = check_cond(cx, check) { // in case of `if !m.contains_key(&k) { m.insert(k, v); }` // we can give a better error message let sole_expr = { else_block.is_none() - && if let ExprKind::Block(ref then_block, _) = then_block.kind { + && if let ExprKind::Block(then_block, _) = then_block.kind { (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 } else { true @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { sole_expr, }; - walk_expr(&mut visitor, &**then_block); + walk_expr(&mut visitor, then_block); } - } else if let Some(ref else_block) = *else_block { + } else if let Some(else_block) = *else_block { if let Some((ty, map, key)) = check_cond(cx, check) { let mut visitor = InsertVisitor { cx, @@ -103,10 +103,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref params, _) = check.kind; + if let ExprKind::MethodCall(path, _, params, _) = check.kind; if params.len() >= 2; if path.ident.name == sym!(contains_key); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind; then { let map = ¶ms[0]; let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); @@ -140,7 +140,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref params, _) = expr.kind; + if let ExprKind::MethodCall(path, _, params, _) = expr.kind; if params.len() == 3; if path.ident.name == sym!(insert); if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 6d7046ac8b7..90f391b5f5c 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -65,12 +65,12 @@ const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_e 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 { + if let ExprKind::Block(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 StmtKind::Semi(matchexpr) = stmt.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]); @@ -88,12 +88,12 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } - if let ExprKind::Binary(op, ref left, ref right) = e.kind { + if let ExprKind::Binary(op, left, right) = e.kind { if e.span.from_expansion() { return; } let macro_with_not_op = |expr_kind: &ExprKind<'_>| { - if let ExprKind::Unary(_, ref expr) = *expr_kind { + if let ExprKind::Unary(_, expr) = *expr_kind { in_macro(expr.span) } else { false @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { // do not suggest to dereference literals (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, // &foo == &bar - (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let lty = cx.typeck_results().expr_ty(l); let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } }, // &foo == bar - (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => { + (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => { let lty = cx.typeck_results().expr_ty(l); let lcpy = is_copy(cx, lty); if (requires_ref || lcpy) @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } }, // foo == &bar - (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let rty = cx.typeck_results().expr_ty(r); let rcpy = is_copy(cx, rty); if (requires_ref || rcpy) diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index 59602616781..f95ca86a2d0 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -34,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(ref cmp, left, right) = e.kind { match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { check(cx, left, e.span); diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 99253555a95..2f1aa53236d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind { + if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind { let body = cx.tcx.hir().body(eid); let ex = &body.value; @@ -109,7 +109,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { } if_chain!( - if let ExprKind::Call(ref caller, ref args) = ex.kind; + if let ExprKind::Call(caller, args) = ex.kind; if let ExprKind::Path(_) = caller.kind; @@ -142,7 +142,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { ); if_chain!( - if let ExprKind::MethodCall(ref path, _, ref args, _) = ex.kind; + if let ExprKind::MethodCall(path, _, args, _) = ex.kind; // Not the same number of arguments, there is no way the closure is the same as the function return; if args.len() == decl.inputs.len(); @@ -178,7 +178,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id); if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) { - if match_borrow_depth(expected_type_of_self, &actual_type_of_self) + if match_borrow_depth(expected_type_of_self, actual_type_of_self) && implements_trait(cx, actual_type_of_self, trait_id, &[]) { return Some(cx.tcx.def_path_str(trait_id)); @@ -187,8 +187,8 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a cx.tcx.impl_of_method(method_def_id).and_then(|_| { //a type may implicitly implement other type's methods (e.g. Deref) - if match_types(expected_type_of_self, &actual_type_of_self) { - return Some(get_type_name(cx, &actual_type_of_self)); + if match_types(expected_type_of_self, actual_type_of_self) { + return Some(get_type_name(cx, actual_type_of_self)); } None }) @@ -196,7 +196,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), + (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2), (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } @@ -218,7 +218,7 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String { match ty.kind() { ty::Adt(t, _) => cx.tcx.def_path_str(t.did), - ty::Ref(_, r, _) => get_type_name(cx, &r), + ty::Ref(_, r, _) => get_type_name(cx, r), _ => ty.to_string(), } } @@ -230,7 +230,7 @@ fn compare_inputs( for (closure_input, function_arg) in closure_inputs.zip(call_args) { if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind { // XXXManishearth Should I be checking the binding mode here? - if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind { + if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind { if p.segments.len() != 1 { // If it's a proper path, it can't be a local variable return false; diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index ea33a4d98fd..762f64fe37a 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. match expr.kind { - ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => { + ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => { if let Some(var) = path_to_local(lhs) { let mut visitor = ReadVisitor { cx, @@ -87,12 +87,12 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(ref local) => { - if let Local { init: Some(ref e), .. } = **local { + StmtKind::Local(local) => { + if let Local { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } }, - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => DivergenceVisitor { cx }.maybe_walk_expr(e), + StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e), StmtKind::Item(..) => {}, } } @@ -106,7 +106,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Closure(..) => {}, - ExprKind::Match(ref e, arms, _) => { + ExprKind::Match(e, arms, _) => { self.visit_expr(e); for arm in arms { if let Some(Guard::If(if_expr)) = arm.guard { @@ -130,7 +130,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e), - ExprKind::Call(ref func, _) => { + ExprKind::Call(func, _) => { let typ = self.cx.typeck_results().expr_ty(func); match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { @@ -266,10 +266,10 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) - fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => check_expr(vis, expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer // expression if it has one. Otherwise, keep going. - StmtKind::Local(ref local) => local + StmtKind::Local(local) => local .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), @@ -343,7 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { /// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`. fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::Assign(ref lhs, ..) = parent.kind { + if let ExprKind::Assign(lhs, ..) = parent.kind { return lhs.hir_id == expr.hir_id; } } diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 84fac998662..16246e548b6 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -28,7 +28,7 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path_expr, ref _args) = e.kind; + if let ExprKind::Call(path_expr, _args) = e.kind; if let ExprKind::Path(ref path) = path_expr.kind; if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::EXIT); diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4146b7baa10..da4936ff25b 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -34,11 +34,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // match call to unwrap - if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind; + if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind; if unwrap_fun.ident.name == sym::unwrap; // match call to write_fmt if !unwrap_args.is_empty(); - if let ExprKind::MethodCall(ref write_fun, _, write_args, _) = + if let ExprKind::MethodCall(write_fun, _, write_args, _) = unwrap_args[0].kind; if write_fun.ident.name == sym!(write_fmt); // match calls to std::io::stdout() / std::io::stderr () @@ -135,10 +135,10 @@ fn write_output_string(write_args: &[Expr<'_>]) -> Option { if_chain! { // Obtain the string that should be printed if write_args.len() > 1; - if let ExprKind::Call(_, ref output_args) = write_args[1].kind; + if let ExprKind::Call(_, output_args) = write_args[1].kind; if !output_args.is_empty(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref output_string_expr) = output_args[0].kind; - if let ExprKind::Array(ref string_exprs) = output_string_expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind; + if let ExprKind::Array(string_exprs) = output_string_expr.kind; // we only want to provide an automatic suggestion for simple (non-format) strings if string_exprs.len() == 1; if let ExprKind::Lit(ref lit) = string_exprs[0].kind; diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 52a5a7acf0d..2937fcb9ca0 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -81,8 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // check for `begin_panic` if_chain! { - if let ExprKind::Call(ref func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let ExprKind::Call(func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; if let Some(path_def_id) = path.res.opt_def_id(); if match_panic_def_id(self.lcx, path_def_id); if is_expn_of(expr.span, "unreachable").is_none(); diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index 0c59db360f9..b5ebe5f90ba 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { 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 { + if let ExprKind::Binary(op, left, right) = expr.kind { match op.node { BinOpKind::Lt => { lhs = left; @@ -88,8 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { if let ty::Float(_) = t_val_r.kind(); then { - let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); - let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); + let sug_l = sugg::Sugg::hir(cx, val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, val_r, ".."); // format the suggestion let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); // spans the lint diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 0b5f0379ce6..e0b687b0205 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -131,7 +131,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su let mut suggestion = Sugg::hir(cx, expr, ".."); if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { - expr = &inner_expr; + expr = inner_expr; } if_chain! { @@ -313,8 +313,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { Spanned { node: BinOpKind::Add, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) = parent.kind { let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; @@ -329,7 +329,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { "{}.mul_add({}, {})", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], ".."), - Sugg::hir(cx, &other_addend, ".."), + Sugg::hir(cx, other_addend, ".."), ), Applicability::MachineApplicable, ); @@ -356,18 +356,18 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { Spanned { node: BinOpKind::Add, .. }, - ref add_lhs, - ref add_rhs, + add_lhs, + 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 let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind; 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, ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, ".."))); } } @@ -376,13 +376,13 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if let ExprKind::MethodCall( PathSegment { ident: lmethod_name, .. }, ref _lspan, - ref largs, + largs, _ ) = add_lhs.kind; if let ExprKind::MethodCall( PathSegment { ident: rmethod_name, .. }, ref _rspan, - ref rargs, + rargs, _ ) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; @@ -416,11 +416,11 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(ref path, _, ref method_args, _) = lhs.kind; + if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind; if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point(); if path.ident.name.as_str() == "exp"; then { @@ -442,7 +442,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); if cx.typeck_results().expr_ty(rhs).is_floating_point(); then { @@ -604,8 +604,8 @@ 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; + if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind; then { return method_name_a.as_str() == method_name_b.as_str() && args_a.len() == args_b.len() && @@ -630,8 +630,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { 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; + if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind; + if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind; then { span_lint_and_sugg( cx, @@ -675,7 +675,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "conversion to degrees can be done more accurately", "consider using", - format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")), + format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")), Applicability::MachineApplicable, ); } else if @@ -688,7 +688,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "conversion to radians can be done more accurately", "consider using", - format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")), + format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")), Applicability::MachineApplicable, ); } @@ -698,7 +698,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { 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 { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(&args[0]); if recv_ty.is_floating_point() { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index a33f987b423..4729abbd8e3 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -78,8 +78,8 @@ fn span_useless_format(cx: &T, span: Span, help: &str, mut sugg: fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref format_args) = expr.kind; - if let ExprKind::Array(ref elems) = arms[0].body.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind; + if let ExprKind::Array(elems) = arms[0].body.kind; if elems.len() == 1; if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW); // matches `core::fmt::Display::fmt` @@ -88,10 +88,10 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id(); if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD); // check `(arg0,)` in match block - if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind; + if let PatKind::Tuple(pats, None) = arms[0].pat.kind; if pats.len() == 1; then { - let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); + 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; } @@ -101,7 +101,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & } } else { let snip = snippet(cx, format_args.span, ""); - if let ExprKind::MethodCall(ref path, _, _, _) = format_args.kind { + if let ExprKind::MethodCall(path, _, _, _) = format_args.kind { if path.ident.name == sym!(to_string) { return Some(format!("{}", snip)); } @@ -120,16 +120,16 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option [], }` if tup.is_empty() { @@ -152,16 +152,16 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option) -> bool { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; - if let ExprKind::Array(ref exprs) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; + if let ExprKind::Array(exprs) = expr.kind; if exprs.len() == 1; // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; + if let ExprKind::Struct(_, fields, _) = exprs[0].kind; if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); // struct `core::fmt::rt::v1::FormatSpec` - if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind; + if let ExprKind::Struct(_, fields, _) = format_field.expr.kind; if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision); if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; if last_path_segment(precision_path).ident.name == sym::Implied; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 9e943c647fe..db5fe90b663 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -25,12 +25,12 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { 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 let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); return; } else if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { check_must_use_candidate( cx, - &sig.decl, + sig.decl, cx.tcx.hir().body(*body_id), item.span, item.hir_id(), @@ -48,11 +48,11 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() { check_must_use_candidate( cx, - &sig.decl, + sig.decl, cx.tcx.hir().body(*body_id), item.span, item.hir_id(), @@ -71,13 +71,13 @@ pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { check_must_use_candidate( cx, - &sig.decl, + sig.decl, body, item.span, item.hir_id(), @@ -160,8 +160,8 @@ fn check_must_use_candidate<'tcx>( fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { match decl.output { hir::FnRetTy::DefaultReturn(_) => true, - hir::FnRetTy::Return(ref ty) => match ty.kind { - hir::TyKind::Tup(ref tys) => tys.is_empty(), + hir::FnRetTy::Return(ty) => match ty.kind { + hir::TyKind::Tup(tys) => tys.is_empty(), hir::TyKind::Never => true, _ => false, }, @@ -170,7 +170,7 @@ fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { let mut tys = DefIdSet::default(); - body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) + body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys)) } fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool { @@ -178,7 +178,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) return false; // ignore `_` patterns } 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) + is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) } else { false } @@ -190,12 +190,12 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(ref adt, ref substs) => { + ty::Adt(adt, substs) => { tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Tuple(substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) @@ -239,7 +239,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { tys.clear(); } }, - Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { + Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { self.mutates_static |= is_mutated_static(target) }, _ => {}, @@ -257,7 +257,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { match e.kind { Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), Path(_) => true, - Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), + Field(inner, _) | Index(inner, _) => is_mutated_static(inner), _ => false, } } diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index cc69f4aed0c..b8ea6990866 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -27,7 +27,7 @@ pub(super) fn check_fn( pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); - check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id()); + check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.hir_id()); } } @@ -77,7 +77,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { match expr.kind { - hir::ExprKind::Call(ref f, args) => { + hir::ExprKind::Call(f, args) => { let ty = self.typeck_results.expr_ty(f); if type_is_unsafe_function(self.cx, ty) { @@ -96,7 +96,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { } } }, - hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), + hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr), _ => (), } diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index 3d1092ce21f..c073f312d38 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -18,7 +18,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { 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); + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); } } } @@ -28,7 +28,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< 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); + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); } } } @@ -38,7 +38,7 @@ pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte 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); + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); } } } @@ -46,7 +46,7 @@ pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte 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::FnRetTy::Return(ty) = decl.output; let ty = hir_ty_to_ty(cx.tcx, ty); if is_type_diagnostic_item(cx, ty, sym::result_type); if let ty::Adt(_, substs) = ty.kind(); diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 62b1e6bd7ca..63a14d8d4cd 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -49,7 +49,7 @@ pub(super) fn check_trait_item( if sig.header.abi == Abi::Rust { check_arg_number( cx, - &sig.decl, + sig.decl, item.span.with_hi(sig.decl.output.span().hi()), too_many_arguments_threshold, ); diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index cbcef567c53..3707e792177 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // Is a method call - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; // Method name is "get" if path.ident.name == sym!(get); diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 8bed5e1bf64..366b3b46a8a 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -35,7 +35,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(cmp, left, right) = e.kind { if is_allowed(cx, cmp, left, right) { return; } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 56dfcc6a506..f661f7ede82 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { cx, }; if let ExprKind::Match( - ref op, - ref arms, + op, + arms, MatchSource::IfLetDesugar { contains_else_clause: true, }, @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { { op_visit.visit_expr(op); if op_visit.mutex_lock_called { - for arm in *arms { + for arm in arms { arm_visit.visit_arm(arm); } diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 6e9280c8c7e..611da3744ee 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -44,9 +44,9 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let - if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation + if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let + if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) + if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0b5bf060d4c..ee16519692f 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -72,15 +72,15 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } if_chain! { - if let ExprKind::If(ref cond, ref then, Some(ref els)) = expr.kind; - if let ExprKind::Block(ref then_block, _) = then.kind; - if let Some(ref then_expr) = then_block.expr; - if let ExprKind::Call(ref then_call, [then_arg]) = then_expr.kind; + if let ExprKind::If(cond, then, Some(els)) = expr.kind; + if let ExprKind::Block(then_block, _) = then.kind; + if let Some(then_expr) = then_block.expr; + if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); - if let ExprKind::Block(ref els_block, _) = els.kind; + if let ExprKind::Block(els_block, _) = els.kind; if els_block.stmts.is_empty(); - if let Some(ref els_expr) = els_block.expr; + if let Some(els_expr) = els_block.expr; if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); then { diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 0b748b4d72d..77a38544edc 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -207,7 +207,7 @@ enum ImplicitHasherType<'tcx> { impl<'tcx> ImplicitHasherType<'tcx> { /// Checks that `ty` is a target type without a `BuildHasher`. fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option { - if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind { + if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind { let params: Vec<_> = path .segments .last() @@ -330,8 +330,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref fun, ref args) = e.kind; - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind; + if let ExprKind::Call(fun, args) = e.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; then { if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 6863645a92d..6b379b0d59b 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -100,10 +100,10 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { if check_all_arms { for arm in arms { - expr_match(cx, &arm.body); + expr_match(cx, arm.body); } } else { - expr_match(cx, &arms.first().expect("`if let` doesn't have a single arm").body); + expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body); } }, // skip if it already has a return statement diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5207c628987..cba3183e869 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -46,21 +46,21 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let ExprKind::If(cond, then, None) = &expr.kind; // Check if the conditional expression is a binary operation - if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind; + if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; // Ensure that the binary operator is >, != and < if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; // Check if the true condition block has only one statement - if let ExprKind::Block(ref block, _) = then.kind; + if let ExprKind::Block(block, _) = then.kind; if block.stmts.len() == 1 && block.expr.is_none(); // Check if assign operation is done - if let StmtKind::Semi(ref e) = block.stmts[0].kind; + if let StmtKind::Semi(e) = block.stmts[0].kind; if let Some(target) = subtracts_one(cx, e); // Extracting out the variable name - if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind; + if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; then { // Handle symmetric conditions in the if statement @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { print_lint_and_sugg(cx, &var_name, expr); }; }, - ExprKind::Call(ref func, _) => { + ExprKind::Call(func, _) => { if let ExprKind::Path(ref cond_num_path) = func.kind { if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { print_lint_and_sugg(cx, &var_name, expr); @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> { match expr.kind { - ExprKind::AssignOp(ref op1, ref target, ref value) => { + ExprKind::AssignOp(ref op1, target, value) => { if_chain! { if BinOpKind::Sub == op1.node; // Check if literal being subtracted is one @@ -133,9 +133,9 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<' } } }, - ExprKind::Assign(ref target, ref value, _) => { + ExprKind::Assign(target, value, _) => { if_chain! { - if let ExprKind::Binary(ref op1, ref left1, ref right1) = value.kind; + if let ExprKind::Binary(ref op1, left1, right1) = value.kind; if BinOpKind::Sub == op1.node; if SpanlessEq::new(cx).eq_expr(left1, target); diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 94d39019608..1c54599abc4 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 { + if let ExprKind::Index(array, index) = &expr.kind { 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[..] diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index fb35bc1e780..bbb4ddc613a 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -139,7 +139,7 @@ const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { - ExprKind::MethodCall(ref method, _, ref args, _) => { + ExprKind::MethodCall(method, _, args, _) => { for &(name, len, heuristic, cap) in &HEURISTICS { if method.ident.name.as_str() == name && args.len() == len { return (match heuristic { @@ -159,9 +159,9 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } Finite }, - ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), - ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e), - ExprKind::Call(ref path, _) => { + ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), + ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), + ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { match_qpath(qpath, &paths::REPEAT).into() } else { @@ -215,7 +215,7 @@ const INFINITE_COLLECTORS: [&[&str]; 8] = [ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { - ExprKind::MethodCall(ref method, _, ref args, _) => { + ExprKind::MethodCall(method, _, args, _) => { for &(name, len) in &COMPLETING_METHODS { if method.ident.name.as_str() == name && args.len() == len { return is_infinite(cx, &args[0]); @@ -240,7 +240,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } }, - ExprKind::Binary(op, ref l, ref r) => { + ExprKind::Binary(op, l, r) => { if op.node.is_comparison() { return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index d183fc41f31..c67c02eefa5 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -86,7 +86,7 @@ impl Ord for FullInt { } fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + if let ExprKind::Cast(cast_exp, _) = expr.kind { let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); // if it's a cast from i32 to u32 wrapping will invalidate all these checks @@ -131,7 +131,7 @@ fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> } fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { - if let ExprKind::Cast(ref cast_val, _) = expr.kind { + if let ExprKind::Cast(cast_val, _) = expr.kind { span_lint( cx, INVALID_UPCAST_COMPARISONS, @@ -203,7 +203,7 @@ fn upcast_comparison_bounds_err<'tcx>( impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { val diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 76584dc1822..f166748d86b 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); if variant.fields.len() == 1 { let span = match def.variants[i].data { - VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => { + VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) => { fields[0].ty.span }, VariantData::Unit(..) => unreachable!(), diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 78152ad9019..bb57adff7be 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - if let ItemKind::Trait(_, _, _, _, ref trait_items) = item.kind { + if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind { check_trait_items(cx, item, trait_items); } } @@ -162,7 +162,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind { match cmp { BinOpKind::Eq => { check_cmp(cx, expr.span, left, right, "", 0); // len == 0 @@ -372,8 +372,7 @@ fn check_for_is_empty( } 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(method_path, _, 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" { @@ -451,7 +450,7 @@ fn is_empty_string(expr: &Expr<'_>) -> bool { } fn is_empty_array(expr: &Expr<'_>) -> bool { - if let ExprKind::Array(ref arr) = expr.kind { + if let ExprKind::Array(arr) = expr.kind { return arr.is_empty(); } false @@ -480,17 +479,17 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.tcx .associated_items(*imp) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) + .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| { + ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx .associated_items(principal.def_id()) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) + .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/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 2d7d9c9befb..67eae4d87bb 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -61,13 +61,13 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { while let Some(stmt) = it.next() { if_chain! { if let Some(expr) = it.peek(); - if let hir::StmtKind::Local(ref local) = stmt.kind; + if let hir::StmtKind::Local(local) = stmt.kind; if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; - if let hir::StmtKind::Expr(ref if_) = expr.kind; - if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.kind; + if let hir::StmtKind::Expr(if_) = expr.kind; + if let hir::ExprKind::If(cond, then, ref else_) = if_.kind; let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id); if !used_visitor.check_expr(cond); - if let hir::ExprKind::Block(ref then, _) = then.kind; + if let hir::ExprKind::Block(then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); if !used_visitor.check_expr(value); then { @@ -79,20 +79,20 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { ); if has_interior_mutability { return; } - let (default_multi_stmts, default) = if let Some(ref else_) = *else_ { - if let hir::ExprKind::Block(ref else_, _) = else_.kind { + let (default_multi_stmts, default) = if let Some(else_) = *else_ { + if let hir::ExprKind::Block(else_, _) = else_.kind { if let Some(default) = check_assign(cx, canonical_id, else_) { (else_.stmts.len() > 1, default) - } else if let Some(ref default) = local.init { - (true, &**default) + } else if let Some(default) = local.init { + (true, default) } else { continue; } } else { continue; } - } else if let Some(ref default) = local.init { - (false, &**default) + } else if let Some(default) = local.init { + (false, default) } else { continue; }; @@ -144,8 +144,8 @@ fn check_assign<'tcx>( if_chain! { if block.expr.is_none(); if let Some(expr) = block.stmts.iter().last(); - if let hir::StmtKind::Semi(ref expr) = expr.kind; - if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind; + if let hir::StmtKind::Semi(expr) = expr.kind; + if let hir::ExprKind::Assign(var, value, _) = expr.kind; if path_to_local_id(var, decl); then { let mut v = LocalUsedVisitor::new(cx, decl); diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 7e3a76ded2c..17e23781db7 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if_chain! { if let PatKind::Wild = local.pat.kind; - if let Some(ref init) = local.init; + if let Some(init) = local.init; then { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 43e89c2475f..cd92b551abd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -541,482 +541,482 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // begin register lints, do not remove this comment, it’s used in `update_lints` store.register_lints(&[ #[cfg(feature = "internal-lints")] - &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + utils::internal_lints::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal-lints")] - &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] - &utils::internal_lints::IF_CHAIN_STYLE, + utils::internal_lints::IF_CHAIN_STYLE, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, + utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, #[cfg(feature = "internal-lints")] - &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal-lints")] - &utils::internal_lints::PRODUCE_ICE, + utils::internal_lints::PRODUCE_ICE, #[cfg(feature = "internal-lints")] - &utils::internal_lints::UNNECESSARY_SYMBOL_STR, - &absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, - &approx_const::APPROX_CONSTANT, - &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, - &async_yields_async::ASYNC_YIELDS_ASYNC, - &atomic_ordering::INVALID_ATOMIC_ORDERING, - &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, - &attrs::DEPRECATED_CFG_ATTR, - &attrs::DEPRECATED_SEMVER, - &attrs::EMPTY_LINE_AFTER_OUTER_ATTR, - &attrs::INLINE_ALWAYS, - &attrs::MISMATCHED_TARGET_OS, - &attrs::USELESS_ATTRIBUTE, - &await_holding_invalid::AWAIT_HOLDING_LOCK, - &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, - &bit_mask::BAD_BIT_MASK, - &bit_mask::INEFFECTIVE_BIT_MASK, - &bit_mask::VERBOSE_BIT_MASK, - &blacklisted_name::BLACKLISTED_NAME, - &blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, - &booleans::LOGIC_BUG, - &booleans::NONMINIMAL_BOOL, - &bytecount::NAIVE_BYTECOUNT, - &cargo_common_metadata::CARGO_COMMON_METADATA, - &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - &casts::CAST_LOSSLESS, - &casts::CAST_POSSIBLE_TRUNCATION, - &casts::CAST_POSSIBLE_WRAP, - &casts::CAST_PRECISION_LOSS, - &casts::CAST_PTR_ALIGNMENT, - &casts::CAST_REF_TO_MUT, - &casts::CAST_SIGN_LOSS, - &casts::CHAR_LIT_AS_U8, - &casts::FN_TO_NUMERIC_CAST, - &casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - &casts::PTR_AS_PTR, - &casts::UNNECESSARY_CAST, - &checked_conversions::CHECKED_CONVERSIONS, - &cognitive_complexity::COGNITIVE_COMPLEXITY, - &collapsible_if::COLLAPSIBLE_ELSE_IF, - &collapsible_if::COLLAPSIBLE_IF, - &collapsible_match::COLLAPSIBLE_MATCH, - &comparison_chain::COMPARISON_CHAIN, - &copies::BRANCHES_SHARING_CODE, - &copies::IFS_SAME_COND, - &copies::IF_SAME_THEN_ELSE, - &copies::SAME_FUNCTIONS_IN_IF_CONDITION, - ©_iterator::COPY_ITERATOR, - &create_dir::CREATE_DIR, - &dbg_macro::DBG_MACRO, - &default::DEFAULT_TRAIT_ACCESS, - &default::FIELD_REASSIGN_WITH_DEFAULT, - &default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, - &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, - &disallowed_method::DISALLOWED_METHOD, - &doc::DOC_MARKDOWN, - &doc::MISSING_ERRORS_DOC, - &doc::MISSING_PANICS_DOC, - &doc::MISSING_SAFETY_DOC, - &doc::NEEDLESS_DOCTEST_MAIN, - &double_comparison::DOUBLE_COMPARISONS, - &double_parens::DOUBLE_PARENS, - &drop_forget_ref::DROP_COPY, - &drop_forget_ref::DROP_REF, - &drop_forget_ref::FORGET_COPY, - &drop_forget_ref::FORGET_REF, - &duration_subsec::DURATION_SUBSEC, - &else_if_without_else::ELSE_IF_WITHOUT_ELSE, - &empty_enum::EMPTY_ENUM, - &entry::MAP_ENTRY, - &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - &enum_variants::ENUM_VARIANT_NAMES, - &enum_variants::MODULE_INCEPTION, - &enum_variants::MODULE_NAME_REPETITIONS, - &enum_variants::PUB_ENUM_VARIANT_NAMES, - &eq_op::EQ_OP, - &eq_op::OP_REF, - &erasing_op::ERASING_OP, - &escape::BOXED_LOCAL, - &eta_reduction::REDUNDANT_CLOSURE, - &eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - &eval_order_dependence::DIVERGING_SUB_EXPRESSION, - &eval_order_dependence::EVAL_ORDER_DEPENDENCE, - &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, - &excessive_bools::STRUCT_EXCESSIVE_BOOLS, - &exhaustive_items::EXHAUSTIVE_ENUMS, - &exhaustive_items::EXHAUSTIVE_STRUCTS, - &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, - &floating_point_arithmetic::SUBOPTIMAL_FLOPS, - &format::USELESS_FORMAT, - &formatting::POSSIBLE_MISSING_COMMA, - &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, - &formatting::SUSPICIOUS_ELSE_FORMATTING, - &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, - &from_over_into::FROM_OVER_INTO, - &from_str_radix_10::FROM_STR_RADIX_10, - &functions::DOUBLE_MUST_USE, - &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, - &get_last_with_len::GET_LAST_WITH_LEN, - &identity_op::IDENTITY_OP, - &if_let_mutex::IF_LET_MUTEX, - &if_let_some_result::IF_LET_SOME_RESULT, - &if_not_else::IF_NOT_ELSE, - &if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, - &implicit_hasher::IMPLICIT_HASHER, - &implicit_return::IMPLICIT_RETURN, - &implicit_saturating_sub::IMPLICIT_SATURATING_SUB, - &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, - &indexing_slicing::INDEXING_SLICING, - &indexing_slicing::OUT_OF_BOUNDS_INDEXING, - &infinite_iter::INFINITE_ITER, - &infinite_iter::MAYBE_INFINITE_ITER, - &inherent_impl::MULTIPLE_INHERENT_IMPL, - &inherent_to_string::INHERENT_TO_STRING, - &inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, - &inline_fn_without_body::INLINE_FN_WITHOUT_BODY, - &int_plus_one::INT_PLUS_ONE, - &integer_division::INTEGER_DIVISION, - &invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, - &items_after_statements::ITEMS_AFTER_STATEMENTS, - &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, - &let_underscore::LET_UNDERSCORE_DROP, - &let_underscore::LET_UNDERSCORE_LOCK, - &let_underscore::LET_UNDERSCORE_MUST_USE, - &lifetimes::EXTRA_UNUSED_LIFETIMES, - &lifetimes::NEEDLESS_LIFETIMES, - &literal_representation::DECIMAL_LITERAL_REPRESENTATION, - &literal_representation::INCONSISTENT_DIGIT_GROUPING, - &literal_representation::LARGE_DIGIT_GROUPS, - &literal_representation::MISTYPED_LITERAL_SUFFIXES, - &literal_representation::UNREADABLE_LITERAL, - &literal_representation::UNUSUAL_BYTE_GROUPINGS, - &loops::EMPTY_LOOP, - &loops::EXPLICIT_COUNTER_LOOP, - &loops::EXPLICIT_INTO_ITER_LOOP, - &loops::EXPLICIT_ITER_LOOP, - &loops::FOR_KV_MAP, - &loops::FOR_LOOPS_OVER_FALLIBLES, - &loops::ITER_NEXT_LOOP, - &loops::MANUAL_FLATTEN, - &loops::MANUAL_MEMCPY, - &loops::MUT_RANGE_BOUND, - &loops::NEEDLESS_COLLECT, - &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, - ¯o_use::MACRO_USE_IMPORTS, - &main_recursion::MAIN_RECURSION, - &manual_async_fn::MANUAL_ASYNC_FN, - &manual_map::MANUAL_MAP, - &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - &manual_ok_or::MANUAL_OK_OR, - &manual_strip::MANUAL_STRIP, - &manual_unwrap_or::MANUAL_UNWRAP_OR, - &map_clone::MAP_CLONE, - &map_err_ignore::MAP_ERR_IGNORE, - &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, - &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_SAME_ARMS, - &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, - &matches::WILDCARD_ENUM_MATCH_ARM, - &matches::WILDCARD_IN_OR_PATTERNS, - &mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, - &mem_forget::MEM_FORGET, - &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::BYTES_NTH, - &methods::CHARS_LAST_CMP, - &methods::CHARS_NEXT_CMP, - &methods::CLONE_DOUBLE_REF, - &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_IDENTITY, - &methods::FILTER_MAP_NEXT, - &methods::FILTER_NEXT, - &methods::FLAT_MAP_IDENTITY, - &methods::FROM_ITER_INSTEAD_OF_COLLECT, - &methods::GET_UNWRAP, - &methods::IMPLICIT_CLONE, - &methods::INEFFICIENT_TO_STRING, - &methods::INSPECT_FOR_EACH, - &methods::INTO_ITER_ON_REF, - &methods::ITERATOR_STEP_BY_ZERO, - &methods::ITER_CLONED_COLLECT, - &methods::ITER_COUNT, - &methods::ITER_NEXT_SLICE, - &methods::ITER_NTH, - &methods::ITER_NTH_ZERO, - &methods::ITER_SKIP_NEXT, - &methods::MANUAL_FILTER_MAP, - &methods::MANUAL_FIND_MAP, - &methods::MANUAL_SATURATING_ARITHMETIC, - &methods::MAP_COLLECT_RESULT_UNIT, - &methods::MAP_FLATTEN, - &methods::MAP_UNWRAP_OR, - &methods::NEW_RET_NO_SELF, - &methods::OK_EXPECT, - &methods::OPTION_AS_REF_DEREF, - &methods::OPTION_FILTER_MAP, - &methods::OPTION_MAP_OR_NONE, - &methods::OR_FUN_CALL, - &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::SEARCH_IS_SOME, - &methods::SHOULD_IMPLEMENT_TRAIT, - &methods::SINGLE_CHAR_ADD_STR, - &methods::SINGLE_CHAR_PATTERN, - &methods::SKIP_WHILE_NEXT, - &methods::STRING_EXTEND_CHARS, - &methods::SUSPICIOUS_MAP, - &methods::UNINIT_ASSUMED_INIT, - &methods::UNNECESSARY_FILTER_MAP, - &methods::UNNECESSARY_FOLD, - &methods::UNNECESSARY_LAZY_EVALUATIONS, - &methods::UNWRAP_USED, - &methods::USELESS_ASREF, - &methods::WRONG_PUB_SELF_CONVENTION, - &methods::WRONG_SELF_CONVENTION, - &methods::ZST_OFFSET, - &minmax::MIN_MAX, - &misc::CMP_NAN, - &misc::CMP_OWNED, - &misc::FLOAT_CMP, - &misc::FLOAT_CMP_CONST, - &misc::MODULO_ONE, - &misc::SHORT_CIRCUIT_STATEMENT, - &misc::TOPLEVEL_REF_ARG, - &misc::USED_UNDERSCORE_BINDING, - &misc::ZERO_PTR, - &misc_early::BUILTIN_TYPE_SHADOW, - &misc_early::DOUBLE_NEG, - &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, - &misc_early::MIXED_CASE_HEX_LITERALS, - &misc_early::REDUNDANT_PATTERN, - &misc_early::UNNEEDED_FIELD_PATTERN, - &misc_early::UNNEEDED_WILDCARD_PATTERN, - &misc_early::UNSEPARATED_LITERAL_SUFFIX, - &misc_early::ZERO_PREFIXED_LITERAL, - &missing_const_for_fn::MISSING_CONST_FOR_FN, - &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, - &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - &modulo_arithmetic::MODULO_ARITHMETIC, - &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, - &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_for_each::NEEDLESS_FOR_EACH, - &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &needless_question_mark::NEEDLESS_QUESTION_MARK, - &needless_update::NEEDLESS_UPDATE, - &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, - &neg_multiply::NEG_MULTIPLY, - &new_without_default::NEW_WITHOUT_DEFAULT, - &no_effect::NO_EFFECT, - &no_effect::UNNECESSARY_OPERATION, - &non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, - &non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, - &non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, - &non_expressive_names::MANY_SINGLE_CHAR_NAMES, - &non_expressive_names::SIMILAR_NAMES, - &non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, - &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_in_result_fn::PANIC_IN_RESULT_FN, - &panic_unimplemented::PANIC, - &panic_unimplemented::TODO, - &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, - &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::MANUAL_RANGE_CONTAINS, - &ranges::RANGE_MINUS_ONE, - &ranges::RANGE_PLUS_ONE, - &ranges::RANGE_ZIP_WITH_LEN, - &ranges::REVERSED_EMPTY_RANGES, - &redundant_clone::REDUNDANT_CLONE, - &redundant_closure_call::REDUNDANT_CLOSURE_CALL, - &redundant_else::REDUNDANT_ELSE, - &redundant_field_names::REDUNDANT_FIELD_NAMES, - &redundant_pub_crate::REDUNDANT_PUB_CRATE, - &redundant_slicing::REDUNDANT_SLICING, - &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, - &ref_option_ref::REF_OPTION_REF, - &reference::DEREF_ADDROF, - &reference::REF_IN_DEREF, - ®ex::INVALID_REGEX, - ®ex::TRIVIAL_REGEX, - &repeat_once::REPEAT_ONCE, - &returns::LET_AND_RETURN, - &returns::NEEDLESS_RETURN, - &self_assignment::SELF_ASSIGNMENT, - &semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, - &serde_api::SERDE_API_MISUSE, - &shadow::SHADOW_REUSE, - &shadow::SHADOW_SAME, - &shadow::SHADOW_UNRELATED, - &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, - &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, - &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &stable_sort_primitive::STABLE_SORT_PRIMITIVE, - &strings::STRING_ADD, - &strings::STRING_ADD_ASSIGN, - &strings::STRING_FROM_UTF8_AS_BYTES, - &strings::STRING_LIT_AS_BYTES, - &strings::STRING_TO_STRING, - &strings::STR_TO_STRING, - &suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, - &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, - &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, - &swap::ALMOST_SWAPPED, - &swap::MANUAL_SWAP, - &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, - &transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - &transmute::TRANSMUTE_BYTES_TO_STR, - &transmute::TRANSMUTE_FLOAT_TO_INT, - &transmute::TRANSMUTE_INT_TO_BOOL, - &transmute::TRANSMUTE_INT_TO_CHAR, - &transmute::TRANSMUTE_INT_TO_FLOAT, - &transmute::TRANSMUTE_PTR_TO_PTR, - &transmute::TRANSMUTE_PTR_TO_REF, - &transmute::UNSOUND_COLLECTION_TRANSMUTE, - &transmute::USELESS_TRANSMUTE, - &transmute::WRONG_TRANSMUTE, - &transmuting_null::TRANSMUTING_NULL, - &try_err::TRY_ERR, - &types::BORROWED_BOX, - &types::BOX_VEC, - &types::LINKEDLIST, - &types::OPTION_OPTION, - &types::RC_BUFFER, - &types::REDUNDANT_ALLOCATION, - &types::TYPE_COMPLEXITY, - &types::VEC_BOX, - &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, - &unicode::INVISIBLE_CHARACTERS, - &unicode::NON_ASCII_LITERAL, - &unicode::UNICODE_NOT_NFC, - &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, - &unit_types::LET_UNIT_VALUE, - &unit_types::UNIT_ARG, - &unit_types::UNIT_CMP, - &unnamed_address::FN_ADDRESS_COMPARISONS, - &unnamed_address::VTABLE_ADDRESS_COMPARISONS, - &unnecessary_sort_by::UNNECESSARY_SORT_BY, - &unnecessary_wraps::UNNECESSARY_WRAPS, - &unnested_or_patterns::UNNESTED_OR_PATTERNS, - &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, - &unwrap_in_result::UNWRAP_IN_RESULT, - &upper_case_acronyms::UPPER_CASE_ACRONYMS, - &use_self::USE_SELF, - &useless_conversion::USELESS_CONVERSION, - &vec::USELESS_VEC, - &vec_init_then_push::VEC_INIT_THEN_PUSH, - &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, - &verbose_file_reads::VERBOSE_FILE_READS, - &wildcard_dependencies::WILDCARD_DEPENDENCIES, - &wildcard_imports::ENUM_GLOB_USE, - &wildcard_imports::WILDCARD_IMPORTS, - &write::PRINTLN_EMPTY_STRING, - &write::PRINT_LITERAL, - &write::PRINT_STDERR, - &write::PRINT_STDOUT, - &write::PRINT_WITH_NEWLINE, - &write::USE_DEBUG, - &write::WRITELN_EMPTY_STRING, - &write::WRITE_LITERAL, - &write::WRITE_WITH_NEWLINE, - &zero_div_zero::ZERO_DIVIDED_BY_ZERO, - &zero_sized_map_values::ZERO_SIZED_MAP_VALUES, + utils::internal_lints::UNNECESSARY_SYMBOL_STR, + absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, + approx_const::APPROX_CONSTANT, + 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, + async_yields_async::ASYNC_YIELDS_ASYNC, + atomic_ordering::INVALID_ATOMIC_ORDERING, + attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, + attrs::DEPRECATED_CFG_ATTR, + attrs::DEPRECATED_SEMVER, + attrs::EMPTY_LINE_AFTER_OUTER_ATTR, + attrs::INLINE_ALWAYS, + attrs::MISMATCHED_TARGET_OS, + attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_LOCK, + await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, + bit_mask::BAD_BIT_MASK, + bit_mask::INEFFECTIVE_BIT_MASK, + bit_mask::VERBOSE_BIT_MASK, + blacklisted_name::BLACKLISTED_NAME, + blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, + booleans::LOGIC_BUG, + booleans::NONMINIMAL_BOOL, + bytecount::NAIVE_BYTECOUNT, + cargo_common_metadata::CARGO_COMMON_METADATA, + case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_LOSSLESS, + casts::CAST_POSSIBLE_TRUNCATION, + casts::CAST_POSSIBLE_WRAP, + casts::CAST_PRECISION_LOSS, + casts::CAST_PTR_ALIGNMENT, + casts::CAST_REF_TO_MUT, + casts::CAST_SIGN_LOSS, + casts::CHAR_LIT_AS_U8, + casts::FN_TO_NUMERIC_CAST, + casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + casts::PTR_AS_PTR, + casts::UNNECESSARY_CAST, + checked_conversions::CHECKED_CONVERSIONS, + cognitive_complexity::COGNITIVE_COMPLEXITY, + collapsible_if::COLLAPSIBLE_ELSE_IF, + collapsible_if::COLLAPSIBLE_IF, + collapsible_match::COLLAPSIBLE_MATCH, + comparison_chain::COMPARISON_CHAIN, + copies::BRANCHES_SHARING_CODE, + copies::IFS_SAME_COND, + copies::IF_SAME_THEN_ELSE, + copies::SAME_FUNCTIONS_IN_IF_CONDITION, + copy_iterator::COPY_ITERATOR, + create_dir::CREATE_DIR, + dbg_macro::DBG_MACRO, + default::DEFAULT_TRAIT_ACCESS, + default::FIELD_REASSIGN_WITH_DEFAULT, + default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, + 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, + disallowed_method::DISALLOWED_METHOD, + doc::DOC_MARKDOWN, + doc::MISSING_ERRORS_DOC, + doc::MISSING_PANICS_DOC, + doc::MISSING_SAFETY_DOC, + doc::NEEDLESS_DOCTEST_MAIN, + double_comparison::DOUBLE_COMPARISONS, + double_parens::DOUBLE_PARENS, + drop_forget_ref::DROP_COPY, + drop_forget_ref::DROP_REF, + drop_forget_ref::FORGET_COPY, + drop_forget_ref::FORGET_REF, + duration_subsec::DURATION_SUBSEC, + else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_enum::EMPTY_ENUM, + entry::MAP_ENTRY, + enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, + enum_variants::ENUM_VARIANT_NAMES, + enum_variants::MODULE_INCEPTION, + enum_variants::MODULE_NAME_REPETITIONS, + enum_variants::PUB_ENUM_VARIANT_NAMES, + eq_op::EQ_OP, + eq_op::OP_REF, + erasing_op::ERASING_OP, + escape::BOXED_LOCAL, + eta_reduction::REDUNDANT_CLOSURE, + eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + eval_order_dependence::DIVERGING_SUB_EXPRESSION, + eval_order_dependence::EVAL_ORDER_DEPENDENCE, + excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, + excessive_bools::STRUCT_EXCESSIVE_BOOLS, + exhaustive_items::EXHAUSTIVE_ENUMS, + exhaustive_items::EXHAUSTIVE_STRUCTS, + 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, + floating_point_arithmetic::SUBOPTIMAL_FLOPS, + format::USELESS_FORMAT, + formatting::POSSIBLE_MISSING_COMMA, + formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, + formatting::SUSPICIOUS_ELSE_FORMATTING, + formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + from_over_into::FROM_OVER_INTO, + from_str_radix_10::FROM_STR_RADIX_10, + functions::DOUBLE_MUST_USE, + 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, + get_last_with_len::GET_LAST_WITH_LEN, + identity_op::IDENTITY_OP, + if_let_mutex::IF_LET_MUTEX, + if_let_some_result::IF_LET_SOME_RESULT, + if_not_else::IF_NOT_ELSE, + if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, + implicit_hasher::IMPLICIT_HASHER, + implicit_return::IMPLICIT_RETURN, + implicit_saturating_sub::IMPLICIT_SATURATING_SUB, + inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, + indexing_slicing::INDEXING_SLICING, + indexing_slicing::OUT_OF_BOUNDS_INDEXING, + infinite_iter::INFINITE_ITER, + infinite_iter::MAYBE_INFINITE_ITER, + inherent_impl::MULTIPLE_INHERENT_IMPL, + inherent_to_string::INHERENT_TO_STRING, + inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, + inline_fn_without_body::INLINE_FN_WITHOUT_BODY, + int_plus_one::INT_PLUS_ONE, + integer_division::INTEGER_DIVISION, + invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, + items_after_statements::ITEMS_AFTER_STATEMENTS, + 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, + let_underscore::LET_UNDERSCORE_DROP, + let_underscore::LET_UNDERSCORE_LOCK, + let_underscore::LET_UNDERSCORE_MUST_USE, + lifetimes::EXTRA_UNUSED_LIFETIMES, + lifetimes::NEEDLESS_LIFETIMES, + literal_representation::DECIMAL_LITERAL_REPRESENTATION, + literal_representation::INCONSISTENT_DIGIT_GROUPING, + literal_representation::LARGE_DIGIT_GROUPS, + literal_representation::MISTYPED_LITERAL_SUFFIXES, + literal_representation::UNREADABLE_LITERAL, + literal_representation::UNUSUAL_BYTE_GROUPINGS, + loops::EMPTY_LOOP, + loops::EXPLICIT_COUNTER_LOOP, + loops::EXPLICIT_INTO_ITER_LOOP, + loops::EXPLICIT_ITER_LOOP, + loops::FOR_KV_MAP, + loops::FOR_LOOPS_OVER_FALLIBLES, + loops::ITER_NEXT_LOOP, + loops::MANUAL_FLATTEN, + loops::MANUAL_MEMCPY, + loops::MUT_RANGE_BOUND, + loops::NEEDLESS_COLLECT, + 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, + macro_use::MACRO_USE_IMPORTS, + main_recursion::MAIN_RECURSION, + manual_async_fn::MANUAL_ASYNC_FN, + manual_map::MANUAL_MAP, + manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + manual_ok_or::MANUAL_OK_OR, + manual_strip::MANUAL_STRIP, + manual_unwrap_or::MANUAL_UNWRAP_OR, + map_clone::MAP_CLONE, + map_err_ignore::MAP_ERR_IGNORE, + 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, + 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_SAME_ARMS, + 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, + matches::WILDCARD_ENUM_MATCH_ARM, + matches::WILDCARD_IN_OR_PATTERNS, + mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, + mem_forget::MEM_FORGET, + 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::BYTES_NTH, + methods::CHARS_LAST_CMP, + methods::CHARS_NEXT_CMP, + methods::CLONE_DOUBLE_REF, + 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_IDENTITY, + methods::FILTER_MAP_NEXT, + methods::FILTER_NEXT, + methods::FLAT_MAP_IDENTITY, + methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_UNWRAP, + methods::IMPLICIT_CLONE, + methods::INEFFICIENT_TO_STRING, + methods::INSPECT_FOR_EACH, + methods::INTO_ITER_ON_REF, + methods::ITERATOR_STEP_BY_ZERO, + methods::ITER_CLONED_COLLECT, + methods::ITER_COUNT, + methods::ITER_NEXT_SLICE, + methods::ITER_NTH, + methods::ITER_NTH_ZERO, + methods::ITER_SKIP_NEXT, + methods::MANUAL_FILTER_MAP, + methods::MANUAL_FIND_MAP, + methods::MANUAL_SATURATING_ARITHMETIC, + methods::MAP_COLLECT_RESULT_UNIT, + methods::MAP_FLATTEN, + methods::MAP_UNWRAP_OR, + methods::NEW_RET_NO_SELF, + methods::OK_EXPECT, + methods::OPTION_AS_REF_DEREF, + methods::OPTION_FILTER_MAP, + methods::OPTION_MAP_OR_NONE, + methods::OR_FUN_CALL, + methods::RESULT_MAP_OR_INTO_OPTION, + methods::SEARCH_IS_SOME, + methods::SHOULD_IMPLEMENT_TRAIT, + methods::SINGLE_CHAR_ADD_STR, + methods::SINGLE_CHAR_PATTERN, + methods::SKIP_WHILE_NEXT, + methods::STRING_EXTEND_CHARS, + methods::SUSPICIOUS_MAP, + methods::UNINIT_ASSUMED_INIT, + methods::UNNECESSARY_FILTER_MAP, + methods::UNNECESSARY_FOLD, + methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNWRAP_USED, + methods::USELESS_ASREF, + methods::WRONG_PUB_SELF_CONVENTION, + methods::WRONG_SELF_CONVENTION, + methods::ZST_OFFSET, + minmax::MIN_MAX, + misc::CMP_NAN, + misc::CMP_OWNED, + misc::FLOAT_CMP, + misc::FLOAT_CMP_CONST, + misc::MODULO_ONE, + misc::SHORT_CIRCUIT_STATEMENT, + misc::TOPLEVEL_REF_ARG, + misc::USED_UNDERSCORE_BINDING, + misc::ZERO_PTR, + misc_early::BUILTIN_TYPE_SHADOW, + misc_early::DOUBLE_NEG, + misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, + misc_early::MIXED_CASE_HEX_LITERALS, + misc_early::REDUNDANT_PATTERN, + misc_early::UNNEEDED_FIELD_PATTERN, + misc_early::UNNEEDED_WILDCARD_PATTERN, + misc_early::UNSEPARATED_LITERAL_SUFFIX, + misc_early::ZERO_PREFIXED_LITERAL, + missing_const_for_fn::MISSING_CONST_FOR_FN, + missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, + missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + modulo_arithmetic::MODULO_ARITHMETIC, + 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, + 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_for_each::NEEDLESS_FOR_EACH, + needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + needless_question_mark::NEEDLESS_QUESTION_MARK, + needless_update::NEEDLESS_UPDATE, + neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, + neg_multiply::NEG_MULTIPLY, + new_without_default::NEW_WITHOUT_DEFAULT, + no_effect::NO_EFFECT, + no_effect::UNNECESSARY_OPERATION, + non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, + non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, + non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, + non_expressive_names::MANY_SINGLE_CHAR_NAMES, + non_expressive_names::SIMILAR_NAMES, + non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, + 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_in_result_fn::PANIC_IN_RESULT_FN, + panic_unimplemented::PANIC, + panic_unimplemented::TODO, + 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, + 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::MANUAL_RANGE_CONTAINS, + ranges::RANGE_MINUS_ONE, + ranges::RANGE_PLUS_ONE, + ranges::RANGE_ZIP_WITH_LEN, + ranges::REVERSED_EMPTY_RANGES, + redundant_clone::REDUNDANT_CLONE, + redundant_closure_call::REDUNDANT_CLOSURE_CALL, + redundant_else::REDUNDANT_ELSE, + redundant_field_names::REDUNDANT_FIELD_NAMES, + redundant_pub_crate::REDUNDANT_PUB_CRATE, + redundant_slicing::REDUNDANT_SLICING, + redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + ref_option_ref::REF_OPTION_REF, + reference::DEREF_ADDROF, + reference::REF_IN_DEREF, + regex::INVALID_REGEX, + regex::TRIVIAL_REGEX, + repeat_once::REPEAT_ONCE, + returns::LET_AND_RETURN, + returns::NEEDLESS_RETURN, + self_assignment::SELF_ASSIGNMENT, + semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, + serde_api::SERDE_API_MISUSE, + shadow::SHADOW_REUSE, + shadow::SHADOW_SAME, + shadow::SHADOW_UNRELATED, + single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, + size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, + slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + stable_sort_primitive::STABLE_SORT_PRIMITIVE, + strings::STRING_ADD, + strings::STRING_ADD_ASSIGN, + strings::STRING_FROM_UTF8_AS_BYTES, + strings::STRING_LIT_AS_BYTES, + strings::STRING_TO_STRING, + strings::STR_TO_STRING, + suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, + suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, + suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, + swap::ALMOST_SWAPPED, + swap::MANUAL_SWAP, + 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, + transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + transmute::TRANSMUTE_BYTES_TO_STR, + transmute::TRANSMUTE_FLOAT_TO_INT, + transmute::TRANSMUTE_INT_TO_BOOL, + transmute::TRANSMUTE_INT_TO_CHAR, + transmute::TRANSMUTE_INT_TO_FLOAT, + transmute::TRANSMUTE_PTR_TO_PTR, + transmute::TRANSMUTE_PTR_TO_REF, + transmute::UNSOUND_COLLECTION_TRANSMUTE, + transmute::USELESS_TRANSMUTE, + transmute::WRONG_TRANSMUTE, + transmuting_null::TRANSMUTING_NULL, + try_err::TRY_ERR, + types::BORROWED_BOX, + types::BOX_VEC, + types::LINKEDLIST, + types::OPTION_OPTION, + types::RC_BUFFER, + types::REDUNDANT_ALLOCATION, + types::TYPE_COMPLEXITY, + types::VEC_BOX, + undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, + unicode::INVISIBLE_CHARACTERS, + unicode::NON_ASCII_LITERAL, + unicode::UNICODE_NOT_NFC, + unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, + unit_types::LET_UNIT_VALUE, + unit_types::UNIT_ARG, + unit_types::UNIT_CMP, + unnamed_address::FN_ADDRESS_COMPARISONS, + unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_sort_by::UNNECESSARY_SORT_BY, + unnecessary_wraps::UNNECESSARY_WRAPS, + unnested_or_patterns::UNNESTED_OR_PATTERNS, + 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, + unwrap_in_result::UNWRAP_IN_RESULT, + upper_case_acronyms::UPPER_CASE_ACRONYMS, + use_self::USE_SELF, + useless_conversion::USELESS_CONVERSION, + vec::USELESS_VEC, + vec_init_then_push::VEC_INIT_THEN_PUSH, + vec_resize_to_zero::VEC_RESIZE_TO_ZERO, + verbose_file_reads::VERBOSE_FILE_READS, + wildcard_dependencies::WILDCARD_DEPENDENCIES, + wildcard_imports::ENUM_GLOB_USE, + wildcard_imports::WILDCARD_IMPORTS, + write::PRINTLN_EMPTY_STRING, + write::PRINT_LITERAL, + write::PRINT_STDERR, + write::PRINT_STDOUT, + write::PRINT_WITH_NEWLINE, + write::USE_DEBUG, + write::WRITELN_EMPTY_STRING, + write::WRITE_LITERAL, + write::WRITE_WITH_NEWLINE, + zero_div_zero::ZERO_DIVIDED_BY_ZERO, + zero_sized_map_values::ZERO_SIZED_MAP_VALUES, ]); // end register lints, do not remove this comment, it’s used in `update_lints` @@ -1295,792 +1295,792 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); 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(&default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(&exit::EXIT), - LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(&if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), - LintId::of(&implicit_return::IMPLICIT_RETURN), - LintId::of(&indexing_slicing::INDEXING_SLICING), - LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(&integer_division::INTEGER_DIVISION), - LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), - LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(&mem_forget::MEM_FORGET), - 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::UNWRAP_USED), - LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), - LintId::of(&misc::FLOAT_CMP_CONST), - LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), - 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_fn::PANIC_IN_RESULT_FN), - LintId::of(&panic_unimplemented::PANIC), - 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(&semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(&shadow::SHADOW_REUSE), - LintId::of(&shadow::SHADOW_SAME), - LintId::of(&strings::STRING_ADD), - LintId::of(&strings::STRING_TO_STRING), - LintId::of(&strings::STR_TO_STRING), - LintId::of(&types::RC_BUFFER), - LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), - LintId::of(&write::PRINT_STDERR), - LintId::of(&write::PRINT_STDOUT), - LintId::of(&write::USE_DEBUG), + 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(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), + LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), + LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), + LintId::of(exit::EXIT), + LintId::of(float_literal::LOSSY_FLOAT_LITERAL), + LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), + LintId::of(implicit_return::IMPLICIT_RETURN), + LintId::of(indexing_slicing::INDEXING_SLICING), + LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), + LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), + LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(map_err_ignore::MAP_ERR_IGNORE), + LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), + LintId::of(mem_forget::MEM_FORGET), + 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::UNWRAP_USED), + LintId::of(methods::WRONG_PUB_SELF_CONVENTION), + LintId::of(misc::FLOAT_CMP_CONST), + LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), + 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_fn::PANIC_IN_RESULT_FN), + LintId::of(panic_unimplemented::PANIC), + 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(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(shadow::SHADOW_REUSE), + LintId::of(shadow::SHADOW_SAME), + LintId::of(strings::STRING_ADD), + LintId::of(strings::STRING_TO_STRING), + LintId::of(strings::STR_TO_STRING), + LintId::of(types::RC_BUFFER), + LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), + LintId::of(verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(write::PRINT_STDERR), + LintId::of(write::PRINT_STDOUT), + LintId::of(write::USE_DEBUG), ]); 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(&bytecount::NAIVE_BYTECOUNT), - LintId::of(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(&casts::CAST_LOSSLESS), - LintId::of(&casts::CAST_POSSIBLE_TRUNCATION), - LintId::of(&casts::CAST_POSSIBLE_WRAP), - LintId::of(&casts::CAST_PRECISION_LOSS), - LintId::of(&casts::CAST_PTR_ALIGNMENT), - LintId::of(&casts::CAST_SIGN_LOSS), - LintId::of(&casts::PTR_AS_PTR), - LintId::of(&checked_conversions::CHECKED_CONVERSIONS), - LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(©_iterator::COPY_ITERATOR), - LintId::of(&default::DEFAULT_TRAIT_ACCESS), - LintId::of(&dereference::EXPLICIT_DEREF_METHODS), - LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(&doc::DOC_MARKDOWN), - LintId::of(&doc::MISSING_ERRORS_DOC), - LintId::of(&doc::MISSING_PANICS_DOC), - LintId::of(&empty_enum::EMPTY_ENUM), - LintId::of(&enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES), - LintId::of(&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(&functions::MUST_USE_CANDIDATE), - LintId::of(&functions::TOO_MANY_LINES), - LintId::of(&if_not_else::IF_NOT_ELSE), - LintId::of(&implicit_hasher::IMPLICIT_HASHER), - LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(&invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(&let_underscore::LET_UNDERSCORE_DROP), - LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(&literal_representation::UNREADABLE_LITERAL), - LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(&loops::EXPLICIT_ITER_LOOP), - LintId::of(¯o_use::MACRO_USE_IMPORTS), - LintId::of(&manual_ok_or::MANUAL_OK_OR), - 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), - LintId::of(&methods::FILTER_MAP), - LintId::of(&methods::FILTER_MAP_NEXT), - LintId::of(&methods::IMPLICIT_CLONE), - LintId::of(&methods::INEFFICIENT_TO_STRING), - LintId::of(&methods::MAP_FLATTEN), - 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), - LintId::of(&needless_continue::NEEDLESS_CONTINUE), - LintId::of(&needless_for_each::NEEDLESS_FOR_EACH), - 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(&redundant_else::REDUNDANT_ELSE), - LintId::of(&ref_option_ref::REF_OPTION_REF), - LintId::of(&shadow::SHADOW_UNRELATED), - LintId::of(&strings::STRING_ADD_ASSIGN), - LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(&types::LINKEDLIST), - LintId::of(&types::OPTION_OPTION), - LintId::of(&unicode::NON_ASCII_LITERAL), - LintId::of(&unicode::UNICODE_NOT_NFC), - LintId::of(&unit_types::LET_UNIT_VALUE), - LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), - 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), - LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), + 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(bytecount::NAIVE_BYTECOUNT), + LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), + LintId::of(casts::CAST_LOSSLESS), + LintId::of(casts::CAST_POSSIBLE_TRUNCATION), + LintId::of(casts::CAST_POSSIBLE_WRAP), + LintId::of(casts::CAST_PRECISION_LOSS), + LintId::of(casts::CAST_PTR_ALIGNMENT), + LintId::of(casts::CAST_SIGN_LOSS), + LintId::of(casts::PTR_AS_PTR), + LintId::of(checked_conversions::CHECKED_CONVERSIONS), + LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(copy_iterator::COPY_ITERATOR), + LintId::of(default::DEFAULT_TRAIT_ACCESS), + LintId::of(dereference::EXPLICIT_DEREF_METHODS), + LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), + LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_MARKDOWN), + LintId::of(doc::MISSING_ERRORS_DOC), + LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(empty_enum::EMPTY_ENUM), + LintId::of(enum_variants::MODULE_NAME_REPETITIONS), + LintId::of(enum_variants::PUB_ENUM_VARIANT_NAMES), + LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), + LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), + LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(functions::MUST_USE_CANDIDATE), + LintId::of(functions::TOO_MANY_LINES), + LintId::of(if_not_else::IF_NOT_ELSE), + LintId::of(implicit_hasher::IMPLICIT_HASHER), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), + LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), + LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(let_underscore::LET_UNDERSCORE_DROP), + LintId::of(literal_representation::LARGE_DIGIT_GROUPS), + LintId::of(literal_representation::UNREADABLE_LITERAL), + LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), + LintId::of(loops::EXPLICIT_ITER_LOOP), + LintId::of(macro_use::MACRO_USE_IMPORTS), + LintId::of(manual_ok_or::MANUAL_OK_OR), + 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), + LintId::of(methods::FILTER_MAP), + LintId::of(methods::FILTER_MAP_NEXT), + LintId::of(methods::IMPLICIT_CLONE), + LintId::of(methods::INEFFICIENT_TO_STRING), + LintId::of(methods::MAP_FLATTEN), + 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), + LintId::of(needless_continue::NEEDLESS_CONTINUE), + LintId::of(needless_for_each::NEEDLESS_FOR_EACH), + 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(redundant_else::REDUNDANT_ELSE), + LintId::of(ref_option_ref::REF_OPTION_REF), + LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(strings::STRING_ADD_ASSIGN), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), + LintId::of(types::LINKEDLIST), + LintId::of(types::OPTION_OPTION), + LintId::of(unicode::NON_ASCII_LITERAL), + LintId::of(unicode::UNICODE_NOT_NFC), + LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), + 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), + LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), ]); #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(&utils::internal_lints::DEFAULT_LINT), - LintId::of(&utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(&utils::internal_lints::INVALID_PATHS), - LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), - LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(&utils::internal_lints::PRODUCE_ICE), - LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), + 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::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::INVALID_PATHS), + LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), + LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ - LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), - LintId::of(&approx_const::APPROX_CONSTANT), - 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), - LintId::of(&attrs::DEPRECATED_SEMVER), - LintId::of(&attrs::MISMATCHED_TARGET_OS), - LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&bit_mask::BAD_BIT_MASK), - LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(&booleans::LOGIC_BUG), - LintId::of(&booleans::NONMINIMAL_BOOL), - LintId::of(&casts::CAST_REF_TO_MUT), - LintId::of(&casts::CHAR_LIT_AS_U8), - LintId::of(&casts::FN_TO_NUMERIC_CAST), - LintId::of(&casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&casts::UNNECESSARY_CAST), - LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(&collapsible_if::COLLAPSIBLE_IF), - LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), - LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&copies::BRANCHES_SHARING_CODE), - LintId::of(&copies::IFS_SAME_COND), - LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(&doc::MISSING_SAFETY_DOC), - LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(&double_comparison::DOUBLE_COMPARISONS), - LintId::of(&double_parens::DOUBLE_PARENS), - LintId::of(&drop_forget_ref::DROP_COPY), - LintId::of(&drop_forget_ref::DROP_REF), - LintId::of(&drop_forget_ref::FORGET_COPY), - LintId::of(&drop_forget_ref::FORGET_REF), - LintId::of(&duration_subsec::DURATION_SUBSEC), - LintId::of(&entry::MAP_ENTRY), - LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(&enum_variants::ENUM_VARIANT_NAMES), - LintId::of(&enum_variants::MODULE_INCEPTION), - LintId::of(&eq_op::EQ_OP), - LintId::of(&eq_op::OP_REF), - LintId::of(&erasing_op::ERASING_OP), - LintId::of(&escape::BOXED_LOCAL), - LintId::of(&eta_reduction::REDUNDANT_CLOSURE), - 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), - LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(&from_over_into::FROM_OVER_INTO), - LintId::of(&from_str_radix_10::FROM_STR_RADIX_10), - 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), - LintId::of(&if_let_mutex::IF_LET_MUTEX), - LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(&infinite_iter::INFINITE_ITER), - LintId::of(&inherent_to_string::INHERENT_TO_STRING), - LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - 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), - LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(&lifetimes::NEEDLESS_LIFETIMES), - LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(&loops::EMPTY_LOOP), - LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::ITER_NEXT_LOOP), - LintId::of(&loops::MANUAL_FLATTEN), - LintId::of(&loops::MANUAL_MEMCPY), - LintId::of(&loops::MUT_RANGE_BOUND), - 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::SINGLE_ELEMENT_LOOP), - LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), - 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_map::MANUAL_MAP), - 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), - 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), - 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::BYTES_NTH), - LintId::of(&methods::CHARS_LAST_CMP), - LintId::of(&methods::CHARS_NEXT_CMP), - LintId::of(&methods::CLONE_DOUBLE_REF), - LintId::of(&methods::CLONE_ON_COPY), - LintId::of(&methods::EXPECT_FUN_CALL), - LintId::of(&methods::FILTER_MAP_IDENTITY), - LintId::of(&methods::FILTER_NEXT), - LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(&methods::INSPECT_FOR_EACH), - LintId::of(&methods::INTO_ITER_ON_REF), - LintId::of(&methods::ITERATOR_STEP_BY_ZERO), - LintId::of(&methods::ITER_CLONED_COLLECT), - LintId::of(&methods::ITER_COUNT), - LintId::of(&methods::ITER_NEXT_SLICE), - LintId::of(&methods::ITER_NTH), - LintId::of(&methods::ITER_NTH_ZERO), - LintId::of(&methods::ITER_SKIP_NEXT), - LintId::of(&methods::MANUAL_FILTER_MAP), - LintId::of(&methods::MANUAL_FIND_MAP), - 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), - LintId::of(&methods::OPTION_FILTER_MAP), - LintId::of(&methods::OPTION_MAP_OR_NONE), - LintId::of(&methods::OR_FUN_CALL), - LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(&methods::SEARCH_IS_SOME), - LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(&methods::SINGLE_CHAR_ADD_STR), - LintId::of(&methods::SINGLE_CHAR_PATTERN), - LintId::of(&methods::SKIP_WHILE_NEXT), - LintId::of(&methods::STRING_EXTEND_CHARS), - LintId::of(&methods::SUSPICIOUS_MAP), - LintId::of(&methods::UNINIT_ASSUMED_INIT), - LintId::of(&methods::UNNECESSARY_FILTER_MAP), - LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(&methods::USELESS_ASREF), - LintId::of(&methods::WRONG_SELF_CONVENTION), - LintId::of(&methods::ZST_OFFSET), - LintId::of(&minmax::MIN_MAX), - LintId::of(&misc::CMP_NAN), - LintId::of(&misc::CMP_OWNED), - LintId::of(&misc::FLOAT_CMP), - LintId::of(&misc::MODULO_ONE), - LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc::TOPLEVEL_REF_ARG), - LintId::of(&misc::ZERO_PTR), - LintId::of(&misc_early::BUILTIN_TYPE_SHADOW), - 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_PATTERN), - 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), - LintId::of(&needless_bool::BOOL_COMPARISON), - LintId::of(&needless_bool::NEEDLESS_BOOL), - LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), - 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), - LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(&no_effect::NO_EFFECT), - LintId::of(&no_effect::UNNECESSARY_OPERATION), - 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(&non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), - LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(&precedence::PRECEDENCE), - 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::MANUAL_RANGE_CONTAINS), - 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_slicing::REDUNDANT_SLICING), - LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&reference::DEREF_ADDROF), - LintId::of(&reference::REF_IN_DEREF), - LintId::of(®ex::INVALID_REGEX), - 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(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), - LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(&swap::ALMOST_SWAPPED), - LintId::of(&swap::MANUAL_SWAP), - 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), - LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(&transmute::WRONG_TRANSMUTE), - LintId::of(&transmuting_null::TRANSMUTING_NULL), - LintId::of(&try_err::TRY_ERR), - LintId::of(&types::BORROWED_BOX), - LintId::of(&types::BOX_VEC), - LintId::of(&types::REDUNDANT_ALLOCATION), - LintId::of(&types::TYPE_COMPLEXITY), - 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(&unit_types::UNIT_ARG), - LintId::of(&unit_types::UNIT_CMP), - 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(&unused_unit::UNUSED_UNIT), - LintId::of(&unwrap::PANICKING_UNWRAP), - LintId::of(&unwrap::UNNECESSARY_UNWRAP), - LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(&useless_conversion::USELESS_CONVERSION), - LintId::of(&vec::USELESS_VEC), - LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), - 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), - LintId::of(&write::WRITELN_EMPTY_STRING), - LintId::of(&write::WRITE_LITERAL), - LintId::of(&write::WRITE_WITH_NEWLINE), - LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(approx_const::APPROX_CONSTANT), + 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), + LintId::of(attrs::DEPRECATED_SEMVER), + LintId::of(attrs::MISMATCHED_TARGET_OS), + LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(bit_mask::BAD_BIT_MASK), + LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(booleans::LOGIC_BUG), + LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(casts::CHAR_LIT_AS_U8), + LintId::of(casts::FN_TO_NUMERIC_CAST), + LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(casts::UNNECESSARY_CAST), + LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), + LintId::of(collapsible_if::COLLAPSIBLE_IF), + LintId::of(collapsible_match::COLLAPSIBLE_MATCH), + LintId::of(comparison_chain::COMPARISON_CHAIN), + LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(derive::DERIVE_HASH_XOR_EQ), + LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(doc::MISSING_SAFETY_DOC), + LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(double_comparison::DOUBLE_COMPARISONS), + LintId::of(double_parens::DOUBLE_PARENS), + LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_REF), + LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(entry::MAP_ENTRY), + LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(enum_variants::ENUM_VARIANT_NAMES), + LintId::of(enum_variants::MODULE_INCEPTION), + LintId::of(eq_op::EQ_OP), + LintId::of(eq_op::OP_REF), + LintId::of(erasing_op::ERASING_OP), + LintId::of(escape::BOXED_LOCAL), + LintId::of(eta_reduction::REDUNDANT_CLOSURE), + 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), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(from_over_into::FROM_OVER_INTO), + LintId::of(from_str_radix_10::FROM_STR_RADIX_10), + 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), + LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(infinite_iter::INFINITE_ITER), + LintId::of(inherent_to_string::INHERENT_TO_STRING), + LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + 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), + LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(lifetimes::NEEDLESS_LIFETIMES), + LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), + LintId::of(loops::EMPTY_LOOP), + LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::FOR_KV_MAP), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::MANUAL_FLATTEN), + LintId::of(loops::MANUAL_MEMCPY), + LintId::of(loops::MUT_RANGE_BOUND), + 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::SINGLE_ELEMENT_LOOP), + LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + 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_map::MANUAL_MAP), + 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), + 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), + 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::BYTES_NTH), + LintId::of(methods::CHARS_LAST_CMP), + LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::CLONE_DOUBLE_REF), + LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::FILTER_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), + LintId::of(methods::INSPECT_FOR_EACH), + LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::ITER_CLONED_COLLECT), + LintId::of(methods::ITER_COUNT), + LintId::of(methods::ITER_NEXT_SLICE), + LintId::of(methods::ITER_NTH), + LintId::of(methods::ITER_NTH_ZERO), + LintId::of(methods::ITER_SKIP_NEXT), + LintId::of(methods::MANUAL_FILTER_MAP), + LintId::of(methods::MANUAL_FIND_MAP), + 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), + LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::OPTION_MAP_OR_NONE), + LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(methods::SEARCH_IS_SOME), + LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(methods::SINGLE_CHAR_ADD_STR), + LintId::of(methods::SINGLE_CHAR_PATTERN), + LintId::of(methods::SKIP_WHILE_NEXT), + LintId::of(methods::STRING_EXTEND_CHARS), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::USELESS_ASREF), + LintId::of(methods::WRONG_SELF_CONVENTION), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::CMP_OWNED), + LintId::of(misc::FLOAT_CMP), + LintId::of(misc::MODULO_ONE), + LintId::of(misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(misc::TOPLEVEL_REF_ARG), + LintId::of(misc::ZERO_PTR), + LintId::of(misc_early::BUILTIN_TYPE_SHADOW), + 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_PATTERN), + 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), + LintId::of(needless_bool::BOOL_COMPARISON), + LintId::of(needless_bool::NEEDLESS_BOOL), + LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), + 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), + LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(no_effect::NO_EFFECT), + LintId::of(no_effect::UNNECESSARY_OPERATION), + 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(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(precedence::PRECEDENCE), + 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::MANUAL_RANGE_CONTAINS), + 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_slicing::REDUNDANT_SLICING), + LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(reference::DEREF_ADDROF), + LintId::of(reference::REF_IN_DEREF), + LintId::of(regex::INVALID_REGEX), + 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(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), + LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), + LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap::ALMOST_SWAPPED), + LintId::of(swap::MANUAL_SWAP), + 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), + LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::WRONG_TRANSMUTE), + LintId::of(transmuting_null::TRANSMUTING_NULL), + LintId::of(try_err::TRY_ERR), + LintId::of(types::BORROWED_BOX), + LintId::of(types::BOX_VEC), + LintId::of(types::REDUNDANT_ALLOCATION), + LintId::of(types::TYPE_COMPLEXITY), + 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(unit_types::UNIT_ARG), + LintId::of(unit_types::UNIT_CMP), + 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(unused_unit::UNUSED_UNIT), + LintId::of(unwrap::PANICKING_UNWRAP), + LintId::of(unwrap::UNNECESSARY_UNWRAP), + LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), + LintId::of(useless_conversion::USELESS_CONVERSION), + LintId::of(vec::USELESS_VEC), + LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), + 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), + LintId::of(write::WRITELN_EMPTY_STRING), + LintId::of(write::WRITE_LITERAL), + LintId::of(write::WRITE_WITH_NEWLINE), + LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); 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(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(&casts::FN_TO_NUMERIC_CAST), - LintId::of(&casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(&collapsible_if::COLLAPSIBLE_IF), - LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), - LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(&doc::MISSING_SAFETY_DOC), - LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(&enum_variants::ENUM_VARIANT_NAMES), - LintId::of(&enum_variants::MODULE_INCEPTION), - LintId::of(&eq_op::OP_REF), - LintId::of(&eta_reduction::REDUNDANT_CLOSURE), - LintId::of(&float_literal::EXCESSIVE_PRECISION), - LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(&from_over_into::FROM_OVER_INTO), - LintId::of(&from_str_radix_10::FROM_STR_RADIX_10), - 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(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - 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), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), - 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), - LintId::of(&manual_map::MANUAL_MAP), - 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), - LintId::of(&methods::BYTES_NTH), - LintId::of(&methods::CHARS_LAST_CMP), - LintId::of(&methods::CHARS_NEXT_CMP), - LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(&methods::INTO_ITER_ON_REF), - LintId::of(&methods::ITER_CLONED_COLLECT), - LintId::of(&methods::ITER_NEXT_SLICE), - 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), - LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(&methods::SINGLE_CHAR_ADD_STR), - LintId::of(&methods::STRING_EXTEND_CHARS), - LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(&methods::WRONG_SELF_CONVENTION), - LintId::of(&misc::TOPLEVEL_REF_ARG), - LintId::of(&misc::ZERO_PTR), - LintId::of(&misc_early::BUILTIN_TYPE_SHADOW), - 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_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), - 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(&ptr::CMP_NULL), - 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(&returns::LET_AND_RETURN), - LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(&try_err::TRY_ERR), - LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&unused_unit::UNUSED_UNIT), - LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(&write::PRINTLN_EMPTY_STRING), - LintId::of(&write::PRINT_LITERAL), - LintId::of(&write::PRINT_WITH_NEWLINE), - LintId::of(&write::WRITELN_EMPTY_STRING), - LintId::of(&write::WRITE_LITERAL), - LintId::of(&write::WRITE_WITH_NEWLINE), + LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assign_ops::ASSIGN_OP_PATTERN), + LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(casts::FN_TO_NUMERIC_CAST), + LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), + LintId::of(collapsible_if::COLLAPSIBLE_IF), + LintId::of(collapsible_match::COLLAPSIBLE_MATCH), + LintId::of(comparison_chain::COMPARISON_CHAIN), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(doc::MISSING_SAFETY_DOC), + LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(enum_variants::ENUM_VARIANT_NAMES), + LintId::of(enum_variants::MODULE_INCEPTION), + LintId::of(eq_op::OP_REF), + LintId::of(eta_reduction::REDUNDANT_CLOSURE), + LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(from_over_into::FROM_OVER_INTO), + LintId::of(from_str_radix_10::FROM_STR_RADIX_10), + 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(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + 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), + LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), + 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), + LintId::of(manual_map::MANUAL_MAP), + 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), + LintId::of(methods::BYTES_NTH), + LintId::of(methods::CHARS_LAST_CMP), + LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), + LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::ITER_CLONED_COLLECT), + LintId::of(methods::ITER_NEXT_SLICE), + 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), + LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(methods::SINGLE_CHAR_ADD_STR), + LintId::of(methods::STRING_EXTEND_CHARS), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::WRONG_SELF_CONVENTION), + LintId::of(misc::TOPLEVEL_REF_ARG), + LintId::of(misc::ZERO_PTR), + LintId::of(misc_early::BUILTIN_TYPE_SHADOW), + 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_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), + 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(ptr::CMP_NULL), + 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(returns::LET_AND_RETURN), + LintId::of(returns::NEEDLESS_RETURN), + LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(try_err::TRY_ERR), + LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(unused_unit::UNUSED_UNIT), + LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), + LintId::of(write::PRINTLN_EMPTY_STRING), + LintId::of(write::PRINT_LITERAL), + LintId::of(write::PRINT_WITH_NEWLINE), + LintId::of(write::WRITELN_EMPTY_STRING), + LintId::of(write::WRITE_LITERAL), + LintId::of(write::WRITE_WITH_NEWLINE), ]); store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), - LintId::of(&attrs::DEPRECATED_CFG_ATTR), - LintId::of(&booleans::NONMINIMAL_BOOL), - LintId::of(&casts::CHAR_LIT_AS_U8), - LintId::of(&casts::UNNECESSARY_CAST), - LintId::of(&copies::BRANCHES_SHARING_CODE), - LintId::of(&double_comparison::DOUBLE_COMPARISONS), - LintId::of(&double_parens::DOUBLE_PARENS), - LintId::of(&duration_subsec::DURATION_SUBSEC), - LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), - LintId::of(&explicit_write::EXPLICIT_WRITE), - LintId::of(&format::USELESS_FORMAT), - LintId::of(&functions::TOO_MANY_ARGUMENTS), - LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_op::IDENTITY_OP), - LintId::of(&int_plus_one::INT_PLUS_ONE), - LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(&lifetimes::NEEDLESS_LIFETIMES), - LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::MANUAL_FLATTEN), - 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), - 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), - 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_MAP_IDENTITY), - LintId::of(&methods::FILTER_NEXT), - LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::INSPECT_FOR_EACH), - LintId::of(&methods::ITER_COUNT), - LintId::of(&methods::MANUAL_FILTER_MAP), - LintId::of(&methods::MANUAL_FIND_MAP), - LintId::of(&methods::OPTION_AS_REF_DEREF), - LintId::of(&methods::OPTION_FILTER_MAP), - LintId::of(&methods::SEARCH_IS_SOME), - LintId::of(&methods::SKIP_WHILE_NEXT), - LintId::of(&methods::SUSPICIOUS_MAP), - LintId::of(&methods::UNNECESSARY_FILTER_MAP), - LintId::of(&methods::USELESS_ASREF), - 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), - LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(&needless_update::NEEDLESS_UPDATE), - LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(&no_effect::NO_EFFECT), - LintId::of(&no_effect::UNNECESSARY_OPERATION), - LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - 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_ZIP_WITH_LEN), - LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(&redundant_slicing::REDUNDANT_SLICING), - LintId::of(&reference::DEREF_ADDROF), - LintId::of(&reference::REF_IN_DEREF), - LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(&swap::MANUAL_SWAP), - LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(&types::BORROWED_BOX), - LintId::of(&types::TYPE_COMPLEXITY), - LintId::of(&types::VEC_BOX), - LintId::of(&unit_types::UNIT_ARG), - 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), + LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(attrs::DEPRECATED_CFG_ATTR), + LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CHAR_LIT_AS_U8), + LintId::of(casts::UNNECESSARY_CAST), + LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(double_comparison::DOUBLE_COMPARISONS), + LintId::of(double_parens::DOUBLE_PARENS), + LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(explicit_write::EXPLICIT_WRITE), + LintId::of(format::USELESS_FORMAT), + LintId::of(functions::TOO_MANY_ARGUMENTS), + LintId::of(get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(identity_op::IDENTITY_OP), + LintId::of(int_plus_one::INT_PLUS_ONE), + LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(lifetimes::NEEDLESS_LIFETIMES), + LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::MANUAL_FLATTEN), + 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), + 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), + 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_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::INSPECT_FOR_EACH), + LintId::of(methods::ITER_COUNT), + LintId::of(methods::MANUAL_FILTER_MAP), + LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::OPTION_AS_REF_DEREF), + LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::SEARCH_IS_SOME), + LintId::of(methods::SKIP_WHILE_NEXT), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::USELESS_ASREF), + 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), + LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), + LintId::of(needless_update::NEEDLESS_UPDATE), + LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), + LintId::of(no_effect::NO_EFFECT), + LintId::of(no_effect::UNNECESSARY_OPERATION), + LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + 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_ZIP_WITH_LEN), + LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), + LintId::of(redundant_slicing::REDUNDANT_SLICING), + LintId::of(reference::DEREF_ADDROF), + LintId::of(reference::REF_IN_DEREF), + LintId::of(repeat_once::REPEAT_ONCE), + LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(swap::MANUAL_SWAP), + LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), + LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(types::BORROWED_BOX), + LintId::of(types::TYPE_COMPLEXITY), + LintId::of(types::VEC_BOX), + LintId::of(unit_types::UNIT_ARG), + 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), ]); store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ - LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), - 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), - LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&bit_mask::BAD_BIT_MASK), - LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&booleans::LOGIC_BUG), - LintId::of(&casts::CAST_REF_TO_MUT), - LintId::of(&copies::IFS_SAME_COND), - LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(&drop_forget_ref::DROP_COPY), - LintId::of(&drop_forget_ref::DROP_REF), - LintId::of(&drop_forget_ref::FORGET_COPY), - LintId::of(&drop_forget_ref::FORGET_REF), - 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), - LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(&infinite_iter::INFINITE_ITER), - LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::ITER_NEXT_LOOP), - LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), - LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(&methods::CLONE_DOUBLE_REF), - LintId::of(&methods::ITERATOR_STEP_BY_ZERO), - LintId::of(&methods::UNINIT_ASSUMED_INIT), - LintId::of(&methods::ZST_OFFSET), - LintId::of(&minmax::MIN_MAX), - LintId::of(&misc::CMP_NAN), - LintId::of(&misc::FLOAT_CMP), - LintId::of(&misc::MODULO_ONE), - LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - 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(&self_assignment::SELF_ASSIGNMENT), - LintId::of(&serde_api::SERDE_API_MISUSE), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(&swap::ALMOST_SWAPPED), - 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), - 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(&unit_types::UNIT_CMP), - LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), - 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), + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + 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), + LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(bit_mask::BAD_BIT_MASK), + LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(booleans::LOGIC_BUG), + LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(derive::DERIVE_HASH_XOR_EQ), + LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_REF), + LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_REF), + 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), + LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(infinite_iter::INFINITE_ITER), + LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::NEVER_LOOP), + LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(methods::CLONE_DOUBLE_REF), + LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::FLOAT_CMP), + LintId::of(misc::MODULO_ONE), + LintId::of(mut_key::MUTABLE_KEY_TYPE), + LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + 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(regex::INVALID_REGEX), + LintId::of(self_assignment::SELF_ASSIGNMENT), + LintId::of(serde_api::SERDE_API_MISUSE), + LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), + LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap::ALMOST_SWAPPED), + 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), + 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(unit_types::UNIT_CMP), + LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), + 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![ - LintId::of(&entry::MAP_ENTRY), - LintId::of(&escape::BOXED_LOCAL), - LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(&loops::MANUAL_MEMCPY), - LintId::of(&loops::NEEDLESS_COLLECT), - LintId::of(&methods::EXPECT_FUN_CALL), - LintId::of(&methods::ITER_NTH), - LintId::of(&methods::OR_FUN_CALL), - LintId::of(&methods::SINGLE_CHAR_PATTERN), - LintId::of(&misc::CMP_OWNED), - 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), - LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), + LintId::of(entry::MAP_ENTRY), + LintId::of(escape::BOXED_LOCAL), + LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(loops::MANUAL_MEMCPY), + LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::ITER_NTH), + LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::SINGLE_CHAR_PATTERN), + LintId::of(misc::CMP_OWNED), + 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), + LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), ]); store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(&cargo_common_metadata::CARGO_COMMON_METADATA), - LintId::of(&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), - LintId::of(&wildcard_dependencies::WILDCARD_DEPENDENCIES), + LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), + LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), ]); 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), - 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), - 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(®ex::TRIVIAL_REGEX), - LintId::of(&strings::STRING_LIT_AS_BYTES), - LintId::of(&transmute::USELESS_TRANSMUTE), - LintId::of(&use_self::USE_SELF), + 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), + 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), + 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(regex::TRIVIAL_REGEX), + LintId::of(strings::STRING_LIT_AS_BYTES), + LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index e3b3fa21cab..116ad072837 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -81,7 +81,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { - check_fn_inner(cx, &sig.decl, Some(id), generics, item.span, true); + check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true); } } @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id()).is_none(); check_fn_inner( cx, - &sig.decl, + sig.decl, Some(id), &item.generics, item.span, @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(_) => None, TraitFn::Provided(id) => Some(id), }; - check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true); } } } @@ -149,7 +149,7 @@ fn check_fn_inner<'tcx>( .last() .expect("a path must have at least one segment") .args; - if let Some(ref params) = *params { + if let Some(params) = *params { let lifetimes = params.args.iter().filter_map(|arg| match arg { GenericArg::Lifetime(lt) => Some(lt), _ => None, @@ -163,7 +163,7 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, &generics.params) { + if could_use_elision(cx, decl, body, generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -201,7 +201,7 @@ fn could_use_elision<'tcx>( input_visitor.visit_ty(arg); } // extract lifetimes in output type - if let Return(ref ty) = func.output { + if let Return(ty) = func.output { output_visitor.visit_ty(ty); } for lt in named_generics { @@ -416,12 +416,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl // a predicate like F: Trait or F: for<'a> Trait<'a> let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs - walk_ty(&mut visitor, &pred.bounded_ty); + walk_ty(&mut visitor, pred.bounded_ty); if !visitor.all_lts().is_empty() { return true; } // if the bounds define new lifetimes, they are fine to occur - let allowed_lts = allowed_lts_from(&pred.bound_generic_params); + let allowed_lts = allowed_lts_from(pred.bound_generic_params); // now walk the bounds for bound in pred.bounds.iter() { walk_param_bound(&mut visitor, bound); @@ -433,8 +433,8 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); - walk_ty(&mut visitor, &pred.lhs_ty); - walk_ty(&mut visitor, &pred.rhs_ty); + walk_ty(&mut visitor, pred.lhs_ty); + walk_ty(&mut visitor, pred.rhs_ty); if !visitor.lts.is_empty() { return true; } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7fd55151226..1d63af25803 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -248,7 +248,7 @@ impl LiteralDigitGrouping { fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); - if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); + if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit); then { if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { return; @@ -438,7 +438,7 @@ impl DecimalLiteralRepresentation { if_chain! { if let LitKind::Int(val, _) = lit.kind; if let Some(src) = snippet_opt(cx, lit.span); - if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit); + if let Some(num_lit) = NumericLiteral::from_lit(&src, lit); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index f14dbb1d642..98e60f7ed85 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -26,7 +26,7 @@ pub(super) fn check<'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) { + if let Some(block) = get_enclosing_block(cx, expr.hir_id) { for id in increment_visitor.into_results() { let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); walk_block(&mut initialize_visitor, block); diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 8f18f54119b..666b8c58728 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( ) { let pat_span = pat.span; - if let PatKind::Tuple(ref pat, _) = pat.kind { + if let PatKind::Tuple(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() { @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( Mutability::Mut => "_mut", }; let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr, + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, _ => arg, }; diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 574ad8c0f29..94743cfcf46 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, span: Span, ) { - if let ExprKind::Block(ref block, _) = body.kind { + if let ExprKind::Block(block, _) = body.kind { // Ensure the `if let` statement is the only expression or statement in the for-loop let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { let match_stmt = &block.stmts[0]; @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; if let ExprKind::Match( - ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } ) = inner_expr.kind; // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 3c5abb7a3c4..47005aba388 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -63,8 +63,8 @@ pub(super) fn check<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - 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); + 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 path_to_local(base_left) != path_to_local(base_right); @@ -168,8 +168,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_offset, dst_limit) = print_offset_and_limit(dst); + let (src_offset, src_limit) = print_offset_and_limit(src); let dst_base_str = snippet(cx, dst.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???"); @@ -435,7 +435,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. - get_enclosing_block(&cx, expr.hir_id).and_then(|block| { + get_enclosing_block(cx, expr.hir_id).and_then(|block| { increment_visitor .into_results() .filter_map(move |var_id| { diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 20291491998..28acefd51fe 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -562,7 +562,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) - if let ExprKind::Loop(ref block, _, LoopSource::Loop, _) = expr.kind { + if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); while_let_loop::check(cx, expr, block); @@ -570,7 +570,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some((cond, body)) = higher::while_loop(&expr) { + if let Some((cond, body)) = higher::while_loop(expr) { while_immutable_condition::check(cx, cond, body); } @@ -602,7 +602,7 @@ fn check_for_loop<'tcx>( fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - if let ExprKind::MethodCall(ref method, _, ref args, _) = arg.kind { + if let ExprKind::MethodCall(method, _, args, _) = arg.kind { // just the receiver, no arguments if args.len() == 1 { let method_name = &*method.ident.as_str(); diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index e0c5caf5136..4d73aef76e8 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -21,10 +21,10 @@ pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } 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, method0_span, _, _) = args[0].kind; + if let ExprKind::MethodCall(method, _, args, _) = expr.kind; + if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind; if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); - if let Some(ref generic_args) = chain_method.args; + if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); let ty = cx.typeck_results().node_type(ty.hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) @@ -58,16 +58,16 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont } 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 let ExprKind::Block(block, _) = expr.kind { + for stmt in block.stmts { if_chain! { if let StmtKind::Local( Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(ref init_expr), .. } + init: Some(init_expr), .. } ) = stmt.kind; - if let ExprKind::MethodCall(ref method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator); - if let Some(ref generic_args) = method_name.args; + if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); + if let Some(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) || @@ -165,8 +165,8 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { // 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 let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0); if let &[name] = &path.segments; if name.ident == self.target; then { @@ -193,7 +193,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { } // Check if the collection is used for anything else if_chain! { - if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr; + if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr; if let &[name] = &path.segments; if name.ident == self.target; then { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 60afa449a45..56141fb3837 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -96,7 +96,7 @@ pub(super) fn check<'tcx>( let take = if let Some(end) = *end { let mut take_expr = end; - if let ExprKind::Binary(ref op, ref left, ref right) = end.kind { + if let ExprKind::Binary(ref op, left, right) = end.kind { if let BinOpKind::Add = op.node { let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); @@ -190,10 +190,10 @@ pub(super) fn check<'tcx>( fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind; + if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind; if len_args.len() == 1; if method.ident.name == sym!(len); - if let ExprKind::Path(QPath::Resolved(_, ref path)) = len_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind; if path.segments.len() == 1; if path.segments[0].ident.name == var; then { @@ -254,7 +254,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { if_chain! { // the indexed container is referenced by a name if let ExprKind::Path(ref seqpath) = seqexpr.kind; - if let QPath::Resolved(None, ref seqvar) = *seqpath; + if let QPath::Resolved(None, seqvar) = *seqpath; if seqvar.segments.len() == 1; let index_used_directly = path_to_local_id(idx, self.var); let indexed_indirectly = { @@ -310,7 +310,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { // a range index op - if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(meth, _, args, _) = expr.kind; if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); if !self.check(&args[1], &args[0], expr); @@ -319,7 +319,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // an index op - if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind; + if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); then { return } } @@ -340,19 +340,19 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let old = self.prefer_mutable; match expr.kind { - ExprKind::AssignOp(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => { + ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { self.prefer_mutable = true; self.visit_expr(lhs); self.prefer_mutable = false; self.visit_expr(rhs); }, - ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => { + ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { if mutbl == Mutability::Mut { self.prefer_mutable = true; } self.visit_expr(expr); }, - ExprKind::Call(ref f, args) => { + ExprKind::Call(f, args) => { self.visit_expr(f); for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index f63a3761a0d..e2cb4638018 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -5,7 +5,7 @@ use rustc_lint::LateContext; use std::iter::{once, Iterator}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Loop(ref block, _, _, _) = expr.kind { + if let ExprKind::Loop(block, _, _, _) = expr.kind { match never_loop_block(block, expr.hir_id) { NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"), NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), @@ -76,36 +76,36 @@ fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_lo fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { - StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e), - StmtKind::Local(ref local) => local.init.as_deref(), + StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), + StmtKind::Local(local) => local.init.as_deref(), StmtKind::Item(..) => None, } } fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { - ExprKind::Box(ref e) - | ExprKind::Unary(_, ref e) - | ExprKind::Cast(ref e, _) - | ExprKind::Type(ref e, _) - | ExprKind::Field(ref e, _) - | ExprKind::AddrOf(_, _, ref e) - | ExprKind::Struct(_, _, Some(ref e)) - | ExprKind::Repeat(ref e, _) - | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id), - ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es, _) | ExprKind::Tup(ref es) => { + ExprKind::Box(e) + | ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::Field(e, _) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Struct(_, _, Some(e)) + | ExprKind::Repeat(e, _) + | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), + ExprKind::Array(es) | ExprKind::MethodCall(_, _, es, _) | ExprKind::Tup(es) => { never_loop_expr_all(&mut es.iter(), main_loop_id) }, - ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id), - ExprKind::Binary(_, ref e1, ref e2) - | ExprKind::Assign(ref e1, ref e2, _) - | ExprKind::AssignOp(_, ref e1, ref e2) - | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id), - ExprKind::Loop(ref b, _, _, _) => { + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Binary(_, e1, e2) + | ExprKind::Assign(e1, e2, _) + | ExprKind::AssignOp(_, e1, e2) + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id), + ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. absorb_break(&never_loop_block(b, main_loop_id)) }, - ExprKind::If(ref e, ref e2, ref e3) => { + ExprKind::If(e, e2, ref e3) => { let e1 = never_loop_expr(e, main_loop_id); let e2 = never_loop_expr(e2, main_loop_id); let e3 = e3 @@ -113,7 +113,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); combine_seq(e1, combine_branches(e2, e3)) }, - ExprKind::Match(ref e, ref arms, _) => { + ExprKind::Match(e, arms, _) => { let e = never_loop_expr(e, main_loop_id); if arms.is_empty() { e @@ -122,7 +122,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { combine_seq(e, arms) } }, - ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), ExprKind::Continue(d) => { let id = d .target_id @@ -136,7 +136,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { 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 + ExprKind::InlineAsm(asm) => asm .operands .iter() .map(|(o, _)| match o { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index b3d784474c8..cb2c83e9029 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { 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), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr), _ => {}, } } else { diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 8451c1c6130..fc067e81bca 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -15,12 +15,12 @@ pub(super) fn check<'tcx>( expr: &'tcx Expr<'_>, ) { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); - if let ExprKind::Block(ref block, _) = body.kind; + if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); then { diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index fc287d51249..4db6644b9d7 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } match parent.kind { - ExprKind::AssignOp(op, ref lhs, ref rhs) => { + ExprKind::AssignOp(op, lhs, rhs) => { if lhs.hir_id == expr.hir_id { *state = if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) @@ -79,7 +79,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { }; } }, - ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => { + ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { *state = IncrementVisitorVarState::DontWarn }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { @@ -153,7 +153,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { // Look for declarations of the variable if_chain! { - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { @@ -191,10 +191,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { - ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { + ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == expr.hir_id => { self.state = InitializeVisitorState::DontWarn; }, - ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { + ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => { self.state = if_chain! { if self.depth == 0; if let InitializeVisitorState::Declared(name) @@ -273,7 +273,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { return; } match expr.kind { - ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => { + ExprKind::Assign(path, _, _) | ExprKind::AssignOp(_, path, _) => { if path_to_local_id(path, self.iterator) { self.nesting = RuledOut; } @@ -327,7 +327,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic // (&mut x).into_iter() ==> x.iter_mut() match &arg.kind { ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner) - if has_iter_method(cx, cx.typeck_results().expr_ty(&arg_inner)).is_some() => + if has_iter_method(cx, cx.typeck_results().expr_ty(arg_inner)).is_some() => { let meth_name = match mutability { Mutability::Mut => "iter_mut", @@ -335,7 +335,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{}()", - sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(), + sugg::Sugg::hir_with_applicability(cx, arg_inner, "_", applic_ref).maybe_par(), meth_name, ) } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index ffe8c0c5494..9c172079852 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -11,14 +11,14 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) { - if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind { + if let ExprKind::Match(matchexpr, arms, ref source) = inner.kind { // ensure "if let" compatible match structure match *source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } => { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() - && is_simple_break_expr(&arms[1].body) + && is_simple_break_expr(arms[1].body) { if in_external_macro(cx.sess(), expr.span) { return; @@ -57,7 +57,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< if block.stmts.is_empty() { return None; } - if let StmtKind::Local(ref local) = block.stmts[0].kind { + if let StmtKind::Local(local) = block.stmts[0].kind { local.init //.map(|expr| expr) } else { None @@ -67,9 +67,9 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< /// If a block begins with an expression (with or without semicolon), return it. fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { match block.expr { - Some(ref expr) if block.stmts.is_empty() => Some(expr), + Some(expr) if block.stmts.is_empty() => Some(expr), None if !block.stmts.is_empty() => match block.stmts[0].kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr), StmtKind::Local(..) | StmtKind::Item(..) => None, }, _ => None, @@ -82,7 +82,7 @@ fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_simple_break_expr(expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true, - ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), + ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), _ => false, } } diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 57fc6250a9a..82715d9bafa 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -16,12 +16,10 @@ use rustc_middle::hir::map::Map; use rustc_span::symbol::sym; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind { + if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind { let pat = &arms[0].pat.kind; - if let ( - &PatKind::TupleStruct(ref qpath, ref pat_args, _), - &ExprKind::MethodCall(ref method_path, _, ref method_args, _), - ) = (pat, &match_expr.kind) + if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) = + (pat, &match_expr.kind) { let iter_expr = &method_args[0]; @@ -40,8 +38,8 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && is_trait_method(cx, match_expr, sym::Iterator) && lhs_constructor.ident.name == sym::Some && (pat_args.is_empty() - || !is_refutable(cx, &pat_args[0]) - && !is_used_inside(cx, iter_expr, &arms[0].body) + || !is_refutable(cx, pat_args[0]) + && !is_used_inside(cx, iter_expr, arms[0].body) && !is_iterator_used_after_while_let(cx, iter_expr) && !is_nested(cx, expr, &method_args[0])) { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9da37efddac..dfa464ddb81 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } else { return; }; - let target_res = cx.qpath_res(&target_path, target_arg.hir_id); + let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { return; }; @@ -174,7 +174,7 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t // Tests if `expr` is a `&str`. fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.typeck_results().expr_ty_adjusted(&expr).kind() { + match cx.typeck_results().expr_ty_adjusted(expr).kind() { ty::Ref(_, ty, _) => ty.is_str(), _ => false, } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 9f1ab1f695d..99c35ae3bbf 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } if_chain! { - if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind; + if let hir::ExprKind::MethodCall(method, _, args, _) = e.kind; if args.len() == 2; if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); match closure_body.params[0].pat.kind { - hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding( + hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, .., name, None ) = inner.kind { if ident_eq(name, closure_expr) { @@ -71,14 +71,14 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { }, hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) => { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { if ident_eq(name, inner) { if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { lint(cx, e.span, args[0].span, true); } } }, - hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { + hir::ExprKind::MethodCall(method, _, [obj], _) => if_chain! { if ident_eq(name, obj) && method.ident.name == sym::clone; if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind { path.segments.len() == 1 && path.segments[0].ident == name } else { false diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index a6a63961be5..425a9734e5f 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -112,7 +112,7 @@ 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 { + if let ExprKind::MethodCall(method, _t_span, 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 { diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 75191e1f9ee..e7719e7663d 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'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(method, _, args, _) = expr.kind; if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if is_trait_method(cx, expr, sym::Iterator) @@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a 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), + ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), _ => false, } } @@ -99,12 +99,12 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { 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, _) => { + ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat), + ExprKind::Block(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; + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind; + if let ExprKind::Ret(Some(ret_val)) = expr.kind; then { match_expr_param(cx, ret_val, params[0].pat) } else { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 5cc16244a0d..57cd907e77e 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -133,7 +133,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // Calls can't be reduced any more Some(expr.span) }, - hir::ExprKind::Block(ref block, _) => { + hir::ExprKind::Block(block, _) => { match (block.stmts, block.expr.as_ref()) { (&[], Some(inner_expr)) => { // If block only contains an expression, @@ -144,8 +144,8 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Local(ref local) => Some(local.span), - hir::StmtKind::Expr(ref e) => Some(e.span), + hir::StmtKind::Local(local) => Some(local.span), + hir::StmtKind::Expr(e) => Some(e.span), hir::StmtKind::Semi(..) => Some(inner_stmt.span), hir::StmtKind::Item(..) => None, } @@ -169,12 +169,12 @@ fn unit_closure<'tcx>( expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { if_chain! { - if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind; + if let hir::ExprKind::Closure(_, decl, inner_expr_id, _, _) = expr.kind; let body = cx.tcx.hir().body(inner_expr_id); let body_expr = &body.value; if decl.inputs.len() == 1; if is_unit_expression(cx, body_expr); - if let Some(binding) = iter_input_pats(&decl, body).next(); + if let Some(binding) = iter_input_pats(decl, body).next(); then { return Some((binding, body_expr)); } @@ -267,7 +267,7 @@ impl<'tcx> LateLintPass<'tcx> for MapUnit { return; } - if let hir::StmtKind::Semi(ref expr) = stmt.kind { + if let hir::StmtKind::Semi(expr) = stmt.kind { if let Some(arglists) = method_chain_args(expr, &["map"]) { lint_map_unit_fn(cx, stmt, expr, arglists[0]); } diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index ccaa5e98c83..ca6fb0831fe 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind; + if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind; if let Some(idx_expr) = is_vec_indexing(cx, match_expr); if let ExprKind::Index(vec, idx) = idx_expr.kind; @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Index(ref array, ref index) = expr.kind; + if let ExprKind::Index(array, index) = expr.kind; if is_vector(cx, array); if !is_full_range(cx, index); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c68ae00be01..a892e24482b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -589,7 +589,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { lint_match_arms(cx, expr); } - if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { + if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr); check_overlapping_arms(cx, ex, arms); @@ -604,7 +604,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { check_match_single_binding(cx, ex, arms, expr); } } - if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + if let ExprKind::Match(ex, arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms, expr); } } @@ -613,14 +613,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), local.span); if !in_macro(local.span); - if let Some(ref expr) = local.init; - if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind; + if let Some(expr) = local.init; + if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind; if arms.len() == 1 && arms[0].guard.is_none(); if let PatKind::TupleStruct( - QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind; + QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; - let body = remove_blocks(&arms[0].body); + if let PatKind::Binding(_, arg, ..) = strip_pat_refs(args[0]).kind; + let body = remove_blocks(arms[0].body); if path_to_local_id(body, arg); then { @@ -649,7 +649,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), pat.span); if !in_macro(pat.span); - if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind; + if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); if let ty::Adt(def, _) = ty.kind(); @@ -761,7 +761,7 @@ fn report_single_match_single_pattern( // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) @@ -771,7 +771,7 @@ fn report_single_match_single_pattern( "if let {} = {} {}{}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) @@ -809,7 +809,7 @@ fn check_single_match_opt_like( ]; let path = match arms[1].pat.kind { - PatKind::TupleStruct(ref path, ref inner, _) => { + PatKind::TupleStruct(ref path, inner, _) => { // Contains any non wildcard patterns (e.g., `Err(err)`)? if !inner.iter().all(is_wild) { return; @@ -841,7 +841,7 @@ fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind { + let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { if let ExprKind::Lit(ref lit) = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), @@ -917,7 +917,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm 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 { + if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); if path_str == "Err" { let mut matching_wild = inner.iter().any(is_wild); @@ -937,7 +937,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } if_chain! { if matching_wild; - if let ExprKind::Block(ref block, _) = arm.body.kind; + if let ExprKind::Block(block, _) = arm.body.kind; if is_panic_block(block); then { // `Err(_)` or `Err(_e)` arm with `panic!` found @@ -1143,9 +1143,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // If the block contains only a `panic!` macro (as expression or statement) fn is_panic_block(block: &Block<'_>) -> bool { match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(ref exp), 0, _) => { - is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none() - }, + (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(), (&None, 1, Some(stmt)) => { is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none() }, @@ -1156,7 +1154,7 @@ fn is_panic_block(block: &Block<'_>) -> bool { fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if has_only_ref_pats(arms) { let mut suggs = Vec::with_capacity(arms.len() + 1); - let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { let span = ex.span.source_callsite(); suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); ( @@ -1173,7 +1171,7 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e }; suggs.extend(arms.iter().filter_map(|a| { - if let PatKind::Ref(ref refp, _) = a.pat.kind { + if let PatKind::Ref(refp, _) = a.pat.kind { Some((a.pat.span, snippet(cx, refp.span, "..").to_string())) } else { None @@ -1242,7 +1240,7 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { for arm in arms { - if let PatKind::Or(ref fields) = arm.pat.kind { + if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern if fields.len() > 1 && fields.iter().any(is_wild) { span_lint_and_help( @@ -1308,7 +1306,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = ex; if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() { + if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; } }; @@ -1385,7 +1383,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A let matched_vars = ex.span; let bind_names = arms[0].pat.span; - let match_body = remove_blocks(&arms[0].body); + let match_body = remove_blocks(arms[0].body); let mut snippet_body = if match_body.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() } else { @@ -1396,13 +1394,13 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A match match_body.kind { ExprKind::Block(block, _) => { // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(&match_body).is_unit() { + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } }, _ => { // expr_ty(body) == () - if cx.typeck_results().expr_ty(&match_body).is_unit() { + if cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } }, @@ -1502,10 +1500,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<' fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec> { arms.iter() .flat_map(|arm| { - if let Arm { - ref pat, guard: None, .. - } = *arm - { + if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs = match lhs { Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, @@ -1525,7 +1520,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(ref value) = pat.kind { + if let PatKind::Lit(value) = pat.kind { let value = constant(cx, cx.typeck_results(), value)?.0; return Some(SpannedRange { span: pat.span, @@ -1572,8 +1567,8 @@ fn type_ranges(ranges: &[SpannedRange]) -> TypedRanges { fn is_unit_expr(expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Tup(ref v) if v.is_empty() => true, - ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true, + ExprKind::Tup(v) if v.is_empty() => true, + ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true, _ => false, } } @@ -1586,14 +1581,14 @@ fn is_none_arm(arm: &Arm<'_>) -> bool { // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(arm: &Arm<'_>) -> Option { if_chain! { - if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind; + if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; - if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind; + if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { return Some(rb) @@ -1685,7 +1680,7 @@ where (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (), _ => { // skip if the range `a` is completely included into the range `b` - if let Ordering::Equal | Ordering::Less = a.cmp(&b) { + if let Ordering::Equal | Ordering::Less = a.cmp(b) { let kind_a = Kind::End(a.range().node.1, a.range()); let kind_b = Kind::End(b.range().node.1, b.range()); if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) { @@ -1737,7 +1732,7 @@ mod redundant_pattern_match { kind = &inner.kind; } let good_method = match kind { - PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { if match_qpath(path, &paths::RESULT_OK) { "is_ok()" @@ -1818,8 +1813,8 @@ mod redundant_pattern_match { let found_good_method = match node_pair { ( - PatKind::TupleStruct(ref path_left, ref patterns_left, _), - PatKind::TupleStruct(ref path_right, ref patterns_right, _), + PatKind::TupleStruct(ref path_left, patterns_left, _), + PatKind::TupleStruct(ref path_right, 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( @@ -1846,8 +1841,8 @@ mod redundant_pattern_match { 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, _)) + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { @@ -1969,10 +1964,10 @@ fn test_overlapping() { /// Implementation of `MATCH_SAME_ARMS`. fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); - h.hash_expr(&arm.body); + h.hash_expr(arm.body); h.finish() }; @@ -2008,7 +2003,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) .expr_fallback(eq_fallback) - .eq_expr(&lhs.body, &rhs.body) + .eq_expr(lhs.body, rhs.body) // these checks could be removed to allow unused bindings && bindings_eq(lhs.pat, local_map.keys().copied().collect()) && bindings_eq(rhs.pat, local_map.values().copied().collect()) diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index 7895ba9f1e0..a735c616f6e 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -34,7 +34,7 @@ declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]); impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Call(func, func_args) = expr.kind; // is `mem::discriminant` if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { let mut derefs_needed = ptr_depth; let mut cur_expr = param; while derefs_needed > 0 { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref inner_expr) = cur_expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, _, inner_expr) = cur_expr.kind { derefs_needed -= 1; cur_expr = inner_expr; } else { diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs index c13802e3953..a28cb5f32fe 100644 --- a/clippy_lints/src/mem_forget.rs +++ b/clippy_lints/src/mem_forget.rs @@ -28,7 +28,7 @@ declare_lint_pass!(MemForget => [MEM_FORGET]); impl<'tcx> LateLintPass<'tcx> for MemForget { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(ref path_expr, ref args) = e.kind { + if let ExprKind::Call(path_expr, args) = e.kind { if let ExprKind::Path(ref qpath) = path_expr.kind { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() { if match_def_path(cx, def_id, &paths::MEM_FORGET) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 0418c616efa..e1d351aee45 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -109,14 +109,14 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E // argument's type. All that's left is to get // replacee's path. let replaced_path = match dest.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => { - if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { + if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { replaced_path } else { return; } }, - ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path, + ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, _ => return, }; @@ -161,7 +161,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } if_chain! { - if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind; + if let ExprKind::Call(repl_func, 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.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); @@ -214,7 +214,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< .iter() .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol)) { - if let QPath::TypeRelative(_, ref method) = path { + if let QPath::TypeRelative(_, method) = path { if method.ident.name == sym::new { return true; } @@ -226,7 +226,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { if_chain! { - if let ExprKind::Call(ref repl_func, _) = src.kind; + if let ExprKind::Call(repl_func, _) = src.kind; if !in_external_macro(cx.tcx.sess, expr_span); if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); @@ -273,11 +273,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // Check that `expr` is a call to `mem::replace()` - if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Call(func, func_args) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::MEM_REPLACE); - if let [dest, src] = &**func_args; + if let [dest, src] = func_args; then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 46d4c674648..287bff886bf 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -71,7 +71,7 @@ pub(crate) trait BindInsteadOfMap { closure_args_span: Span, ) -> bool { if_chain! { - if let hir::ExprKind::Call(ref some_expr, [inner_expr]) = closure_expr.kind; + if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind; if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind; if Self::is_variant(cx, path.res); if !contains_return(inner_expr); @@ -107,7 +107,7 @@ pub(crate) trait BindInsteadOfMap { 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, [arg]) = ret_expr.kind; + if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind; if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind; if Self::is_variant(cx, path.res); if !contains_return(arg); diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index c668fe52781..514c4118765 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -19,7 +19,7 @@ pub(super) fn check( ) -> bool { if_chain! { if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind; + if let hir::ExprKind::Call(fun, arg_char) = info.other.kind; if arg_char.len() == 1; if let hir::ExprKind::Path(ref qpath) = fun.kind; if let Some(segment) = single_segment_path(qpath); diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index e7bffa66b3f..03cb41697d5 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -100,9 +100,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa applicability: &mut Applicability, ) -> Vec { if_chain! { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind; - if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind; - if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind; + if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind; + if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind; + if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind; then { format_arg_expr_tup @@ -155,7 +155,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa if block.stmts.len() == 1; if let hir::StmtKind::Local(local) = &block.stmts[0].kind; if let Some(arg_root) = &local.init; - if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind; + if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind; if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; then { diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 45d1ed953b4..35fae450eeb 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -19,7 +19,7 @@ use super::OPTION_FILTER_MAP; fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { - hir::ExprKind::Path(QPath::TypeRelative(_, ref mname)) => mname.ident.name == method_name, + hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, hir::ExprKind::Path(QPath::Resolved(_, segments)) => { segments.segments.last().unwrap().ident.name == method_name }, @@ -28,7 +28,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy let closure_expr = remove_blocks(&body.value); let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { - hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, ref args, _) => { + hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => { if_chain! { if ident.name == method_name; if let hir::ExprKind::Path(path) = &args[0].kind; @@ -61,7 +61,7 @@ fn lint_filter_some_map_unwrap( methods_span: Span, ) { let iterator = is_trait_method(cx, expr, sym::Iterator); - let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&filter_recv), sym::option_type); + let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::option_type); if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) { let msg = "`filter` for `Some` followed by `unwrap`"; let help = "consider using `flatten` instead"; diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 664885a2f0e..dd613d0cd63 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( let body = cx.tcx.hir().body(*body_id); if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; if path.segments.len() == 1; if path.segments[0].ident.name == binding_ident.name; diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 04461ad5c3a..1211e2f2bf7 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -13,7 +13,7 @@ use clippy_utils::is_diagnostic_assoc_item; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) { if_chain! { if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind; - let return_type = cx.typeck_results().expr_ty(&expr); + let return_type = cx.typeck_results().expr_ty(expr); let input_type = cx.typeck_results().expr_ty(arg).peel_refs(); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index dab0a43a096..a49851de38e 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if derefs_to_slice(cx, caller_expr, cx.typeck_results().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 hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind; if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) = higher::range(index_expr); if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa6323b56e1..b1ade5addd6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1740,10 +1740,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { check_methods(cx, expr, self.msrv.as_ref()); match expr.kind { - hir::ExprKind::Call(ref func, ref args) => { + hir::ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, &func.kind); }, - hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { + hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); clone_on_copy::check(cx, expr, method_call.ident.name, args); @@ -1753,9 +1753,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) - if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => - { + hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { expr, chain: lhs, @@ -1763,7 +1761,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { eq: op.node == hir::BinOpKind::Eq, }; lint_binary_expr_with_method_call(cx, &mut info); - } + }, _ => (), } } @@ -1781,7 +1779,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; - if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); + if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); let method_sig = cx.tcx.fn_sig(impl_item.def_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); @@ -1801,7 +1799,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { method_config.output_type.matches(&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) + method_config.lifetime_param_cond(impl_item) { span_lint_and_help( cx, @@ -2272,10 +2270,10 @@ impl OutType { let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); match (self, ty) { (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), _ => false, } } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 1367a0c21d8..7e9c8fa829d 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -77,10 +77,10 @@ pub(super) fn check<'tcx>( } } }, - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => { if_chain! { - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind; - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind; + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind; + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind; then { path_to_local_id(inner2, closure_body.params[0].pat.hir_id) } else { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 89dedc5f0d8..df89da5d3e0 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -86,7 +86,7 @@ pub(super) fn check<'tcx>( (&paths::RESULT, true, &["or", "unwrap_or"], "else"), ]; - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { + if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind { if path.ident.as_str() == "len" { let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -105,7 +105,7 @@ pub(super) fn check<'tcx>( if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); if is_lazyness_candidate(cx, arg); - if !contains_return(&arg); + if !contains_return(arg); let self_ty = cx.typeck_results().expr_ty(self_expr); @@ -158,7 +158,7 @@ pub(super) fn check<'tcx>( if args.len() == 2 { match args[1].kind { - hir::ExprKind::Call(ref fun, ref or_args) => { + hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { let fun_span = if or_has_args { None } else { Some(fun.span) }; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 8a94d7f4155..ecec6fc3bb7 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( then { if let hir::PatKind::Ref(..) = closure_arg.pat.kind { Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind { + } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind { let name = &*ident.name.as_str(); Some(search_snippet.replace(&format!("*{}", name), name)) } else { @@ -108,8 +108,8 @@ pub(super) fn check<'tcx>( } }; if_chain! { - if is_string_or_str_slice(&search_recv); - if is_string_or_str_slice(&search_arg); + if is_string_or_str_slice(search_recv); + if is_string_or_str_slice(search_arg); then { let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); match option_check_method { diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 3cc1912b15a..0ae65c0c01d 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -10,7 +10,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::Call(ref callee, ref args) = recv.kind; + if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); if let hir::ExprKind::Path(ref path) = callee.kind; if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); @@ -28,9 +28,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { match ty.kind() { - ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), - ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), - ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), + ty::Array(component, _) => is_maybe_uninit_ty_valid(cx, component), + ty::Tuple(types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), + ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), _ => false, } } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 8e637e12393..0f28bfdf09e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -20,9 +20,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< let mutates_arg = mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id)); - let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value); + let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value); - let mut return_visitor = ReturnVisitor::new(&cx, arg_id); + let mut return_visitor = ReturnVisitor::new(cx, arg_id); return_visitor.visit_expr(&body.value); found_mapping |= return_visitor.found_mapping; found_filtering |= return_visitor.found_filtering; @@ -52,7 +52,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match &expr.kind { - hir::ExprKind::Call(ref func, ref args) => { + hir::ExprKind::Call(func, args) => { if let hir::ExprKind::Path(ref path) = func.kind { if match_qpath(path, &paths::OPTION_SOME) { if path_to_local_id(&args[0], arg_id) { @@ -65,22 +65,22 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, - hir::ExprKind::Block(ref block, _) => block + hir::ExprKind::Block(block, _) => block .expr .as_ref() - .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)), + .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; for arm in *arms { - let (m, f) = check_expression(cx, arg_id, &arm.body); + let (m, f) = check_expression(cx, arg_id, arm.body); found_mapping |= m; found_filtering |= f; } (found_mapping, found_filtering) }, // There must be an else_arm or there will be a type error - hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => { + hir::ExprKind::If(_, if_arm, Some(else_arm)) => { let if_check = check_expression(cx, arg_id, if_arm); let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 7c16470348f..75517c48a21 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -34,13 +34,13 @@ pub(super) fn check( let closure_expr = remove_blocks(&closure_body.value); // Check if the closure body is of the form `acc some_expr(x)` - if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind; + if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind; if bin_op.node == op; // Extract the names of the two arguments to the closure if let [param_a, param_b] = closure_body.params; - if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind; - if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind; + if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind; + if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind; if path_to_local_id(left_expr, first_arg_id); if replacement_has_args || path_to_local_id(right_expr, second_arg_id); diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index ac6b55396da..f6bf37e08b9 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -26,7 +26,7 @@ pub(super) fn derefs_to_slice<'tcx>( } } - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { + if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind { if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { Some(&args[0]) } else { diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs index 0489d0f6fcf..866cf616679 100644 --- a/clippy_lints/src/methods/zst_offset.rs +++ b/clippy_lints/src/methods/zst_offset.rs @@ -8,7 +8,7 @@ use super::ZST_OFFSET; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); + if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); if layout.is_zst(); then { diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 776f4c7b741..45948f4d926 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -67,7 +67,7 @@ enum MinMax { fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { - ExprKind::Call(ref path, ref args) => { + ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { cx.typeck_results() .qpath_res(qpath, path.hir_id) @@ -85,7 +85,7 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons None } }, - ExprKind::MethodCall(ref path, _, ref args, _) => { + ExprKind::MethodCall(path, _, args, _) => { if_chain! { if let [obj, _] = args; if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index c23643cb2f5..afced5a5ce5 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -304,9 +304,9 @@ 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 StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; - if let Some(ref init) = local.init; + if let Some(init) = local.init; if !higher::is_from_for_desugar(local); if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut; then { @@ -322,7 +322,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } else { ("", sugg_init.addr()) }; - let tyopt = if let Some(ref ty) = local.ty { + let tyopt = if let Some(ty) = local.ty { format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) } else { String::new() @@ -350,8 +350,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } }; if_chain! { - if let StmtKind::Semi(ref expr) = stmt.kind; - if let ExprKind::Binary(ref binop, ref a, ref b) = expr.kind; + if let StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::Binary(ref binop, a, b) = expr.kind; if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; if let Some(sugg) = Sugg::hir_opt(cx, a); then { @@ -378,11 +378,11 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { - ExprKind::Cast(ref e, ref ty) => { + ExprKind::Cast(e, ty) => { check_cast(cx, expr.span, e, ty); return; }, - ExprKind::Binary(ref cmp, ref left, ref right) => { + ExprKind::Binary(ref cmp, left, right) => { check_binary(cx, expr, cmp, left, right); return; }, @@ -501,12 +501,12 @@ fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { // Return true if `expr` is the result of `signum()` invoked on a float value. fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { // The negation of a signum is still a signum - if let ExprKind::Unary(UnOp::Neg, ref child_expr) = expr.kind { - return is_signum(cx, &child_expr); + if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind { + return is_signum(cx, child_expr); } if_chain! { - if let ExprKind::MethodCall(ref method_name, _, ref expressions, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind; if sym!(signum) == method_name.ident.name; // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) @@ -552,7 +552,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } let (arg_ty, snip) = match expr.kind { - ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => { + ExprKind::MethodCall(.., args, _) if args.len() == 1 => { if_chain!( if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString) @@ -564,7 +564,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } ) }, - ExprKind::Call(ref path, ref v) if v.len() == 1 => { + ExprKind::Call(path, 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.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, "..")) @@ -649,7 +649,7 @@ 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), + ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), _ => is_used(cx, parent), }) } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 23554669d97..0dc02431ad5 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, &mir) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) { if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index dd4488f3f02..041fe64a1a9 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, ref _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 41bd07bcf1e..ed7b9cd62dc 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -58,21 +58,21 @@ declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { if trait_ref_of_method(cx, item.hir_id()).is_none() { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index ef33e41a5fa..4b9c51d0c16 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); - } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, ref e) = expr.kind { + } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { span_lint( self.cx, @@ -85,7 +85,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { if let hir::TyKind::Rptr( _, hir::MutTy { - ty: ref pty, + ty: pty, mutbl: hir::Mutability::Mut, }, ) = ty.kind diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 0c09ddb8073..b85cc4b9548 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -32,7 +32,7 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { match e.kind { - ExprKind::Call(ref fn_expr, ref arguments) => { + ExprKind::Call(fn_expr, arguments) => { if let ExprKind::Path(ref path) = fn_expr.kind { check_arguments( cx, @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ); } }, - ExprKind::MethodCall(ref path, _, ref arguments, _) => { + ExprKind::MethodCall(path, _, arguments, _) => { 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); diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index db7b3423ad9..96a58d1410f 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -71,7 +71,7 @@ declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]); impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.kind { + if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; let snip = Sugg::hir_with_applicability(cx, pred, "", &mut applicability); @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.make_return(); } - if parent_node_is_if_expr(&e, &cx) { + if parent_node_is_if_expr(e, cx) { snip = snip.blockify() } @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let ExprKind::Block(ref then_block, _) = then_block.kind { + if let ExprKind::Block(then_block, _) = then_block.kind { match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( @@ -225,7 +225,7 @@ fn check_comparison<'a, 'tcx>( ) { use self::Expression::{Bool, Other}; - if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind { + if let ExprKind::Binary(op, left_side, right_side) = e.kind { let (l_ty, r_ty) = ( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), @@ -237,7 +237,7 @@ fn check_comparison<'a, 'tcx>( let mut applicability = Applicability::MachineApplicable; if let BinOpKind::Eq = op.node { - let expression_info = one_side_is_unary_not(&left_side, &right_side); + let expression_info = one_side_is_unary_not(left_side, right_side); if expression_info.one_side_is_unary_not { span_lint_and_sugg( cx, @@ -324,9 +324,9 @@ fn fetch_bool_block(block: &Block<'_>) -> Expression { match (&*block.stmts, block.expr.as_ref()) { (&[], Some(e)) => fetch_bool_expr(&**e), (&[ref e], None) => { - if let StmtKind::Semi(ref e) = e.kind { + if let StmtKind::Semi(e) = e.kind { if let ExprKind::Ret(_) = e.kind { - fetch_bool_expr(&**e) + fetch_bool_expr(e) } else { Expression::Other } @@ -340,7 +340,7 @@ fn fetch_bool_block(block: &Block<'_>) -> Expression { fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { match expr.kind { - ExprKind::Block(ref block, _) => fetch_bool_block(block), + ExprKind::Block(block, _) => fetch_bool_block(block), ExprKind::Lit(ref lit_ptr) => { if let LitKind::Bool(value) = lit_ptr.node { Expression::Bool(value) @@ -348,7 +348,7 @@ fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { Expression::Other } }, - ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) { + ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) { Expression::Bool(value) => Expression::RetBool(value), _ => Expression::Other, }, diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 79d84da2dfc..eef3c16730b 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { if e.span.from_expansion() || self.derived_item.is_some() { return; } - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 7fbffe04a3f..0e976b130eb 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { if_chain! { // Only lint immutable refs, because `&mut ref T` may be useful. - if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind; + if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind; // Check sub_pat got a `ref` keyword (excluding `ref mut`). if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind; diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 2ea871990f1..079b6642d58 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for NeedlessForEach { ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ); // Checks the type of the `iter` method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_recv)).is_some(); + if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7370ba39922..780e2241293 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if is_type_diagnostic_item(cx, ty, sym::vec_type); if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); - if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = input.kind; if let Some(elem_ty) = path.segments.iter() .find(|seg| seg.ident.name == sym::Vec) .and_then(|ps| ps.args.as_ref()) diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index e93de8a252a..8f325404deb 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -49,7 +49,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]); impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { + if let ExprKind::Struct(_, fields, Some(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { if fields.len() == def.non_enum_variant().fields.len() 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 4b935c7b906..0704173a011 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -51,8 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { if_chain! { if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Unary(UnOp::Not, ref inner) = expr.kind; - if let ExprKind::Binary(ref op, ref left, _) = inner.kind; + if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; + if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 7b00879251f..34fd012572f 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -28,12 +28,12 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); #[allow(clippy::match_same_arms)] impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, ref left, ref right) = e.kind { + if let ExprKind::Binary(ref op, left, right) = e.kind { if BinOpKind::Mul == op.node { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, ref lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, ref lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), _ => {}, } } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 83953a16bc8..cfcaf509471 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -51,23 +51,21 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Lit(..) | ExprKind::Closure(..) => true, ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), - ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => { - has_no_effect(cx, a) && has_no_effect(cx, b) - }, - ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)), - ExprKind::Repeat(ref inner, _) - | ExprKind::Cast(ref inner, _) - | ExprKind::Type(ref inner, _) - | ExprKind::Unary(_, ref inner) - | ExprKind::Field(ref inner, _) - | ExprKind::AddrOf(_, _, ref inner) - | ExprKind::Box(ref inner) => has_no_effect(cx, inner), - ExprKind::Struct(_, ref fields, ref base) => { + ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), + ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), + ExprKind::Repeat(inner, _) + | ExprKind::Cast(inner, _) + | ExprKind::Type(inner, _) + | ExprKind::Unary(_, inner) + | ExprKind::Field(inner, _) + | ExprKind::AddrOf(_, _, inner) + | ExprKind::Box(inner) => has_no_effect(cx, inner), + ExprKind::Struct(_, fields, ref base) => { !has_drop(cx, cx.typeck_results().expr_ty(expr)) - && fields.iter().all(|field| has_no_effect(cx, &field.expr)) + && fields.iter().all(|field| has_no_effect(cx, field.expr)) && base.as_ref().map_or(true, |base| has_no_effect(cx, base)) }, - ExprKind::Call(ref callee, ref args) => { + ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); match res { @@ -81,7 +79,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr)) }, _ => false, @@ -92,7 +90,7 @@ declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]); impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Semi(ref expr) = stmt.kind { + if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { @@ -127,26 +125,26 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(vec![&**a, &**b]), - ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { - Some(vec![&**a, &**b]) + ExprKind::Index(a, b) => Some(vec![a, b]), + ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { + Some(vec![a, b]) }, - ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()), - ExprKind::Repeat(ref inner, _) - | ExprKind::Cast(ref inner, _) - | ExprKind::Type(ref inner, _) - | ExprKind::Unary(_, ref inner) - | ExprKind::Field(ref inner, _) - | ExprKind::AddrOf(_, _, ref inner) - | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), - ExprKind::Struct(_, ref fields, ref base) => { + ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()), + ExprKind::Repeat(inner, _) + | ExprKind::Cast(inner, _) + | ExprKind::Type(inner, _) + | ExprKind::Unary(_, inner) + | ExprKind::Field(inner, _) + | ExprKind::AddrOf(_, _, inner) + | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None } else { Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) } }, - ExprKind::Call(ref callee, ref args) => { + ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); match res { @@ -161,7 +159,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { + ExprKind::Block(block, _) => { if block.stmts.is_empty() { block.expr.as_ref().and_then(|e| { match block.rules { diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 6d45e7bc6cf..a83daea97bf 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -44,7 +44,7 @@ impl LateLintPass<'_> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, _, [func, param], _) => { - let obj_ty = cx.typeck_results().expr_ty(&func).peel_refs(); + let obj_ty = cx.typeck_results().expr_ty(func).peel_refs(); if_chain! { if (path.ident.name == sym!(mode) @@ -65,7 +65,7 @@ impl LateLintPass<'_> for NonOctalUnixPermissions { } } }, - ExprKind::Call(ref func, [param]) => { + ExprKind::Call(func, [param]) => { if_chain! { if let ExprKind::Path(ref path) = func.kind; if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index c61dff4b8e0..9efe45336bf 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -31,7 +31,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 { + if let ExprKind::MethodCall(path, _, arguments, _) = e.kind { 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(); @@ -59,7 +59,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 { + if let ExprKind::MethodCall(path, _, arguments, _) = argument.kind { 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 diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index a76a4a33f1f..1b9120ae45f 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -66,9 +66,9 @@ 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(path, _, &[ref receiver], _) = &expr.kind { path.ident.name.as_str() == "ok" - && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::result_type) } else { false } @@ -97,9 +97,9 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { ) = &arm.body.kind { if let [] = statements { - Some(&expr) + Some(expr) } else { - Some(&arm.body) + Some(arm.body) } } else { None diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index cf667c6e805..e222782c2cc 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -31,11 +31,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); if_chain! { - if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; - if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = first.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path3)) = second.kind; + if let ExprKind::Binary(ref op, first, second) = expr.kind; + if let ExprKind::Binary(ref op2, ident1, ident2) = first.kind; + if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind; if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); @@ -56,11 +56,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { } if_chain! { - if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; - if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = second.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path3)) = first.kind; + if let ExprKind::Binary(ref op, first, second) = expr.kind; + if let ExprKind::Binary(ref op2, ident1, ident2) = second.kind; + if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind; if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 9a5b1c3b944..c86a847b2ee 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -141,11 +141,11 @@ impl<'tcx> PassByRefOrValue { }; if_chain! { - if !output_lts.contains(&input_lt); + 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; + if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind; then { let value_type = if is_self_ty(decl_ty) { "self".into() diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 95ffae28d8c..00245926381 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -45,7 +45,7 @@ declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]); impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(push); if args.len() == 2; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::PathBuf); diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 4550b367da4..8c198cecd6a 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -86,7 +86,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); 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 StmtKind::Local(local) = stmt.kind { if let Some(init) = &local.init { if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) { let pat = &local.pat; @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(ref expr, arms, source) = expr.kind { + if let ExprKind::Match(expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) { @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { ) { if let Some(fn_sig) = cx.typeck_results().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); + apply_lint(cx, param.pat, ty, DerefPossible::Impossible); } } } @@ -187,7 +187,7 @@ fn find_first_mismatch<'tcx>( ty: Ty<'tcx>, level: Level, ) -> Option<(Span, Mutability, Level)> { - if let PatKind::Ref(ref sub_pat, _) = pat.kind { + if let PatKind::Ref(sub_pat, _) = pat.kind { if let TyKind::Ref(_, sub_ty, _) = ty.kind() { return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); } @@ -199,8 +199,8 @@ fn find_first_mismatch<'tcx>( } } - 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 PatKind::Struct(ref qpath, field_pats, _) = pat.kind { + if let TyKind::Adt(adt_def, 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); @@ -208,8 +208,8 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { + if let PatKind::TupleStruct(ref qpath, pats, _) = pat.kind { + if let TyKind::Adt(adt_def, 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)); @@ -218,7 +218,7 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::Tuple(ref pats, _) = pat.kind { + if let PatKind::Tuple(pats, _) = pat.kind { if let TyKind::Tuple(..) = ty.kind() { return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 6e530d0ffb0..09fcdb5faf8 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -124,7 +124,7 @@ declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, _, body_id) = item.kind { - check_fn(cx, &sig.decl, item.hir_id(), Some(body_id)); + check_fn(cx, sig.decl, item.hir_id(), Some(body_id)); } } @@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; // ignore trait impls } } - check_fn(cx, &sig.decl, item.hir_id(), Some(body_id)); + check_fn(cx, sig.decl, item.hir_id(), Some(body_id)); } } @@ -147,12 +147,12 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { } else { None }; - check_fn(cx, &sig.decl, item.hir_id(), body_id); + check_fn(cx, sig.decl, item.hir_id(), body_id); } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind { + if let ExprKind::Binary(ref op, l, r) = expr.kind { if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) { span_lint( cx, @@ -262,10 +262,10 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } else if match_type(cx, ty, &paths::COW) { if_chain! { - if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; - if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind; + if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind; + if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind; if let [ref bx] = *pp.segments; - if let Some(ref params) = bx.args; + if let Some(params) = bx.args; if !params.parenthesized; if let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), @@ -289,7 +289,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } - if let FnRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ty) = decl.output { if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { let mut immutables = vec![]; for (_, ref mutbl, ref argspan) in decl @@ -322,8 +322,8 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option { if_chain! { - if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last(); let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -346,7 +346,7 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } fn is_null_path(expr: &Expr<'_>) -> bool { - if let ExprKind::Call(ref pathexp, ref args) = expr.kind { + if let ExprKind::Call(pathexp, args) = expr.kind { if args.is_empty() { if let ExprKind::Path(ref path) = pathexp.kind { return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 5796c59c8b3..77cfa3f6b17 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -46,11 +46,11 @@ impl LateLintPass<'_> for PtrEq { return; } - if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if let ExprKind::Binary(ref op, left, 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), + _ => (left, right), }; if_chain! { @@ -79,7 +79,7 @@ impl LateLintPass<'_> for PtrEq { // 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 { + if let ExprKind::Cast(expr, _) = cast_expr.kind { return Some(expr); } } @@ -90,7 +90,7 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> // 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 { + if let ExprKind::Cast(expr, _) = cast_expr.kind { return Some(expr); } } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index c04b4255256..afb198f4955 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -78,8 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { // If the given expression is a cast from a usize, return the lhs of the cast fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(ref cast_lhs_expr, _) = expr.kind { - if is_expr_ty_usize(cx, &cast_lhs_expr) { + if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind { + if is_expr_ty_usize(cx, cast_lhs_expr) { return Some(cast_lhs_expr); } } @@ -92,7 +92,7 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind { + if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind { if is_expr_ty_raw_ptr(cx, &args[0]) { if path_segment.ident.name == sym::offset { return Some((&args[0], &args[1], Method::Offset)); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 2054255a7c9..6d720f43851 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -148,14 +148,14 @@ impl QuestionMark { fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { match expression.kind { - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { if let Some(return_expression) = Self::return_expression(block) { - return Self::expression_returns_none(cx, &return_expression); + return Self::expression_returns_none(cx, return_expression); } false }, - ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr), + ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), ExprKind::Path(ref qp) => { if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = cx.qpath_res(qp, expression.hir_id) @@ -174,7 +174,7 @@ impl QuestionMark { if_chain! { if block.stmts.len() == 1; if let Some(expr) = block.stmts.iter().last(); - if let StmtKind::Semi(ref expr) = expr.kind; + if let StmtKind::Semi(expr) = expr.kind; if let ExprKind::Ret(Some(ret_expr)) = expr.kind; then { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 79692abb6ac..1c3c125e579 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -183,10 +183,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { - ExprKind::MethodCall(ref path, _, ref args, _) => { + ExprKind::MethodCall(path, _, args, _) => { check_range_zip_with_len(cx, path, args, expr.span); }, - ExprKind::Binary(ref op, ref l, ref r) => { + ExprKind::Binary(ref op, l, r) => { if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { check_possible_range_contains(cx, op.node, l, r, expr); } @@ -287,7 +287,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } 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 { + if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), BinOpKind::Ge => (true, Ordering::Greater), @@ -324,18 +324,18 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: if path.ident.as_str() == "zip"; if let [iter, zip_arg] = args; // `.iter()` call - if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = iter.kind; + if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind; 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 let ExprKind::MethodCall(len_path, _, 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); + if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, 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, @@ -508,8 +508,8 @@ fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<' Spanned { node: BinOpKind::Add, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) => { if is_integer_const(cx, lhs, 1) { Some(rhs) @@ -529,8 +529,8 @@ fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr< Spanned { node: BinOpKind::Sub, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) if is_integer_const(cx, rhs, 1) => Some(lhs), _ => None, } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9656ee64c81..19650c41b84 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .into_results_cursor(mir); let mut possible_borrower = { let mut vis = PossibleBorrowerVisitor::new(cx, mir); - vis.visit_body(&mir); + vis.visit_body(mir); vis.into_map(cx, maybe_storage_live_result) }; @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(ref def, _) = arg_ty.kind() { + if let ty::Adt(def, _) = arg_ty.kind() { if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { continue; } @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { clone_consumed_or_mutated: true, } } else { - let clone_usage = visit_clone_usage(local, ret_local, &mir, bb); + let clone_usage = visit_clone_usage(local, ret_local, mir, bb); if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { // cloned value is used, and the clone is modified or moved continue; @@ -426,7 +426,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, // TODO: Actually check for mutation of non-temporaries. clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, }; - traversal::ReversePostorder::new(&mir, bb) + traversal::ReversePostorder::new(mir, bb) .skip(1) .fold(init, |usage, (tbb, tdata)| { // Short-circuit @@ -588,7 +588,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { // If the call returns something with lifetimes, // let's conservatively assume the returned value contains lifetime of all the arguments. // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - if ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty).is_continue() { + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() { return; } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 5429d389610..92921bedf4d 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -111,8 +111,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { 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 let hir::ExprKind::Call(closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; if self.path.segments[0].ident == path.segments[0].ident; if self.path.res == path.res; then { @@ -133,14 +133,14 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { 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::StmtKind::Local(local) = w[0].kind; + if let Option::Some(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 let hir::StmtKind::Semi(second) = w[1].kind; + if let hir::ExprKind::Assign(_, call, _) = second.kind; + if let hir::ExprKind::Call(closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; if ident == path.segments[0].ident; if count_closure_usage(cx, block, path) == 1; then { diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 0922cfa494e..0cf4e0ce7fe 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(def_id) = res.opt_def_id(); if cx.tcx.is_diagnostic_item(sym::option_type, def_id); - if let Some(ref params) = last_path_segment(qpath).args ; + if let Some(params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(inner_ty) => Some(inner_ty), diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 1cc332de894..4b5306de58e 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -60,7 +60,7 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Call(fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if args.len() == 1; if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -134,7 +134,7 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; if let ExprKind::Array(exprs) = expr.kind; then { for expr in exprs { diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 63e5ec69e66..560a5e7c920 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -45,10 +45,10 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if_chain! { 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(&count); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count); if !in_macro(receiver.span); then { - let ty = cx.typeck_results().expr_ty(&receiver).peel_refs(); + 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/returns.rs b/clippy_lints/src/returns.rs index 8995ae431ad..af772cf4a14 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { err.span_label(local.span, "unnecessary `let` binding"); if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { snippet.push_str(" as _"); } err.multipart_suggestion( @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { check_final_expr(cx, &body.value, Some(body.value.span), replacement) }, FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(ref block, _) = body.value.kind { + if let ExprKind::Block(block, _) = body.value.kind { check_block_return(cx, block); } }, @@ -160,7 +160,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { 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) => { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); }, _ => (), @@ -192,11 +192,11 @@ fn check_final_expr<'tcx>( } }, // a whole block? check it! - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { check_block_return(cx, block); }, ExprKind::If(_, then, else_clause_opt) => { - if let ExprKind::Block(ref ifblock, _) = then.kind { + if let ExprKind::Block(ifblock, _) = then.kind { check_block_return(cx, ifblock); } if let Some(else_clause) = else_clause_opt { @@ -207,16 +207,16 @@ fn check_final_expr<'tcx>( // 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 { + ExprKind::Match(_, arms, source) => match source { MatchSource::Normal => { for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); } }, MatchSource::IfLetDesugar { contains_else_clause: true, } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + if let ExprKind::Block(ifblock, _) = arms[0].body.kind { check_block_return(cx, ifblock); } check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index f61af15fbed..553987a426b 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -51,7 +51,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { return; } - let sugg = sugg::Sugg::hir_with_macro_callsite(cx, &expr, ".."); + let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); let suggestion = format!("{0};", sugg); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 612d2fd84cb..d6101bd5e36 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -130,12 +130,12 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: & let len = bindings.len(); for stmt in block.stmts { match stmt.kind { - StmtKind::Local(ref local) => check_local(cx, local, bindings), - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings), + StmtKind::Local(local) => check_local(cx, local, bindings), + StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings), StmtKind::Item(..) => {}, } } - if let Some(ref o) = block.expr { + if let Some(o) = block.expr { check_expr(cx, o, bindings); } bindings.truncate(len); @@ -149,16 +149,16 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & return; } let Local { - ref pat, + pat, ref ty, ref init, span, .. } = *local; - if let Some(ref t) = *ty { + if let Some(t) = *ty { check_ty(cx, t, bindings) } - if let Some(ref o) = *init { + if let Some(o) = *init { check_expr(cx, o, bindings); check_pat(cx, pat, Some(o), span, bindings); } else { @@ -196,34 +196,34 @@ fn check_pat<'tcx>( bindings.push((name, ident.span)); } } - if let Some(ref p) = *inner { + if let Some(p) = *inner { check_pat(cx, p, init, span, bindings); } }, PatKind::Struct(_, pfields, _) => { if let Some(init_struct) = init { - if let ExprKind::Struct(_, ref efields, _) = init_struct.kind { + if let ExprKind::Struct(_, efields, _) = init_struct.kind { for field in pfields { let name = field.ident.name; let efield = efields .iter() .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None }); - check_pat(cx, &field.pat, efield, span, bindings); + check_pat(cx, field.pat, efield, span, bindings); } } else { for field in pfields { - check_pat(cx, &field.pat, init, span, bindings); + check_pat(cx, field.pat, init, span, bindings); } } } else { for field in pfields { - check_pat(cx, &field.pat, None, span, bindings); + check_pat(cx, field.pat, None, span, bindings); } } }, PatKind::Tuple(inner, _) => { if let Some(init_tup) = init { - if let ExprKind::Tup(ref tup) = init_tup.kind { + if let ExprKind::Tup(tup) = init_tup.kind { for (i, p) in inner.iter().enumerate() { check_pat(cx, p, Some(&tup[i]), p.span, bindings); } @@ -238,10 +238,10 @@ fn check_pat<'tcx>( } } }, - PatKind::Box(ref inner) => { + PatKind::Box(inner) => { if let Some(initp) = init { - if let ExprKind::Box(ref inner_init) = initp.kind { - check_pat(cx, inner, Some(&**inner_init), span, bindings); + if let ExprKind::Box(inner_init) = initp.kind { + check_pat(cx, inner, Some(inner_init), span, bindings); } else { check_pat(cx, inner, init, span, bindings); } @@ -249,7 +249,7 @@ fn check_pat<'tcx>( check_pat(cx, inner, init, span, bindings); } }, - PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings), + PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings), // PatVec(Vec>, Option>, Vec>), _ => (), } @@ -323,11 +323,10 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut return; } match expr.kind { - ExprKind::Unary(_, ref e) - | ExprKind::Field(ref e, _) - | ExprKind::AddrOf(_, _, ref e) - | ExprKind::Box(ref e) => check_expr(cx, e, bindings), - ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => check_block(cx, block, bindings), + ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => { + check_expr(cx, e, bindings) + }, + ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings), // ExprKind::Call // ExprKind::MethodCall ExprKind::Array(v) | ExprKind::Tup(v) => { @@ -335,18 +334,18 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut check_expr(cx, e, bindings) } }, - ExprKind::If(ref cond, ref then, ref otherwise) => { + ExprKind::If(cond, then, ref otherwise) => { check_expr(cx, cond, bindings); - check_expr(cx, &**then, bindings); - if let Some(ref o) = *otherwise { + check_expr(cx, then, bindings); + if let Some(o) = *otherwise { check_expr(cx, o, bindings); } }, - ExprKind::Match(ref init, arms, _) => { + ExprKind::Match(init, arms, _) => { check_expr(cx, init, bindings); let len = bindings.len(); for arm in arms { - check_pat(cx, &arm.pat, Some(&**init), arm.pat.span, bindings); + check_pat(cx, arm.pat, Some(init), arm.pat.span, bindings); // This is ugly, but needed to get the right type if let Some(ref guard) = arm.guard { match guard { @@ -357,7 +356,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut }, } } - check_expr(cx, &arm.body, bindings); + check_expr(cx, arm.body, bindings); bindings.truncate(len); } }, @@ -367,14 +366,12 @@ 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<(Symbol, Span)>) { match ty.kind { - TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), - TyKind::Array(ref fty, ref anon_const) => { + TyKind::Slice(sty) => check_ty(cx, sty, bindings), + TyKind::Array(fty, ref anon_const) => { check_ty(cx, fty, bindings); check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings); }, - TyKind::Ptr(MutTy { ty: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => { - check_ty(cx, mty, bindings) - }, + TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings), TyKind::Tup(tup) => { for t in tup { check_ty(cx, t, bindings) @@ -387,12 +384,12 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( 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, _) => { + ExprKind::Box(inner) | ExprKind::AddrOf(_, _, inner) => is_self_shadow(name, inner), + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e)) }, - ExprKind::Unary(op, ref inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), - ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path), + ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), + ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path), _ => false, } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index d55a83f1613..8cf89ae456e 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -70,14 +70,14 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)` if_chain! { - if let ExprKind::Assign(ref left, ref right, _) = expr.kind; + if let ExprKind::Assign(left, right, _) = expr.kind; // Extract variable name - if let ExprKind::Path(QPath::Resolved(_, ref path)) = left.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; if let Some(variable_name) = path.segments.get(0); // Extract len argument - if let Some(ref len_arg) = Self::is_vec_with_capacity(right); + if let Some(len_arg) = Self::is_vec_with_capacity(right); then { let vi = VecAllocation { @@ -94,10 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; - if let Some(ref init) = local.init; - if let Some(ref len_arg) = Self::is_vec_with_capacity(init); + if let Some(init) = local.init; + if let Some(len_arg) = Self::is_vec_with_capacity(init); then { let vi = VecAllocation { @@ -117,7 +117,7 @@ impl SlowVectorInit { /// of the first argument of `with_capacity` call if it matches or `None` if it does not. fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &["Vec", "with_capacity"]); if args.len() == 1; @@ -208,11 +208,11 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); if path.ident.name == sym!(extend); - if let Some(ref extend_arg) = args.get(1); + if let Some(extend_arg) = args.get(1); if self.is_repeat_take(extend_arg); then { @@ -225,11 +225,11 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); if path.ident.name == sym!(resize); - if let (Some(ref len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); + if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); // Check that is filled with 0 if let ExprKind::Lit(ref lit) = fill_arg.kind; @@ -247,15 +247,15 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&self, expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::MethodCall(ref take_path, _, ref take_args, _) = expr.kind; + if let ExprKind::MethodCall(take_path, _, take_args, _) = expr.kind; if take_path.ident.name == sym!(take); // Check that take is applied to `repeat(0)` - if let Some(ref repeat_expr) = take_args.get(0); + if let Some(repeat_expr) = take_args.get(0); if Self::is_repeat_zero(repeat_expr); // Check that len expression is equals to `with_capacity` expression - if let Some(ref len_arg) = take_args.get(1); + if let Some(len_arg) = take_args.get(1); if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); then { @@ -269,10 +269,10 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::Call(ref fn_expr, ref repeat_args) = expr.kind; + if let ExprKind::Call(fn_expr, repeat_args) = expr.kind; if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; - if match_qpath(&qpath_repeat, &["repeat"]); - if let Some(ref repeat_arg) = repeat_args.get(0); + if match_qpath(qpath_repeat, &["repeat"]); + if let Some(repeat_arg) = repeat_args.get(0); if let ExprKind::Lit(ref lit) = repeat_arg.kind; if let LitKind::Int(0, _) = lit.node; @@ -291,7 +291,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { if self.initialization_found { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { self.search_slow_extend_filling(expr); self.search_slow_resize_filling(expr); }, @@ -306,7 +306,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_block(&mut self, block: &'tcx Block<'_>) { if self.initialization_found { - if let Some(ref s) = block.stmts.get(0) { + if let Some(s) = block.stmts.get(0) { self.visit_stmt(s) } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 99ca7ef77a5..9d91b53e1bb 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { Spanned { node: BinOpKind::Add, .. }, - ref left, + left, _, ) = e.kind { @@ -127,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { let parent = get_parent_expr(cx, e); if let Some(p) = parent { - if let ExprKind::Assign(ref target, _, _) = p.kind { + if let ExprKind::Assign(target, _, _) = p.kind { // avoid duplicate matches if SpanlessEq::new(cx).eq_expr(target, left) { return; @@ -142,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { "you added something to a string. Consider using `String::push_str()` instead", ); } - } else if let ExprKind::Assign(ref target, ref src, _) = e.kind { + } else if let ExprKind::Assign(target, src, _) = e.kind { if is_string(cx, target) && is_add(cx, src, target) { span_lint( cx, @@ -166,10 +166,10 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { Spanned { node: BinOpKind::Add, .. }, - ref left, + left, _, ) => SpanlessEq::new(cx).eq_expr(target, left), - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target)) }, _ => false, @@ -210,8 +210,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); // Find string::as_bytes - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind; - if let ExprKind::Index(ref left, ref right) = args.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; + if let ExprKind::Index(left, right) = args.kind; let (method_names, expressions, _) = method_calls(left, 1); if method_names.len() == 1; if expressions.len() == 1; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 46f423204a2..cb2237e5312 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -225,7 +225,7 @@ fn attempt_to_emit_no_difference_lint( emit_suggestion( cx, binop.span, - replace_left_sugg(cx, &binop, &sugg, &mut applicability), + replace_left_sugg(cx, binop, &sugg, &mut applicability), applicability, ); return; @@ -247,7 +247,7 @@ fn attempt_to_emit_no_difference_lint( emit_suggestion( cx, binop.span, - replace_right_sugg(cx, &binop, &sugg, &mut applicability), + replace_right_sugg(cx, binop, &sugg, &mut applicability), applicability, ); return; @@ -276,8 +276,8 @@ fn ident_swap_sugg( location: IdentLocation, applicability: &mut Applicability, ) -> Option { - let left_ident = get_ident(&binop.left, location)?; - let right_ident = get_ident(&binop.right, location)?; + let left_ident = get_ident(binop.left, location)?; + let right_ident = get_ident(binop.right, location)?; let sugg = match ( paired_identifiers.contains(&left_ident), @@ -293,8 +293,7 @@ fn ident_swap_sugg( // ends up duplicating a clause, the `logic_bug` lint // should catch it. - let right_suggestion = - suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?; replace_right_sugg(cx, binop, &right_suggestion, applicability) }, @@ -302,15 +301,14 @@ fn ident_swap_sugg( // We haven't seen a pair involving the left one, so // it's probably what is wanted. - let right_suggestion = - suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?; replace_right_sugg(cx, binop, &right_suggestion, applicability) }, (true, false) => { // We haven't seen a pair involving the right one, so // it's probably what is wanted. - let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?; + let left_suggestion = suggestion_with_swapped_ident(cx, binop.left, location, right_ident, applicability)?; replace_left_sugg(cx, binop, &left_suggestion, applicability) }, diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 14519eaa962..19967e2c970 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -78,26 +78,26 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(3) { if_chain! { // let t = foo(); - if let StmtKind::Local(ref tmp) = w[0].kind; - if let Some(ref tmp_init) = tmp.init; + if let StmtKind::Local(tmp) = w[0].kind; + if let Some(tmp_init) = tmp.init; if let PatKind::Binding(.., ident, None) = tmp.pat.kind; // foo() = bar(); - if let StmtKind::Semi(ref first) = w[1].kind; - if let ExprKind::Assign(ref lhs1, ref rhs1, _) = first.kind; + if let StmtKind::Semi(first) = w[1].kind; + if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; // bar() = t; - if let StmtKind::Semi(ref second) = w[2].kind; - if let ExprKind::Assign(ref lhs2, ref rhs2, _) = second.kind; - if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; + if let StmtKind::Semi(second) = w[2].kind; + if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; + if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; if ident.name == rhs2.segments[0].ident.name; 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 { + if let ExprKind::Field(lhs1, _) = lhs1.kind { + if let ExprKind::Field(lhs2, _) = lhs2.kind { if lhs1.hir_id.owner == lhs2.hir_id.owner { return; } @@ -192,8 +192,8 @@ enum Slice<'a> { /// Checks if both expressions are index operations into "slice-like" types. 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 let ExprKind::Index(lhs1, idx1) = lhs1.kind { + if let ExprKind::Index(lhs2, idx2) = lhs2.kind { if eq_expr_value(cx, lhs1, lhs2) { let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); @@ -217,11 +217,11 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(2) { if_chain! { - if let StmtKind::Semi(ref first) = w[0].kind; - if let StmtKind::Semi(ref second) = w[1].kind; + if let StmtKind::Semi(first) = w[0].kind; + if let StmtKind::Semi(second) = w[1].kind; 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 let ExprKind::Assign(lhs0, rhs0, _) = first.kind; + if let ExprKind::Assign(lhs1, rhs1, _) = second.kind; if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs1, rhs0); then { diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 88bd2feaadd..a0492a88f91 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -86,7 +86,7 @@ impl TabsInDocComments { impl EarlyLintPass for TabsInDocComments { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) { - Self::warn_if_tabs_in_doc(cx, &attribute); + Self::warn_if_tabs_in_doc(cx, attribute); } } diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 42ec14c31b5..ae05a8da37b 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -92,7 +92,7 @@ impl LateLintPass<'_> for ToStringInDisplay { if_chain! { if self.in_display_impl; if let Some(self_hir_id) = self.self_hir_id; - if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 3ff27c3bcf4..b0589b0512e 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -107,7 +107,7 @@ impl TraitBounds { 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); + let h = hash(p.bounded_ty); if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()); then { @@ -170,7 +170,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !in_macro(bound_predicate.span); - if let TyKind::Path(QPath::Resolved(_, Path { ref segments, .. })) = bound_predicate.bounded_ty.kind; + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 47d58bd30db..86ac916df6c 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -325,7 +325,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path_expr, ref args) = e.kind; + if let ExprKind::Call(path_expr, args) = e.kind; if let ExprKind::Path(ref qpath) = path_expr.kind; 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); diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 1a6124e9ddb..3aa3c393ba5 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( let mut arg = sugg::Sugg::hir(cx, expr, ".."); if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { - expr = &inner_expr; + expr = inner_expr; } if_chain! { diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index c6d0d63b0b5..f359b606e45 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -16,7 +16,7 @@ use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String { let seg = last_path_segment(path); if_chain! { - if let Some(ref params) = seg.args; + if let Some(params) = seg.args; if !params.parenthesized; if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index d42cdde110e..0be05d3e0cf 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -37,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { } if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &paths::STD_MEM_TRANSMUTE); if args.len() == 1; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(0 as *const i32)` if_chain! { - if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].kind; + if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind; if let ExprKind::Lit(ref lit) = inner_expr.kind; if let LitKind::Int(0, _) = lit.node; then { @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::())` if_chain! { - if let ExprKind::Call(ref func1, ref args1) = args[0].kind; + if let ExprKind::Call(func1, args1) = args[0].kind; if let ExprKind::Path(ref path1) = func1.kind; if match_qpath(path1, &paths::STD_PTR_NULL); if args1.is_empty(); diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e4799790d35..23a1953ffac 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -60,13 +60,13 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { // }; if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind; - if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind; + if let ExprKind::Match(match_arg, _, MatchSource::TryDesugar) = expr.kind; + if let ExprKind::Call(match_fun, try_args) = match_arg.kind; if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if matches!(match_fun_path, QPath::LangItem(LangItem::TryIntoResult, _)); - if let Some(ref try_arg) = try_args.get(0); - if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind; - if let Some(ref err_arg) = err_args.get(0); + if let Some(try_arg) = try_args.get(0); + if let ExprKind::Call(err_fun, err_args) = try_arg.kind; + if let Some(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_ty) = find_return_type(cx, &expr.kind); @@ -123,9 +123,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { /// 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 { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { for arm in arms.iter() { - if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + if let ExprKind::Ret(Some(ret)) = arm.body.kind { return Some(cx.typeck_results().expr_ty(ret)); } } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index d68c6db4e23..1425d8f3f37 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -19,9 +19,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m if_chain! { if let Some(def_id) = def.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let QPath::Resolved(None, ref path) = *qpath; + if let QPath::Resolved(None, path) = *qpath; if let [ref bx] = *path.segments; - if let Some(ref params) = bx.args; + if let Some(params) = bx.args; if !params.parenthesized; if let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), @@ -86,11 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(t: &hir::Ty<'_>) -> bool { if_chain! { - if let TyKind::TraitObject(ref traits, ..) = t.kind; + if let TyKind::TraitObject(traits, ..) = t.kind; if !traits.is_empty(); // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT); + if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT); then { return true; } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 12e1eba2ca6..d9b47a699dc 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -278,9 +278,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { match item.kind { - ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => { - self.check_ty(cx, ty, CheckTyContext::default()) - }, + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()), // functions, enums, structs, impls and traits are covered _ => (), } @@ -288,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { match item.kind { - ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_ty( + ImplItemKind::Const(ty, _) | ImplItemKind::TyAlias(ty) => self.check_ty( cx, ty, CheckTyContext { @@ -302,21 +300,21 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - self.check_ty(cx, &field.ty, CheckTyContext::default()); + self.check_ty(cx, field.ty, CheckTyContext::default()); } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { match item.kind { - TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => { + TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { self.check_ty(cx, ty, CheckTyContext::default()) }, - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl, CheckTyContext::default()), + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()), TraitItemKind::Type(..) => (), } } fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if let Some(ref ty) = local.ty { + if let Some(ty) = local.ty { self.check_ty( cx, ty, @@ -342,7 +340,7 @@ impl Types { self.check_ty(cx, input, context); } - if let FnRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ty) = decl.output { self.check_ty(cx, ty, context); } } @@ -383,7 +381,7 @@ impl Types { } } match *qpath { - QPath::Resolved(Some(ref ty), ref p) => { + QPath::Resolved(Some(ty), p) => { context.is_nested_call = true; self.check_ty(cx, ty, context); for ty in p.segments.iter().flat_map(|seg| { @@ -398,7 +396,7 @@ impl Types { self.check_ty(cx, ty, context); } }, - QPath::Resolved(None, ref p) => { + QPath::Resolved(None, p) => { context.is_nested_call = true; for ty in p.segments.iter().flat_map(|seg| { seg.args @@ -412,10 +410,10 @@ impl Types { self.check_ty(cx, ty, context); } }, - QPath::TypeRelative(ref ty, ref seg) => { + QPath::TypeRelative(ty, seg) => { context.is_nested_call = true; self.check_ty(cx, ty, context); - if let Some(ref params) = seg.args { + if let Some(params) = seg.args { for ty in params.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -430,10 +428,10 @@ impl Types { TyKind::Rptr(ref lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { - self.check_ty(cx, &mut_ty.ty, context); + self.check_ty(cx, mut_ty.ty, context); } }, - TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => { + TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => { context.is_nested_call = true; self.check_ty(cx, ty, context) }, diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 9a4e9da3e2b..d8c4b67520d 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -48,9 +48,9 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), // function types bring a lot of overhead - TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), + TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), - TyKind::TraitObject(ref param_bounds, _, _) => { + TyKind::TraitObject(param_bounds, _, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound .bound_generic_params diff --git a/clippy_lints/src/types/utils.rs b/clippy_lints/src/types/utils.rs index 45f891ed718..0fa75f8f0a9 100644 --- a/clippy_lints/src/types/utils.rs +++ b/clippy_lints/src/types/utils.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::Span; pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); if_chain! { - if let Some(ref params) = last.args; + if let Some(params) = last.args; if !params.parenthesized; if let Some(ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index d2c373db261..7a444174626 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -22,7 +22,7 @@ pub(super) fn check( if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) { if_chain! { // Get the _ part of Vec<_> - if let Some(ref last) = last_path_segment(qpath).args; + if let Some(last) = last_path_segment(qpath).args; if let Some(ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -33,7 +33,7 @@ pub(super) fn check( if let Some(def_id) = res.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); // At this point, we know ty is Box, now get T - if let Some(ref last) = last_path_segment(ty_qpath).args; + if let Some(last) = last_path_segment(ty_qpath).args; if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index b6749069176..f4f5e1233e3 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -35,7 +35,7 @@ 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) { + if let Some(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( diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index cdc65abe47c..47b95b18ffb 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -138,7 +138,7 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa 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 { + if let ExprKind::MethodCall(_, _, args, _) = expr.kind { let arg_indices = get_args_to_check(cx, expr); for (i, trait_name) in arg_indices { if i < args.len() { diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 8698a718bbd..fad647dfb26 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -9,8 +9,8 @@ use rustc_middle::lint::in_external_macro; use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { - if cx.typeck_results().pat_ty(&local.pat).is_unit() { + if let StmtKind::Local(local) = stmt.kind { + if cx.typeck_results().pat_ty(local.pat).is_unit() { if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { return; } diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index f77d811c283..57be2d2f674 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -54,7 +54,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::DesugaringKind; - if let ExprKind::Call(ref callee, _) = expr.kind { + if let ExprKind::Call(callee, _) = expr.kind { callee.span.is_desugaring(DesugaringKind::QuestionMark) } else { false diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index b3077dec5d8..85257f3113c 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -9,7 +9,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { if let Some(callee) = expr.span.source_callee() { if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + if let ExprKind::Binary(ref cmp, left, _) = expr.kind { let op = cmp.node; if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { let result = match &*symbol.as_str() { @@ -34,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { return; } - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + if let ExprKind::Binary(ref cmp, left, _) = expr.kind { let op = cmp.node; if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { let result = match op { diff --git a/clippy_lints/src/unit_types/utils.rs b/clippy_lints/src/unit_types/utils.rs index 4e194a05e8d..9a3750b2356 100644 --- a/clippy_lints/src/unit_types/utils.rs +++ b/clippy_lints/src/unit_types/utils.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, ExprKind}; pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) + matches!(expr.kind, ExprKind::Tup(slice) if slice.is_empty()) } diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index 9ceb2c3ffe2..9cca05b1f1a 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -77,7 +77,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if let ExprKind::Binary(binop, left, right) = expr.kind; if is_comparison(binop.node); if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); then { @@ -93,7 +93,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind; + if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PTR_EQ) || @@ -114,7 +114,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if let ExprKind::Binary(binop, left, right) = expr.kind; if is_comparison(binop.node); if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr(); if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index e23bab5eba0..03711eb5b65 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -187,15 +187,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if method_path.ident.name == sym::cmp; then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( - &cx, - &left_expr, - &left_ident, - &right_expr, - &right_ident + cx, + left_expr, + left_ident, + right_expr, + right_ident ) { - (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) { - (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) + (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) { + (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true) } else { return None; }; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index c2be457e9dc..5bb417cb1be 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if_chain! { if !in_macro(ret_expr.span); // Check if a function call. - if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Call(func, args) = ret_expr.kind; // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 9990052e114..024ab03fd41 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -36,13 +36,13 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { let expr = match s.kind { - hir::StmtKind::Semi(ref expr) | hir::StmtKind::Expr(ref expr) => &**expr, + hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, _ => return, }; match expr.kind { - hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => { - if let hir::ExprKind::Call(ref func, ref args) = res.kind { + hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => { + if let hir::ExprKind::Call(func, args) = res.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, - hir::ExprKind::MethodCall(ref path, _, ref args, _) => match &*path.ident.as_str() { + hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { check_method_call(cx, &args[0], expr); }, @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { - if let hir::ExprKind::MethodCall(ref path, _, _, _) = call.kind { + if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = &*path.ident.as_str(); let read_trait = match_trait_method(cx, call, &paths::IO_READ); let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 329ea49024b..ce2d0b3ab2f 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -45,7 +45,7 @@ impl EarlyLintPass for UnusedUnit { fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { if_chain! { - if let Some(ref stmt) = block.stmts.last(); + if let Some(stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; if is_unit_expr(expr) && !stmt.span.from_expansion(); then { diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index fb29acca18a..d4efee56eff 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -166,8 +166,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } else { // find `unwrap[_err]()` calls: if_chain! { - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; + if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind; if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = method_name.ident.name == sym::unwrap; if let Some(unwrappable) = self.unwrappables.iter() diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 116cb8b1e1c..c6a3c58a9a2 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { of_trait, .. }) => { - let should_check = if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind { + let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) @@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); - visitor.visit_ty(&impl_hir_ty); + visitor.visit_ty(impl_hir_ty); types_to_skip.extend(visitor.types_to_skip); } } @@ -333,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // unit enum variants (`Enum::A`) ExprKind::Path(qpath) => { if expr_ty_matches(cx, expr, self_ty) { - span_lint_on_qpath_resolved(cx, &qpath, true); + span_lint_on_qpath_resolved(cx, qpath, true); } }, _ => (), diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index d893b271a20..7edb280be73 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -53,17 +53,17 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } match e.kind { - ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { + ExprKind::Match(_, arms, MatchSource::TryDesugar) => { let e = match arms[0].body.kind { - ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, + ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e, _ => return, }; - if let ExprKind::Call(_, ref args) = e.kind { + if let ExprKind::Call(_, args) = e.kind { self.try_desugar_arm.push(args[0].hir_id); } }, - ExprKind::MethodCall(ref name, .., ref args, _) => { + ExprKind::MethodCall(name, .., args, _) => { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter { if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { if parent_name.ident.name != sym::into_iter { return; } @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } }, - ExprKind::Call(ref path, ref args) => { + ExprKind::Call(path, args) => { if_chain! { if args.len() == 1; if let ExprKind::Path(ref qpath) = path.kind; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index a92c987014f..e70f8a09ebe 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -203,13 +203,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { print!(" if let ExprKind::"); let current = format!("{}.kind", self.current); match expr.kind { - ExprKind::Box(ref inner) => { + ExprKind::Box(inner) => { let inner_pat = self.next("inner"); println!("Box(ref {}) = {};", inner_pat, current); self.current = inner_pat; self.visit_expr(inner); }, - ExprKind::Array(ref elements) => { + ExprKind::Array(elements) => { let elements_pat = self.next("elements"); println!("Array(ref {}) = {};", elements_pat, current); println!(" if {}.len() == {};", elements_pat, elements.len()); @@ -218,7 +218,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(element); } }, - ExprKind::Call(ref func, ref args) => { + ExprKind::Call(func, args) => { let func_pat = self.next("func"); let args_pat = self.next("args"); println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); @@ -230,14 +230,14 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(arg); } }, - ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => { + ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => { 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) => { + ExprKind::Tup(elements) => { let elements_pat = self.next("elements"); println!("Tup(ref {}) = {};", elements_pat, current); println!(" if {}.len() == {};", elements_pat, elements.len()); @@ -246,7 +246,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(element); } }, - ExprKind::Binary(ref op, ref left, ref right) => { + ExprKind::Binary(ref op, left, right) => { let op_pat = self.next("op"); let left_pat = self.next("left"); let right_pat = self.next("right"); @@ -260,7 +260,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = right_pat; self.visit_expr(right); }, - ExprKind::Unary(ref op, ref inner) => { + ExprKind::Unary(ref op, inner) => { let inner_pat = self.next("inner"); println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); self.current = inner_pat; @@ -296,7 +296,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, } }, - ExprKind::Cast(ref expr, ref ty) => { + ExprKind::Cast(expr, ty) => { let cast_pat = self.next("expr"); let cast_ty = self.next("cast_ty"); let qp_label = self.next("qp"); @@ -310,13 +310,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = cast_pat; self.visit_expr(expr); }, - ExprKind::Type(ref expr, ref _ty) => { + ExprKind::Type(expr, _ty) => { let cast_pat = self.next("expr"); println!("Type(ref {}, _) = {};", cast_pat, current); self.current = cast_pat; self.visit_expr(expr); }, - ExprKind::Loop(ref body, _, desugaring, _) => { + ExprKind::Loop(body, _, desugaring, _) => { let body_pat = self.next("body"); let des = loop_desugaring_name(desugaring); let label_pat = self.next("label"); @@ -324,10 +324,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = body_pat; self.visit_block(body); }, - ExprKind::If(ref cond, ref then, ref opt_else) => { + ExprKind::If(cond, then, ref opt_else) => { let cond_pat = self.next("cond"); let then_pat = self.next("then"); - if let Some(ref else_) = *opt_else { + if let Some(else_) = *opt_else { let else_pat = self.next("else_"); println!( "If(ref {}, ref {}, Some(ref {})) = {};", @@ -343,7 +343,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = then_pat; self.visit_expr(then); }, - ExprKind::Match(ref expr, ref arms, desugaring) => { + ExprKind::Match(expr, arms, desugaring) => { let des = desugaring_name(desugaring); let expr_pat = self.next("expr"); let arms_pat = self.next("arms"); @@ -353,18 +353,18 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", arms_pat, arms.len()); for (i, arm) in arms.iter().enumerate() { self.current = format!("{}[{}].body", arms_pat, i); - self.visit_expr(&arm.body); + self.visit_expr(arm.body); if let Some(ref guard) = arm.guard { let guard_pat = self.next("guard"); println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); match guard { - hir::Guard::If(ref if_expr) => { + hir::Guard::If(if_expr) => { let if_expr_pat = self.next("expr"); println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); self.current = if_expr_pat; self.visit_expr(if_expr); }, - hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => { + hir::Guard::IfLet(if_let_pat, if_let_expr) => { let if_let_pat_pat = self.next("pat"); let if_let_expr_pat = self.next("expr"); println!( @@ -379,26 +379,26 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } } self.current = format!("{}[{}].pat", arms_pat, i); - self.visit_pat(&arm.pat); + self.visit_pat(arm.pat); } }, - ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => { + ExprKind::Closure(ref _capture_clause, _func, _, _, _) => { println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); }, - ExprKind::Yield(ref sub, _) => { + ExprKind::Yield(sub, _) => { let sub_pat = self.next("sub"); println!("Yield(ref sub) = {};", current); self.current = sub_pat; self.visit_expr(sub); }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { let block_pat = self.next("block"); println!("Block(ref {}) = {};", block_pat, current); self.current = block_pat; self.visit_block(block); }, - ExprKind::Assign(ref target, ref value, _) => { + ExprKind::Assign(target, value, _) => { let target_pat = self.next("target"); let value_pat = self.next("value"); println!( @@ -410,7 +410,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; self.visit_expr(value); }, - ExprKind::AssignOp(ref op, ref target, ref value) => { + ExprKind::AssignOp(ref op, target, value) => { let op_pat = self.next("op"); let target_pat = self.next("target"); let value_pat = self.next("value"); @@ -424,7 +424,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; self.visit_expr(value); }, - ExprKind::Field(ref object, ref field_ident) => { + ExprKind::Field(object, ref field_ident) => { let obj_pat = self.next("object"); let field_name_pat = self.next("field_name"); println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); @@ -432,7 +432,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = obj_pat; self.visit_expr(object); }, - ExprKind::Index(ref object, ref index) => { + ExprKind::Index(object, index) => { let object_pat = self.next("object"); let index_pat = self.next("index"); println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); @@ -447,7 +447,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = path_pat; self.print_qpath(path); }, - ExprKind::AddrOf(kind, mutability, ref inner) => { + ExprKind::AddrOf(kind, mutability, inner) => { let inner_pat = self.next("inner"); println!( "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", @@ -458,7 +458,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, ExprKind::Break(ref _destination, ref opt_value) => { let destination_pat = self.next("destination"); - if let Some(ref value) = *opt_value { + if let Some(value) = *opt_value { let value_pat = self.next("value"); println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); self.current = value_pat; @@ -474,7 +474,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { // FIXME: implement label printing }, ExprKind::Ret(ref opt_value) => { - if let Some(ref value) = *opt_value { + if let Some(value) = *opt_value { let value_pat = self.next("value"); println!("Ret(Some(ref {})) = {};", value_pat, current); self.current = value_pat; @@ -491,10 +491,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!("LlvmInlineAsm(_) = {};", current); println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); }, - ExprKind::Struct(ref path, ref fields, ref opt_base) => { + ExprKind::Struct(path, fields, ref opt_base) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); - if let Some(ref base) = *opt_base { + if let Some(base) = *opt_base { let base_pat = self.next("base"); println!( "Struct(ref {}, ref {}, Some(ref {})) = {};", @@ -516,7 +516,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; }, // FIXME: compute length (needs type info) - ExprKind::Repeat(ref value, _) => { + ExprKind::Repeat(value, _) => { let value_pat = self.next("value"); println!("Repeat(ref {}, _) = {};", value_pat, current); println!("// unimplemented: repeat count check"); @@ -526,7 +526,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { ExprKind::Err => { println!("Err = {}", current); }, - ExprKind::DropTemps(ref expr) => { + ExprKind::DropTemps(expr) => { let expr_pat = self.next("expr"); println!("DropTemps(ref {}) = {};", expr_pat, current); self.current = expr_pat; @@ -560,7 +560,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { BindingAnnotation::RefMut => "BindingAnnotation::RefMut", }; let name_pat = self.next("name"); - if let Some(ref sub) = *sub { + if let Some(sub) = *sub { let sub_pat = self.next("sub"); println!( "Binding({}, _, {}, Some(ref {})) = {};", @@ -573,7 +573,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); }, - PatKind::Struct(ref path, ref fields, ignore) => { + PatKind::Struct(ref path, fields, ignore) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); println!( @@ -585,13 +585,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::Or(ref fields) => { + PatKind::Or(fields) => { let fields_pat = self.next("fields"); println!("Or(ref {}) = {};", fields_pat, current); println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::TupleStruct(ref path, ref fields, skip_pos) => { + PatKind::TupleStruct(ref path, fields, skip_pos) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); println!( @@ -609,25 +609,25 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = path_pat; self.print_qpath(path); }, - PatKind::Tuple(ref fields, skip_pos) => { + PatKind::Tuple(fields, skip_pos) => { let fields_pat = self.next("fields"); println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::Box(ref pat) => { + PatKind::Box(pat) => { let pat_pat = self.next("pat"); println!("Box(ref {}) = {};", pat_pat, current); self.current = pat_pat; self.visit_pat(pat); }, - PatKind::Ref(ref pat, muta) => { + PatKind::Ref(pat, muta) => { let pat_pat = self.next("pat"); println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); self.current = pat_pat; self.visit_pat(pat); }, - PatKind::Lit(ref lit_expr) => { + PatKind::Lit(lit_expr) => { let lit_expr_pat = self.next("lit_expr"); println!("Lit(ref {}) = {}", lit_expr_pat, current); self.current = lit_expr_pat; @@ -645,10 +645,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = end_pat; walk_list!(self, visit_expr, end); }, - PatKind::Slice(ref start, ref middle, ref end) => { + PatKind::Slice(start, ref middle, end) => { let start_pat = self.next("start"); let end_pat = self.next("end"); - if let Some(ref middle) = middle { + if let Some(middle) = middle { let middle_pat = self.next("middle"); println!( "Slice(ref {}, Some(ref {}), ref {}) = {};", @@ -678,17 +678,17 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { let current = format!("{}.kind", self.current); match s.kind { // A local (let) binding: - StmtKind::Local(ref local) => { + StmtKind::Local(local) => { let local_pat = self.next("local"); println!("Local(ref {}) = {};", local_pat, current); - if let Some(ref init) = local.init { + if let Some(init) = local.init { let init_pat = self.next("init"); println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); self.current = init_pat; self.visit_expr(init); } self.current = format!("{}.pat", local_pat); - self.visit_pat(&local.pat); + self.visit_pat(local.pat); }, // An item binding: StmtKind::Item(_) => { @@ -696,7 +696,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, // Expr without trailing semi-colon (must have unit type): - StmtKind::Expr(ref e) => { + StmtKind::Expr(e) => { let e_pat = self.next("e"); println!("Expr(ref {}, _) = {}", e_pat, current); self.current = e_pat; @@ -704,7 +704,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, // Expr with trailing semi-colon (may have any type): - StmtKind::Semi(ref e) => { + StmtKind::Semi(e) => { let e_pat = self.next("e"); println!("Semi(ref {}, _) = {}", e_pat, current); self.current = e_pat; @@ -752,7 +752,7 @@ fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { fn print_path(path: &QPath<'_>, first: &mut bool) { match *path { - QPath::Resolved(_, ref path) => { + QPath::Resolved(_, path) => { for segment in path.segments { if *first { *first = false; @@ -762,7 +762,7 @@ fn print_path(path: &QPath<'_>, first: &mut bool) { print!("{:?}", segment.ident.as_str()); } }, - QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + QPath::TypeRelative(ty, segment) => match ty.kind { hir::TyKind::Path(ref inner_path) => { print_path(inner_path, first); if *first { diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 6fd3c9d7dec..32d34e8d31e 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { match item.vis.node { hir::VisibilityKind::Public => println!("public"), hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( + hir::VisibilityKind::Restricted { path, .. } => println!( "visible in module `{}`", rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) ), @@ -99,13 +99,13 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { return; } - print_pat(cx, &arm.pat, 1); + print_pat(cx, arm.pat, 1); if let Some(ref guard) = arm.guard { println!("guard:"); print_guard(cx, guard, 1); } println!("body:"); - print_expr(cx, &arm.body, 1); + print_expr(cx, arm.body, 1); } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { @@ -113,17 +113,17 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { return; } match stmt.kind { - hir::StmtKind::Local(ref local) => { + hir::StmtKind::Local(local) => { println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); println!("pattern:"); - print_pat(cx, &local.pat, 0); - if let Some(ref e) = local.init { + print_pat(cx, local.pat, 0); + if let Some(e) = local.init { println!("init expression:"); print_expr(cx, e, 0); } }, hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0), + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), } } // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx @@ -151,7 +151,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { cx.typeck_results().adjustments().get(expr.hir_id) ); match expr.kind { - hir::ExprKind::Box(ref e) => { + hir::ExprKind::Box(e) => { println!("{}Box", ind); print_expr(cx, e, indent + 1); }, @@ -161,7 +161,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Call(ref func, args) => { + hir::ExprKind::Call(func, args) => { println!("{}Call", ind); println!("{}function:", ind); print_expr(cx, func, indent + 1); @@ -170,7 +170,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, arg, indent + 1); } }, - hir::ExprKind::MethodCall(ref path, _, args, _) => { + hir::ExprKind::MethodCall(path, _, args, _) => { println!("{}MethodCall", ind); println!("{}method name: {}", ind, path.ident.name); for arg in args { @@ -183,7 +183,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + hir::ExprKind::Binary(op, lhs, rhs) => { println!("{}Binary", ind); println!("{}op: {:?}", ind, op.node); println!("{}lhs:", ind); @@ -191,7 +191,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::Unary(op, ref inner) => { + hir::ExprKind::Unary(op, inner) => { println!("{}Unary", ind); println!("{}op: {:?}", ind, op); print_expr(cx, inner, indent + 1); @@ -200,12 +200,12 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Lit", ind); println!("{}{:?}", ind, lit); }, - hir::ExprKind::Cast(ref e, ref target) => { + hir::ExprKind::Cast(e, target) => { println!("{}Cast", ind); print_expr(cx, e, indent + 1); println!("{}target type: {:?}", ind, target); }, - hir::ExprKind::Type(ref e, ref target) => { + hir::ExprKind::Type(e, target) => { println!("{}Type", ind); print_expr(cx, e, indent + 1); println!("{}target type: {:?}", ind, target); @@ -213,16 +213,16 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { hir::ExprKind::Loop(..) => { println!("{}Loop", ind); }, - hir::ExprKind::If(ref cond, _, ref else_opt) => { + hir::ExprKind::If(cond, _, ref else_opt) => { println!("{}If", ind); println!("{}condition:", ind); print_expr(cx, cond, indent + 1); - if let Some(ref els) = *else_opt { + if let Some(els) = *else_opt { println!("{}else:", ind); print_expr(cx, els, indent + 1); } }, - hir::ExprKind::Match(ref cond, _, ref source) => { + hir::ExprKind::Match(cond, _, ref source) => { println!("{}Match", ind); println!("{}condition:", ind); print_expr(cx, cond, indent + 1); @@ -232,21 +232,21 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Closure", ind); println!("{}clause: {:?}", ind, clause); }, - hir::ExprKind::Yield(ref sub, _) => { + hir::ExprKind::Yield(sub, _) => { println!("{}Yield", ind); print_expr(cx, sub, indent + 1); }, hir::ExprKind::Block(_, _) => { println!("{}Block", ind); }, - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + hir::ExprKind::Assign(lhs, rhs, _) => { println!("{}Assign", ind); println!("{}lhs:", ind); print_expr(cx, lhs, indent + 1); println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => { + hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { println!("{}AssignOp", ind); println!("{}op: {:?}", ind, binop.node); println!("{}lhs:", ind); @@ -254,31 +254,31 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::Field(ref e, ident) => { + hir::ExprKind::Field(e, ident) => { println!("{}Field", ind); println!("{}field name: {}", ind, ident.name); println!("{}struct expr:", ind); print_expr(cx, e, indent + 1); }, - hir::ExprKind::Index(ref arr, ref idx) => { + hir::ExprKind::Index(arr, idx) => { println!("{}Index", ind); println!("{}array expr:", ind); print_expr(cx, arr, indent + 1); println!("{}index expr:", ind); print_expr(cx, idx, indent + 1); }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { println!("{}Resolved Path, {:?}", ind, ty); println!("{}path: {:?}", ind, path); }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { println!("{}Lang Item Path, {:?}", ind, lang_item.name()); }, - hir::ExprKind::AddrOf(kind, ref muta, ref e) => { + hir::ExprKind::AddrOf(kind, ref muta, e) => { println!("{}AddrOf", ind); println!("kind: {:?}", kind); println!("mutability: {:?}", muta); @@ -286,18 +286,18 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { }, hir::ExprKind::Break(_, ref e) => { println!("{}Break", ind); - if let Some(ref e) = *e { + if let Some(e) = *e { print_expr(cx, e, indent + 1); } }, hir::ExprKind::Continue(_) => println!("{}Again", ind), hir::ExprKind::Ret(ref e) => { println!("{}Ret", ind); - if let Some(ref e) = *e { + if let Some(e) = *e { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::InlineAsm(ref asm) => { + hir::ExprKind::InlineAsm(asm) => { println!("{}InlineAsm", ind); println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); println!("{}options: {:?}", ind, asm.options); @@ -322,7 +322,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { } } }, - hir::ExprKind::LlvmInlineAsm(ref asm) => { + hir::ExprKind::LlvmInlineAsm(asm) => { let inputs = &asm.inputs_exprs; let outputs = &asm.outputs_exprs; println!("{}LlvmInlineAsm", ind); @@ -335,14 +335,14 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Struct(ref path, fields, ref base) => { + hir::ExprKind::Struct(path, fields, ref base) => { println!("{}Struct", ind); println!("{}path: {:?}", ind, path); for field in fields { println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, &field.expr, indent + 1); + print_expr(cx, field.expr, indent + 1); } - if let Some(ref base) = *base { + if let Some(base) = *base { println!("{}base:", ind); print_expr(cx, base, indent + 1); } @@ -352,7 +352,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}anon_const:", ind); print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); }, - hir::ExprKind::Repeat(ref val, ref anon_const) => { + hir::ExprKind::Repeat(val, ref anon_const) => { println!("{}Repeat", ind); println!("{}value:", ind); print_expr(cx, val, indent + 1); @@ -362,7 +362,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { hir::ExprKind::Err => { println!("{}Err", ind); }, - hir::ExprKind::DropTemps(ref e) => { + hir::ExprKind::DropTemps(e) => { println!("{}DropTemps", ind); print_expr(cx, e, indent + 1); }, @@ -375,7 +375,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { match item.vis.node { hir::VisibilityKind::Public => println!("public"), hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( + hir::VisibilityKind::Restricted { path, .. } => println!( "visible in module `{}`", rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) ), @@ -395,7 +395,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { println!("weird extern crate without a crate id"); } }, - hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind), + hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), hir::ItemKind::Fn(..) => { @@ -404,7 +404,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { }, hir::ItemKind::Mod(..) => println!("module"), hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), + hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), hir::ItemKind::TyAlias(..) => { println!("type alias for {:?}", cx.tcx.type_of(did)); }, @@ -454,7 +454,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { println!("{}Binding", ind); println!("{}mode: {:?}", ind, mode); println!("{}name: {}", ind, ident.name); - if let Some(ref inner) = *inner { + if let Some(inner) = *inner { println!("{}inner:", ind); print_pat(cx, inner, indent + 1); } @@ -479,7 +479,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { if field.is_shorthand { println!("{} in shorthand notation", ind); } - print_pat(cx, &field.pat, indent + 1); + print_pat(cx, field.pat, indent + 1); } }, hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { @@ -496,11 +496,11 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, field, indent + 1); } }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { println!("{}Resolved Path, {:?}", ind, ty); println!("{}path: {:?}", ind, path); }, - hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, @@ -516,16 +516,16 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, field, indent + 1); } }, - hir::PatKind::Box(ref inner) => { + hir::PatKind::Box(inner) => { println!("{}Box", ind); print_pat(cx, inner, indent + 1); }, - hir::PatKind::Ref(ref inner, ref muta) => { + hir::PatKind::Ref(inner, ref muta) => { println!("{}Ref", ind); println!("{}mutability: {:?}", ind, muta); print_pat(cx, inner, indent + 1); }, - hir::PatKind::Lit(ref e) => { + hir::PatKind::Lit(e) => { println!("{}Lit", ind); print_expr(cx, e, indent + 1); }, @@ -549,7 +549,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, pat, indent + 1); } println!("i:"); - if let Some(ref pat) = *range { + if let Some(pat) = *range { print_pat(cx, pat, indent + 1); } println!("[y, z]:"); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index d5a13c135b1..cf8039d6059 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -353,12 +353,12 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { return; } - if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { let expr = &cx.tcx.hir().body(body_id).value; if_chain! { - if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; - if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind; + if let ExprKind::Struct(_, fields, _) = inner_exp.kind; let field = fields .iter() .find(|f| f.ident.as_str() == "desc") @@ -385,7 +385,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, - items: ref impl_item_refs, + items: impl_item_refs, .. }) = item.kind { @@ -437,7 +437,7 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { - ty: ref inner, + ty: inner, mutbl: Mutability::Not, }, ) = ty.kind @@ -498,7 +498,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { } if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -577,7 +577,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { } if_chain! { - if let ExprKind::Call(ref func, ref and_then_args) = expr.kind; + if let ExprKind::Call(func, and_then_args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &["span_lint_and_then"]); if and_then_args.len() == 5; @@ -587,7 +587,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { let stmts = &block.stmts; if stmts.len() == 1 && block.expr.is_none(); if let StmtKind::Semi(only_expr) = &stmts[0].kind; - if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; + if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind; then { let and_then_snippets = get_and_then_snippets(cx, and_then_args); let mut sle = SpanlessEq::new(cx).deny_side_effects(); @@ -762,7 +762,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { // 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"]); + 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(); diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index bc2eb88114e..febd4b6ff7b 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { self.check_vec_macro(cx, &vec_args, mutability, expr.span); diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs index e035d3c5cad..5540e87405f 100644 --- a/clippy_lints/src/vec_resize_to_zero.rs +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -30,7 +30,7 @@ declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); impl<'tcx> LateLintPass<'tcx> for VecResizeToZero { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let hir::ExprKind::MethodCall(path_segment, _, ref args, _) = expr.kind; + if let hir::ExprKind::MethodCall(path_segment, _, args, _) = 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::VEC_RESIZE) && args.len() == 3; if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 3b4890ad560..350b1cf25ff 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -31,7 +31,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for instances of 0.0/0.0 if_chain! { - if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind; + if let ExprKind::Binary(ref op, left, right) = expr.kind; if let BinOpKind::Div = op.node; // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 2abd033e2a0..f93f0047f51 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -50,7 +50,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { if !in_trait_impl(cx, hir_ty.hir_id); let ty = ty_from_hir_ty(cx, hir_ty); if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || match_type(cx, ty, &paths::BTREEMAP); - if let Adt(_, ref substs) = ty.kind(); + if let Adt(_, substs) = ty.kind(); let ty = substs.type_at(1); // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. if is_normalizable(cx, cx.param_env, ty); diff --git a/src/driver.rs b/src/driver.rs index b6aed862e89..fa0c5f01430 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -99,17 +99,17 @@ impl rustc_driver::Callbacks for ClippyCallbacks { config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); })); - config.register_lints = Some(Box::new(move |sess, mut lint_store| { + config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that // is there already. Certainly it can't hurt. if let Some(previous) = &previous { (previous)(sess, lint_store); } - 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); - clippy_lints::register_renamed(&mut lint_store); + let conf = clippy_lints::read_conf(&[], sess); + clippy_lints::register_plugins(lint_store, sess, &conf); + clippy_lints::register_pre_expansion_lints(lint_store); + clippy_lints::register_renamed(lint_store); })); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be @@ -191,7 +191,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(¬e); + handler.note_without_error(note); } // If backtraces are enabled, also print the query stack diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0594663786c..f4d354f0bf9 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -98,7 +98,7 @@ fn default_config() -> compiletest::Config { fn run_mode(cfg: &mut compiletest::Config) { cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui"); - compiletest::run_tests(&cfg); + compiletest::run_tests(cfg); } fn run_internal_tests(cfg: &mut compiletest::Config) { @@ -108,7 +108,7 @@ fn run_internal_tests(cfg: &mut compiletest::Config) { } cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui-internal"); - compiletest::run_tests(&cfg); + compiletest::run_tests(cfg); } fn run_ui_toml(config: &mut compiletest::Config) { @@ -136,7 +136,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { base: config.src_base.clone(), relative_dir: dir_path.file_name().unwrap().into(), }; - let test_name = compiletest::make_test_name(&config, &paths); + let test_name = compiletest::make_test_name(config, &paths); let index = tests .iter() .position(|test| test.desc.name == test_name) @@ -150,10 +150,10 @@ fn run_ui_toml(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap(); - let tests = compiletest::make_tests(&config); + let tests = compiletest::make_tests(config); let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); - let res = run_tests(&config, tests); + let res = run_tests(config, tests); set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, @@ -221,7 +221,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { 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 test_name = compiletest::make_test_name(config, &paths); let index = tests .iter() .position(|test| test.desc.name == test_name) @@ -240,11 +240,11 @@ fn run_ui_cargo(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); - let tests = compiletest::make_tests(&config); + let tests = compiletest::make_tests(config); let current_dir = env::current_dir().unwrap(); let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default(); - let res = run_tests(&config, &config.filters, tests); + let res = run_tests(config, &config.filters, tests); env::set_current_dir(current_dir).unwrap(); set_var("CLIPPY_CONF_DIR", conf_dir); diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index ea25729d11d..4327f12c37c 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -1,5 +1,9 @@ #![warn(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::ref_in_deref, + clippy::needless_borrow +)] #![allow(const_item_mutation)] use std::borrow::Cow; diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 9a908cf30e9..f146b97cf61 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:54:5 + --> $DIR/others.rs:58: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/others.rs:55:16 + --> $DIR/others.rs:59: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/others.rs:58:22 + --> $DIR/others.rs:62: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/others.rs:59:25 + --> $DIR/others.rs:63: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/others.rs:60:27 + --> $DIR/others.rs:64: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/others.rs:61:26 + --> $DIR/others.rs:65: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/others.rs:72:14 + --> $DIR/others.rs:76: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/others.rs:73:14 + --> $DIR/others.rs:77: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/others.rs:74:19 + --> $DIR/others.rs:78: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/others.rs:75:14 + --> $DIR/others.rs:79: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/others.rs:76:13 + --> $DIR/others.rs:80: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/others.rs:82:13 + --> $DIR/others.rs:86: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/others.rs:87:5 + --> $DIR/others.rs:91: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/others.rs:88:16 + --> $DIR/others.rs:92:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index 8372a212477..542e6948427 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.rs @@ -1,5 +1,10 @@ #![warn(clippy::collapsible_match)] -#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] +#![allow( + clippy::needless_return, + clippy::no_effect, + clippy::single_match, + clippy::needless_borrow +)] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { // if guards on outer match diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index c8a445ef369..ffef32d1fde 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -1,5 +1,5 @@ error: unnecessary nested match - --> $DIR/collapsible_match2.rs:8:34 + --> $DIR/collapsible_match2.rs:13:34 | LL | Ok(val) if make() => match val { | __________________________________^ @@ -10,7 +10,7 @@ LL | | }, | = note: `-D clippy::collapsible-match` implied by `-D warnings` help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:8:16 + --> $DIR/collapsible_match2.rs:13:16 | LL | Ok(val) if make() => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:15:24 + --> $DIR/collapsible_match2.rs:20:24 | LL | Ok(val) => match val { | ________________________^ @@ -28,7 +28,7 @@ LL | | }, | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:15:16 + --> $DIR/collapsible_match2.rs:20:16 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:29:29 + --> $DIR/collapsible_match2.rs:34:29 | LL | $pat => match $e { | _____________________________^ @@ -49,7 +49,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ------------------------------------------------- in this macro invocation | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:41:28 + --> $DIR/collapsible_match2.rs:46:28 | LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ^^^ ^^^^^^^ with this pattern @@ -58,7 +58,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary nested match - --> $DIR/collapsible_match2.rs:46:20 + --> $DIR/collapsible_match2.rs:51:20 | LL | Some(s) => match *s { | ____________________^ @@ -68,7 +68,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:46:14 + --> $DIR/collapsible_match2.rs:51:14 | LL | Some(s) => match *s { | ^ replace this binding @@ -76,7 +76,7 @@ LL | [n] => foo(n), | ^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:55:24 + --> $DIR/collapsible_match2.rs:60:24 | LL | Some(ref s) => match &*s { | ________________________^ @@ -86,7 +86,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:55:14 + --> $DIR/collapsible_match2.rs:60:14 | LL | Some(ref s) => match &*s { | ^^^^^ replace this binding diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index d26f48fc68f..13e2b6c7a2e 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -101,7 +101,7 @@ fn warn_match() { let x = box A; match &x { // not moved - ref y => (), + y => (), } } @@ -111,7 +111,7 @@ fn nowarn_large_array() { let x = box [1; 10000]; match &x { // not moved - ref y => (), + y => (), } } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 19101522163..cef71cf79d7 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -66,7 +66,7 @@ fn main() { let _ = vec.to_vec(); let vec_ref = &vec; - let _ = return_owned_from_slice(&vec_ref); + let _ = return_owned_from_slice(vec_ref); let _ = vec_ref.to_owned(); let _ = vec_ref.to_vec(); diff --git a/tests/ui/into_iter_on_ref.fixed b/tests/ui/into_iter_on_ref.fixed index 7f92d0dbdc9..b77f17944d8 100644 --- a/tests/ui/into_iter_on_ref.fixed +++ b/tests/ui/into_iter_on_ref.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_borrow)] #![warn(clippy::into_iter_on_ref)] struct X; diff --git a/tests/ui/into_iter_on_ref.rs b/tests/ui/into_iter_on_ref.rs index 416056d3fdb..3854bb05af8 100644 --- a/tests/ui/into_iter_on_ref.rs +++ b/tests/ui/into_iter_on_ref.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_borrow)] #![warn(clippy::into_iter_on_ref)] struct X; diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index b2c275d68af..2df45c927d7 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -9,7 +9,7 @@ static THRESHOLD: i32 = 10; static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); const CONST_THRESHOLD: &i32 = &10; -const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); +const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD); type RefOptRefU32<'a> = &'a Option<&'a u32>; type RefOptRef<'a, T> = &'a Option<&'a T>; diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 4e7fc800061..b61334758e8 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -9,7 +9,7 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:12:18 | -LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); +LL | const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` diff --git a/tests/ui/stable_sort_primitive.fixed b/tests/ui/stable_sort_primitive.fixed index 8f8f5665931..f5f18169df2 100644 --- a/tests/ui/stable_sort_primitive.fixed +++ b/tests/ui/stable_sort_primitive.fixed @@ -20,7 +20,7 @@ fn main() { // 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)); + 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(); diff --git a/tests/ui/stable_sort_primitive.rs b/tests/ui/stable_sort_primitive.rs index f9bd9779067..8149c5638e0 100644 --- a/tests/ui/stable_sort_primitive.rs +++ b/tests/ui/stable_sort_primitive.rs @@ -20,7 +20,7 @@ fn main() { // 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)); + 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(); -- cgit 1.4.1-3-g733a5 From 6b5778eb176deda52a3960cc2029bc93f1424c35 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 26 Mar 2021 22:28:59 -0400 Subject: Fix `explicit_into_iter_loop` Only lint when `into_iter` is an implementation of `IntoIterator` Minor cleanups --- clippy_lints/src/loops/explicit_into_iter_loop.rs | 13 +++++----- clippy_lints/src/loops/explicit_iter_loop.rs | 10 ++++---- clippy_lints/src/loops/mod.rs | 29 ++++++++++------------- tests/ui/for_loop_fixable.fixed | 26 ++++++++++++++++++++ tests/ui/for_loop_fixable.rs | 26 ++++++++++++++++++++ 5 files changed, 77 insertions(+), 27 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4871a031187..c7a28f42ea1 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,24 +1,25 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{match_trait_method, paths}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); - if !TyS::same_type(receiver_ty, receiver_ty_adjusted) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { + let self_ty = cx.typeck_results().expr_ty(self_arg); + let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); + if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) { return; } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); span_lint_and_sugg( cx, EXPLICIT_INTO_ITER_LOOP, - arg.span, + call_expr.span, "it is more concise to loop over containers instead of using explicit \ iteration methods", "to write this more concisely, try", diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 92aa2beb66d..ce02ad013be 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -9,12 +9,12 @@ use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TyS}; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) { let should_lint = match method_name { - "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]), + "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg), "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); + let receiver_ty = cx.typeck_results().expr_ty(self_arg); + let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); let ref_receiver_ty = cx.tcx.mk_ref( cx.tcx.lifetimes.re_erased, ty::TypeAndMut { @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); let muta = if method_name == "iter_mut" { "mut " } else { "" }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 28acefd51fe..a4bc3e6bd10 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -602,22 +602,19 @@ fn check_for_loop<'tcx>( fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - if let ExprKind::MethodCall(method, _, args, _) = arg.kind { - // just the receiver, no arguments - if args.len() == 1 { - let method_name = &*method.ident.as_str(); - // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x - match method_name { - "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name), - "into_iter" => { - explicit_iter_loop::check(cx, args, arg, method_name); - explicit_into_iter_loop::check(cx, args, arg); - }, - "next" => { - next_loop_linted = iter_next_loop::check(cx, arg, expr); - }, - _ => {}, - } + if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind { + let method_name = &*method.ident.as_str(); + // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x + match method_name { + "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name), + "into_iter" => { + explicit_iter_loop::check(cx, self_arg, arg, method_name); + explicit_into_iter_loop::check(cx, self_arg, arg); + }, + "next" => { + next_loop_linted = iter_next_loop::check(cx, arg, expr); + }, + _ => {}, } } diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index 249a88a0b39..f44928d4083 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -281,3 +281,29 @@ mod issue_4958 { for _ in rr.into_iter() {} } } + +// explicit_into_iter_loop +#[warn(clippy::explicit_into_iter_loop)] +mod issue_6900 { + struct S; + impl S { + #[allow(clippy::should_implement_trait)] + pub fn into_iter(self) -> I { + unimplemented!() + } + } + + struct I(T); + impl Iterator for I { + type Item = T; + fn next(&mut self) -> Option { + unimplemented!() + } + } + + fn f() { + for _ in S.into_iter::() { + unimplemented!() + } + } +} diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 306d85a6351..5b1eb3ee4dc 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -281,3 +281,29 @@ mod issue_4958 { for _ in rr.into_iter() {} } } + +// explicit_into_iter_loop +#[warn(clippy::explicit_into_iter_loop)] +mod issue_6900 { + struct S; + impl S { + #[allow(clippy::should_implement_trait)] + pub fn into_iter(self) -> I { + unimplemented!() + } + } + + struct I(T); + impl Iterator for I { + type Item = T; + fn next(&mut self) -> Option { + unimplemented!() + } + } + + fn f() { + for _ in S.into_iter::() { + unimplemented!() + } + } +} -- cgit 1.4.1-3-g733a5 From 47f0c15f67e334338b82deef1e812f157a54dcf2 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 24 Mar 2021 09:31:43 -0500 Subject: Symbol optimizations --- clippy_lints/src/attrs.rs | 112 +++++++++++++++----------------- clippy_lints/src/excessive_bools.rs | 6 +- clippy_lints/src/functions/must_use.rs | 6 +- clippy_lints/src/macro_use.rs | 6 +- clippy_lints/src/methods/or_fun_call.rs | 6 +- clippy_lints/src/option_env_unwrap.rs | 4 +- clippy_utils/src/lib.rs | 9 +-- 7 files changed, 67 insertions(+), 82 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 382fb03d920..c5b01461c1c 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -250,12 +250,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { if let Some(items) = &attr.meta_item_list() { if let Some(ident) = attr.ident() { - let ident = &*ident.as_str(); - match ident { - "allow" | "warn" | "deny" | "forbid" => { - check_clippy_lint_names(cx, ident, items); - }, - _ => {}, + if is_lint_level(ident.name) { + check_clippy_lint_names(cx, ident.name, items); } if items.is_empty() || !attr.has_name(sym::deprecated) { return; @@ -288,60 +284,54 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { return; } if let Some(lint_list) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - match &*ident.as_str() { - "allow" | "warn" | "deny" | "forbid" => { - // 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 { - ItemKind::Use(..) => { - if is_word(lint, sym!(unused_imports)) - || 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; - } - }, - ItemKind::ExternCrate(..) => { - if is_word(lint, sym!(unused_imports)) && skip_unused_imports { - return; - } - if is_word(lint, sym!(unused_extern_crates)) { - return; - } - }, - _ => {}, + if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { + // 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 { + ItemKind::Use(..) => { + if is_word(lint, sym!(unused_imports)) + || 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; + } + }, + ItemKind::ExternCrate(..) => { + if is_word(lint, sym!(unused_imports)) && skip_unused_imports { + return; } - } - let line_span = first_line_of_span(cx, attr.span); - - if let Some(mut sugg) = snippet_opt(cx, line_span) { - if sugg.contains("#[") { - span_lint_and_then( - cx, - USELESS_ATTRIBUTE, + if is_word(lint, sym!(unused_extern_crates)) { + return; + } + }, + _ => {}, + } + } + let line_span = first_line_of_span(cx, attr.span); + + if let Some(mut sugg) = snippet_opt(cx, line_span) { + if sugg.contains("#[") { + span_lint_and_then( + cx, + USELESS_ATTRIBUTE, + line_span, + "useless lint attribute", + |diag| { + sugg = sugg.replacen("#[", "#![", 1); + diag.span_suggestion( line_span, - "useless lint attribute", - |diag| { - sugg = sugg.replacen("#[", "#![", 1); - diag.span_suggestion( - line_span, - "if you just forgot a `!`, use", - sugg, - Applicability::MaybeIncorrect, - ); - }, + "if you just forgot a `!`, use", + sugg, + Applicability::MaybeIncorrect, ); - } - } - }, - _ => {}, + }, + ); + } } } } @@ -379,10 +369,10 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { None } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { +fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) { for lint in items { if let Some(lint_name) = extract_clippy_lint(lint) { - if lint_name == "restriction" && ident != "allow" { + if lint_name == "restriction" && name != sym::allow { span_lint_and_help( cx, BLANKET_CLIPPY_RESTRICTION_LINTS, @@ -647,3 +637,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } } } + +fn is_lint_level(symbol: Symbol) -> bool { + matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid) +} diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 6c9652fd87d..249ee27330b 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{attr_by_name, in_macro, match_path_ast}; +use clippy_utils::{in_macro, match_path_ast}; use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::convert::TryInto; @@ -138,7 +138,7 @@ impl EarlyLintPass for ExcessiveBools { } match &item.kind { ItemKind::Struct(variant_data, _) => { - if attr_by_name(&item.attrs, "repr").is_some() { + if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { return; } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index db5fe90b663..9ea8e2eaa2a 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -8,13 +8,13 @@ use rustc_middle::{ lint::in_external_macro, ty::{self, Ty}, }; -use rustc_span::Span; +use rustc_span::{sym, Span}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{attr_by_name, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; +use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method}; use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; @@ -27,7 +27,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); return; - } else if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { + } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { check_must_use_candidate( cx, sig.decl, diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573c297838..c506d52e746 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -9,7 +9,7 @@ use rustc_errors::Applicability; 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}; +use rustc_span::{edition::Edition, sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for `#[macro_use] use...`. @@ -110,9 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { if cx.sess().opts.edition >= Edition::Edition2018; if let hir::ItemKind::Use(path, _kind) = &item.kind; let attrs = cx.tcx.hir().attrs(item.hir_id()); - if let Some(mac_attr) = attrs - .iter() - .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); if let Res::Def(DefKind::Mod, id) = path.res; then { for kid in cx.tcx.item_children(id).iter() { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index df89da5d3e0..800172f4cf3 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -10,7 +10,7 @@ use rustc_hir::{BlockCheckMode, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; use std::borrow::Cow; use super::OR_FUN_CALL; @@ -38,8 +38,8 @@ pub(super) fn check<'tcx>( if !or_has_args; if name == "unwrap_or"; if let hir::ExprKind::Path(ref qpath) = fun.kind; - let path = &*last_path_segment(qpath).ident.as_str(); - if ["default", "new"].contains(&path); + let path = last_path_segment(qpath).ident.name; + if matches!(path, kw::Default | sym::new); let arg_ty = cx.typeck_results().expr_ty(arg); if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); if implements_trait(cx, arg_ty, default_trait_id, &[]); diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index a0bc324e026..b6f518661bd 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -4,6 +4,7 @@ use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `option_env!(...).unwrap()` and @@ -37,8 +38,7 @@ impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if_chain! { if let ExprKind::MethodCall(path_segment, args, _) = &expr.kind; - let method_name = path_segment.ident.as_str(); - if method_name == "expect" || method_name == "unwrap"; + if matches!(path_segment.ident.name, sym::expect | sym::unwrap); if let ExprKind::Call(caller, _) = &args[0].kind; if is_direct_expn_of(caller.span, "option_env").is_some(); then { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 64f86f5f101..6088cd323c4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1203,16 +1203,9 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { ) } -// Finds the attribute with the given name, if any -pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> { - attrs - .iter() - .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name)) -} - // Finds the `#[must_use]` attribute, if any pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { - attr_by_name(attrs, "must_use") + attrs.iter().find(|a| a.has_name(sym::must_use)) } // check if expr is calling method or function with #[must_use] attribute -- cgit 1.4.1-3-g733a5 From 7468542328caf3f0114ac4733fe2b76e2ed79954 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 17:09:58 -0500 Subject: Introduce is_lang_ctor --- clippy_lints/src/collapsible_match.rs | 27 ++++------- clippy_lints/src/if_then_some_else_none.rs | 9 ++-- clippy_lints/src/loops/manual_flatten.rs | 11 +++-- clippy_lints/src/manual_map.rs | 35 ++++---------- clippy_lints/src/manual_ok_or.rs | 9 ++-- clippy_lints/src/manual_unwrap_or.rs | 25 +++++----- clippy_lints/src/matches.rs | 38 +++++++-------- clippy_lints/src/mem_replace.rs | 7 +-- clippy_lints/src/methods/option_map_or_none.rs | 7 +-- clippy_lints/src/methods/unnecessary_filter_map.rs | 7 +-- clippy_lints/src/needless_question_mark.rs | 8 ++-- clippy_lints/src/option_if_let_else.rs | 6 +-- clippy_lints/src/question_mark.rs | 17 ++----- clippy_lints/src/try_err.rs | 5 +- clippy_lints/src/unnecessary_wraps.rs | 11 +++-- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_utils/src/lib.rs | 54 +++++++++------------- 17 files changed, 121 insertions(+), 157 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 04fff237bb4..ab22578abd6 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{path_to_local, SpanlessEq}; +use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq}; use if_chain::if_chain; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; +use rustc_hir::LangItem::OptionNone; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -52,7 +52,7 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Match(_expr, arms, _source) = expr.kind { - if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) { + if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { check_arm(arm, wild_arm, cx); } @@ -75,7 +75,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext // match { .. } if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results())); // one of the branches must be "wild-like" - if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner)); let (wild_inner_arm, non_wild_inner_arm) = (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); if !pat_contains_or(non_wild_inner_arm.pat); @@ -126,13 +126,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> /// A "wild-like" pattern is wild ("_") or `None`. /// For this lint to apply, both the outer and inner match expressions /// must have "wild-like" branches that can be combined. -fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool { +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if arm.guard.is_some() { return false; } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true, + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, } } @@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool { result } -fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { - if let Some(none_id) = tcx.lang_items().option_none_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res { - if let Some(variant_id) = tcx.parent(id) { - return variant_id == none_id; - } - } - } - false -} - /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed. fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> { diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index ee16519692f..f73b3a1fe67 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr}; +use clippy_utils::{is_lang_ctor, meets_msrv, parent_node_is_if_expr}; use if_chain::if_chain; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -77,12 +78,12 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let Some(then_expr) = then_block.expr; if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; - if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); + if is_lang_ctor(cx, then_call_qpath, OptionSome); if let ExprKind::Block(els_block, _) = els.kind; if els_block.stmts.is_empty(); if let Some(els_expr) = els_block.expr; - if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; - if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); + if let ExprKind::Path(ref qpath) = els_expr.kind; + if is_lang_ctor(cx, qpath, OptionNone); then { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 94743cfcf46..64ff7574f86 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,10 +1,11 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; +use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -42,9 +43,9 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(match_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; - let some_ctor = is_some_ctor(cx, path.res); - let ok_ctor = is_ok_ctor(cx, path.res); + if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind; + let some_ctor = is_lang_ctor(cx, qpath, OptionSome); + let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 8c9e3af62f4..29be0739977 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,9 +2,10 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ def::Res, intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, @@ -269,20 +270,9 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(QPath::Resolved(None, path)) - if path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) => - { - Some(OptionPat::None) - }, - PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _) - if path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) - && pat.span.ctxt() == ctxt => + PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -298,17 +288,11 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte match expr.kind { ExprKind::Call( Expr { - kind: ExprKind::Path(QPath::Resolved(None, path)), + kind: ExprKind::Path(ref qpath), .. }, [arg], - ) if ctxt == expr.span.ctxt() => { - if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { - Some(arg) - } else { - None - } - }, + ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg), ExprKind::Block( Block { stmts: [], @@ -324,10 +308,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(QPath::Resolved(None, path)) => path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)), + ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), ExprKind::Block( Block { stmts: [], diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index 9bfae602c40..847c8c648b0 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; @@ -54,7 +55,7 @@ impl LateLintPass<'_> for ManualOkOr { let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; - if match_qpath(err_path, &paths::RESULT_ERR); + if is_lang_ctor(cx, err_path, ResultErr); if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); if let Some(indent) = indent_of(cx, scrutinee.span); @@ -81,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr { fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { if let ExprKind::Path(ref qpath) = map_expr.kind { - if match_qpath(qpath, &paths::RESULT_OK) { + if is_lang_ctor(cx, qpath, ResultOk) { return true; } } @@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir().body(body_id); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if match_qpath(ok_path, &paths::RESULT_OK); + if is_lang_ctor(cx, ok_path, ResultOk); then { path_to_local_id(ok_arg, param_id) } else { false } } } diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index f296d6a1a15..65baa2552cc 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg}; +use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -68,23 +69,21 @@ impl Case { } 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>> { + fn applicable_or_arm<'a>(cx: &LateContext<'_>, 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, or_arm)) = arms.iter().enumerate().find(|(_, arm)| + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { match arm.pat.kind { - PatKind::Path(ref some_qpath) => - match_qpath(some_qpath, &paths::OPTION_NONE), - PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => - match_qpath(err_qpath, &paths::RESULT_ERR), + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, &[pat], _) => + matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), _ => false, } - ); + }); let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; - if match_qpath(unwrap_qpath, &paths::OPTION_SOME) - || match_qpath(unwrap_qpath, &paths::RESULT_OK); + if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if !contains_return_break_continue_macro(or_arm.body); @@ -106,7 +105,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { } else { None }; - if let Some(or_arm) = applicable_or_arm(match_arms); + if let Some(or_arm) = applicable_or_arm(cx, match_arms); if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); if let Some(indent) = indent_of(cx, expr.span); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index a892e24482b..72f5eed6531 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -7,7 +7,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; @@ -15,6 +15,7 @@ use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, @@ -1188,10 +1189,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { - let arm_ref: Option = if is_none_arm(&arms[0]) { - is_ref_some_arm(&arms[1]) - } else if is_none_arm(&arms[1]) { - is_ref_some_arm(&arms[0]) + let arm_ref: Option = if is_none_arm(cx, &arms[0]) { + is_ref_some_arm(cx, &arms[1]) + } else if is_none_arm(cx, &arms[1]) { + is_ref_some_arm(cx, &arms[0]) } else { None }; @@ -1574,20 +1575,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool { } // Checks if arm has the form `None => None` -fn is_none_arm(arm: &Arm<'_>) -> bool { - matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE)) +fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) -fn is_ref_some_arm(arm: &Arm<'_>) -> Option { +fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if_chain! { - if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; - if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); + if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome); if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; - if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; + if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1; if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { @@ -1699,10 +1700,11 @@ mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; - use clippy_utils::{is_trait_method, match_qpath, paths}; + use clippy_utils::{is_lang_ctor, is_trait_method, match_qpath, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; + use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -1734,13 +1736,13 @@ mod redundant_pattern_match { let good_method = match kind { PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { - if match_qpath(path, &paths::RESULT_OK) { + if is_lang_ctor(cx, path, ResultOk) { "is_ok()" - } else if match_qpath(path, &paths::RESULT_ERR) { + } else if is_lang_ctor(cx, path, ResultErr) { "is_err()" - } else if match_qpath(path, &paths::OPTION_SOME) { + } else if is_lang_ctor(cx, path, OptionSome) { "is_some()" - } else if match_qpath(path, &paths::POLL_READY) { + } else if is_lang_ctor(cx, path, PollReady) { "is_ready()" } else if match_qpath(path, &paths::IPADDR_V4) { "is_ipv4()" @@ -1754,9 +1756,9 @@ mod redundant_pattern_match { } }, PatKind::Path(ref path) => { - if match_qpath(path, &paths::OPTION_NONE) { + if is_lang_ctor(cx, path, OptionNone) { "is_none()" - } else if match_qpath(path, &paths::POLL_PENDING) { + } else if is_lang_ctor(cx, path, PollPending) { "is_pending()" } else { return; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e1d351aee45..1f521ec6090 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_diagnostic_assoc_item; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; +use clippy_utils::{in_macro, match_def_path, meets_msrv, paths}; +use clippy_utils::{is_diagnostic_assoc_item, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -102,7 +103,7 @@ impl_lint_pass!(MemReplace => fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { if let ExprKind::Path(ref replacement_qpath) = src.kind { // Check that second argument is `Option::None` - if match_qpath(replacement_qpath, &paths::OPTION_NONE) { + if is_lang_ctor(cx, replacement_qpath, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 013a6f90ac9..36a1c13d5be 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_qpath, paths}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -32,7 +33,7 @@ pub(super) fn check<'tcx>( let (lint_name, msg, instead, hint) = { let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { - match_qpath(qpath, &paths::OPTION_NONE) + is_lang_ctor(cx, qpath, OptionNone) } else { return; }; @@ -43,7 +44,7 @@ pub(super) fn check<'tcx>( } let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { - match_qpath(qpath, &paths::OPTION_SOME) + is_lang_ctor(cx, qpath, OptionSome) } else { false }; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 0f28bfdf09e..66255b77331 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::usage::mutated_variables; -use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::sym; @@ -54,7 +55,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc match &expr.kind { hir::ExprKind::Call(func, args) => { if let hir::ExprKind::Path(ref path) = func.kind { - if match_qpath(path, &paths::OPTION_SOME) { + if is_lang_ctor(cx, path, OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } @@ -85,7 +86,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true), + hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true), _ => (true, true), } } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 9852633b734..cfe7ae6630e 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv}; +use clippy_utils::{differing_macro_contexts, meets_msrv}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_semver::RustcVersion; @@ -159,8 +161,8 @@ fn is_some_or_ok_call<'a>( if_chain! { // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + if let ExprKind::Path(ref qpath) = &path.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 1b9120ae45f..e527adbb892 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath}; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -164,7 +164,7 @@ fn detect_option_if_let_else<'tcx>( if arms.len() == 2; 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 match_qpath(struct_qpath, &paths::OPTION_SOME); + if is_lang_ctor(cx, struct_qpath, OptionSome); 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); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 6d720f43851..ea9ff37e13f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths}; +use clippy_utils::{eq_expr_value, match_qpath}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; +use rustc_hir::LangItem::OptionNone; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -156,15 +157,7 @@ impl QuestionMark { false }, ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), - ExprKind::Path(ref qp) => { - if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = - cx.qpath_res(qp, expression.hir_id) - { - return match_def_path(cx, def_id, &paths::OPTION_NONE); - } - - false - }, + ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, } } diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 23a1953ffac..0b8c03c6865 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; +use clippy_utils::{differing_macro_contexts, in_macro, is_lang_ctor, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let ExprKind::Call(err_fun, err_args) = try_arg.kind; if let Some(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 is_lang_ctor(cx, err_fun_path, ResultErr); if let Some(return_ty) = find_return_type(cx, &expr.kind); then { let prefix; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5bb417cb1be..d2c0f60d296 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; +use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -85,11 +86,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } // Get the wrapper and inner types, if can't, abort. - let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { + let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) { - ("Option", &paths::OPTION_SOME, subst.type_at(0)) + ("Option", OptionSome, subst.type_at(0)) } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) { - ("Result", &paths::RESULT_OK, subst.type_at(0)) + ("Result", ResultOk, subst.type_at(0)) } else { return; } @@ -107,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. - if match_qpath(qpath, path); + if is_lang_ctor(cx, qpath, lang_item); if args.len() == 1; // Make sure the function argument does not contain a return expression. if !contains_return(&args[0]); diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 024ab03fd41..5e8e530f480 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { }; match expr.kind { - hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => { + hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { if let hir::ExprKind::Call(func, args) = res.kind { if matches!( func.kind, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 64f86f5f101..1d61b7dd412 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -56,9 +56,10 @@ use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, @@ -221,6 +222,19 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { } } +/// Checks if a `QPath` resolves to a constructor of a `LangItem`. +/// For example, use this to check whether a function call or a pattern is `Some(..)`. +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool { + if let QPath::Resolved(_, path) = qpath { + if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { + if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { + return cx.tcx.parent(ctor_id) == Some(item_id); + } + } + } + false +} + /// Returns `true` if this `span` was expanded by any macro. #[must_use] pub fn in_macro(span: Span) -> bool { @@ -1010,11 +1024,11 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It /// Checks if a given expression is a match expression expanded from the `?` /// operator or the `try` macro. -pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - fn is_ok(arm: &Arm<'_>) -> bool { +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if_chain! { if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind; - if match_qpath(path, &paths::RESULT_OK[1..]); + if is_lang_ctor(cx, path, ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id); then { @@ -1024,9 +1038,9 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { false } - fn is_err(arm: &Arm<'_>) -> bool { + fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - match_qpath(path, &paths::RESULT_ERR[1..]) + is_lang_ctor(cx, path, ResultErr) } else { false } @@ -1042,8 +1056,8 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if arms.len() == 2; if arms[0].guard.is_none(); if arms[1].guard.is_none(); - if (is_ok(&arms[0]) && is_err(&arms[1])) || - (is_ok(&arms[1]) && is_err(&arms[0])); + if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || + (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])); then { return Some(expr); } @@ -1456,27 +1470,3 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } } - -/// Check if the resolution of a given path is an `Ok` variant of `Result`. -pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == ok_id; - } - } - } - false -} - -/// Check if the resolution of a given path is a `Some` variant of `Option`. -pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == some_id; - } - } - } - false -} -- cgit 1.4.1-3-g733a5 From 4f7fc11ef17e3afb0175b965995cad2f9d7ab210 Mon Sep 17 00:00:00 2001 From: boxdot Date: Sun, 18 Oct 2020 13:42:09 +0200 Subject: Add invalid null pointer usage lint. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/ptr.rs | 95 +++++++++++++++-- clippy_lints/src/size_of_in_element_count.rs | 4 +- clippy_utils/src/paths.rs | 13 ++- tests/ui/invalid_null_ptr_usage.fixed | 49 +++++++++ tests/ui/invalid_null_ptr_usage.rs | 49 +++++++++ tests/ui/invalid_null_ptr_usage.stderr | 154 +++++++++++++++++++++++++++ 8 files changed, 353 insertions(+), 15 deletions(-) create mode 100644 tests/ui/invalid_null_ptr_usage.fixed create mode 100644 tests/ui/invalid_null_ptr_usage.rs create mode 100644 tests/ui/invalid_null_ptr_usage.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 73997192ae0..715f4702e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2267,6 +2267,7 @@ Released 2018-09-13 [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cd92b551abd..2fb8bbcd1f7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -902,6 +902,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: pattern_type_mismatch::PATTERN_TYPE_MISMATCH, precedence::PRECEDENCE, ptr::CMP_NULL, + ptr::INVALID_NULL_PTR_USAGE, ptr::MUT_FROM_REF, ptr::PTR_ARG, ptr_eq::PTR_EQ, @@ -1671,6 +1672,7 @@ 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::CMP_NULL), + LintId::of(ptr::INVALID_NULL_PTR_USAGE), LintId::of(ptr::MUT_FROM_REF), LintId::of(ptr::PTR_ARG), LintId::of(ptr_eq::PTR_EQ), @@ -2010,6 +2012,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(ptr::INVALID_NULL_PTR_USAGE), LintId::of(ptr::MUT_FROM_REF), LintId::of(ranges::REVERSED_EMPTY_RANGES), LintId::of(regex::INVALID_REGEX), diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 09fcdb5faf8..2ab1e958ec8 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; -use clippy_utils::{is_allowed, match_qpath, paths}; +use clippy_utils::{is_allowed, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; use rustc_span::{sym, MultiSpan}; use std::borrow::Cow; @@ -94,7 +95,7 @@ declare_clippy_lint! { /// ``` pub CMP_NULL, style, - "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead." + "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead" } declare_clippy_lint! { @@ -119,7 +120,28 @@ declare_clippy_lint! { "fns that create mutable refs from immutable ref args" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); +declare_clippy_lint! { + /// **What it does:** This lint checks for invalid usages of `ptr::null`. + /// + /// **Why is this bad?** This causes undefined behavior. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// // Bad. Undefined behavior + /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } + /// ``` + /// + /// // Good + /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } + /// ``` + pub INVALID_NULL_PTR_USAGE, + correctness, + "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -153,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind { - if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) { + if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) { span_lint( cx, CMP_NULL, @@ -161,6 +183,55 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { "comparing with null is better expressed by the `.is_null()` method", ); } + } else { + check_invalid_ptr_usage(cx, expr); + } + } +} + +fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B. + const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [ + (&paths::SLICE_FROM_RAW_PARTS, &[0]), + (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]), + (&paths::PTR_COPY, &[0, 1]), + (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]), + (&paths::PTR_READ, &[0]), + (&paths::PTR_READ_UNALIGNED, &[0]), + (&paths::PTR_READ_VOLATILE, &[0]), + (&paths::PTR_REPLACE, &[0]), + (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]), + (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]), + (&paths::PTR_SWAP, &[0, 1]), + (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]), + (&paths::PTR_WRITE, &[0]), + (&paths::PTR_WRITE_UNALIGNED, &[0]), + (&paths::PTR_WRITE_VOLATILE, &[0]), + (&paths::PTR_WRITE_BYTES, &[0]), + ]; + + if_chain! { + if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::>(); + if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE + .iter() + .find(|&&(fn_path, _)| fn_path == fun_def_path); + then { + for &arg_idx in arg_indices { + if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) { + span_lint_and_sugg( + cx, + INVALID_NULL_PTR_USAGE, + arg.span, + "pointer must be non-null", + "change this to", + "core::ptr::NonNull::dangling().as_ptr()".to_string(), + Applicability::MachineApplicable, + ); + } + } } } } @@ -345,13 +416,15 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } } -fn is_null_path(expr: &Expr<'_>) -> bool { - if let ExprKind::Call(pathexp, args) = expr.kind { - if args.is_empty() { - if let ExprKind::Path(ref path) = pathexp.kind { - return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); - } +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::Call(path, []) = expr.kind; + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(fn_def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + then { + match_def_path(cx, fn_def_id, &paths::PTR_NULL) || match_def_path(cx, fn_def_id, &paths::PTR_NULL_MUT) + } else { + false } } - false } diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 09e00866815..cd2bdec1707 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -65,8 +65,8 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ - &paths::COPY_NONOVERLAPPING, - &paths::COPY, + &paths::PTR_COPY_NONOVERLAPPING, + &paths::PTR_COPY, &paths::WRITE_BYTES, &paths::PTR_SWAP_NONOVERLAPPING, &paths::PTR_SLICE_FROM_RAW_PARTS, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 3b4c4070c0e..5da9c624ac4 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -18,8 +18,6 @@ pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeS pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; -pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"]; -pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; @@ -100,12 +98,23 @@ pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; +pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"]; +pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"]; pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"]; pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"]; +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"]; +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"]; +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed new file mode 100644 index 00000000000..4f5322ebf20 --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.fixed @@ -0,0 +1,49 @@ +// run-rustfix + +fn main() { + unsafe { + let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + struct A; // zero sized struct + assert_eq!(std::mem::size_of::(), 0); + + let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); + std::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); + + std::ptr::swap_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); + std::ptr::swap_nonoverlapping::(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_bytes::(core::ptr::NonNull::dangling().as_ptr(), 42, 0); + } +} diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs new file mode 100644 index 00000000000..ae51c52d8af --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.rs @@ -0,0 +1,49 @@ +// run-rustfix + +fn main() { + unsafe { + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); + + let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + + std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + + std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + + struct A; // zero sized struct + assert_eq!(std::mem::size_of::(), 0); + + let _a: A = std::ptr::read(std::ptr::null()); + let _a: A = std::ptr::read(std::ptr::null_mut()); + + let _a: A = std::ptr::read_unaligned(std::ptr::null()); + let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); + + let _a: A = std::ptr::read_volatile(std::ptr::null()); + let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); + + let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); + + std::ptr::swap::(std::ptr::null_mut(), &mut A); + std::ptr::swap::(&mut A, std::ptr::null_mut()); + + std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); + std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); + + std::ptr::write(std::ptr::null_mut(), A); + + std::ptr::write_unaligned(std::ptr::null_mut(), A); + + std::ptr::write_volatile(std::ptr::null_mut(), A); + + std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + } +} diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr new file mode 100644 index 00000000000..532c36abe51 --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.stderr @@ -0,0 +1,154 @@ +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:5:59 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | + = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:6:59 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:8:63 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:10:33 + | +LL | std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:11:73 + | +LL | std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:13:48 + | +LL | std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:14:88 + | +LL | std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:19:36 + | +LL | let _a: A = std::ptr::read(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:20:36 + | +LL | let _a: A = std::ptr::read(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:22:46 + | +LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:23:46 + | +LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:25:45 + | +LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:26:45 + | +LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:28:39 + | +LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:30:69 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:31:69 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:33:73 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:35:29 + | +LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:36:37 + | +LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:38:44 + | +LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:39:52 + | +LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:41:25 + | +LL | std::ptr::write(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:43:35 + | +LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:45:34 + | +LL | std::ptr::write_volatile(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:47:40 + | +LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: aborting due to 25 previous errors + -- cgit 1.4.1-3-g733a5 From 012f9d47b24dc5e92be2d83bf798ca332847e68a Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 9 Apr 2021 10:09:06 -0400 Subject: Use `register_renamed` instead of `register_removed` for uplifted lints This still applies the lint, and also adds a structured suggestion to rename it. --- CHANGELOG.md | 29 +++++++---------- clippy_lints/src/deprecated_lints.rs | 63 ------------------------------------ clippy_lints/src/lib.rs | 37 ++++++--------------- tests/ui/deprecated.stderr | 28 ++++++++-------- 4 files changed, 34 insertions(+), 123 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 715f4702e95..7885bdfb12c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -179,7 +179,7 @@ Current stable, released 2021-03-25 * Replace [`find_map`] with [`manual_find_map`] [#6591](https://github.com/rust-lang/rust-clippy/pull/6591) -* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint [#6653](https://github.com/rust-lang/rust-clippy/pull/6653) ### Enhancements @@ -280,7 +280,7 @@ Released 2021-02-11 * Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333) -* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt` +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic` [#6351](https://github.com/rust-lang/rust-clippy/pull/6351) * Move [`map_err_ignore`] to `restriction` [#6416](https://github.com/rust-lang/rust-clippy/pull/6416) @@ -419,7 +419,7 @@ Released 2020-12-31 [#6037](https://github.com/rust-lang/rust-clippy/pull/6037) * Rename `zero_width_space` to [`invisible_characters`] [#6105](https://github.com/rust-lang/rust-clippy/pull/6105) -* Deprecate [`drop_bounds`] (uplifted) +* Deprecate `drop_bounds` (uplifted) [#6111](https://github.com/rust-lang/rust-clippy/pull/6111) * Move [`string_lit_as_bytes`] to `nursery` [#6117](https://github.com/rust-lang/rust-clippy/pull/6117) @@ -1018,7 +1018,7 @@ Released 2020-03-12 [#5015](https://github.com/rust-lang/rust-clippy/pull/5015) * Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057) * Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106) -* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930) +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930) ### Enhancements @@ -1046,7 +1046,7 @@ Released 2020-03-12 * [`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) -* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963) +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963) * [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978) * [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022) * [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032) @@ -1080,7 +1080,7 @@ Released 2020-01-30 [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714) * Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863) -* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788) +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788) * Expand [`string_lit_as_bytes`] to also trigger when literal has escapes [#4808](https://github.com/rust-lang/rust-clippy/pull/4808) * Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842) @@ -1282,7 +1282,7 @@ Released 2019-05-20 [1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e) -* New lint: [`drop_bounds`] to detect `T: Drop` bounds +* New lint: `drop_bounds` to detect `T: Drop` bounds * Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101) * Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code * Move [`get_unwrap`] to the restriction category @@ -1375,7 +1375,7 @@ Released 2019-01-17 * New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`], [`redundant_clone`], [`wildcard_dependencies`], - [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`], + [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`], [`mem_discriminant_non_enum`], [`cargo_common_metadata`] * Add support for `u128` and `i128` to integer related lints * Add float support to `mistyped_literal_suffixes` @@ -1649,7 +1649,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`] @@ -2037,7 +2037,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)* @@ -2091,7 +2091,7 @@ Released 2018-09-13 ## 0.0.49 — 2016-03-09 * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* -* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`] +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 * Fixed: ICE in [`needless_range_loop`] with globals @@ -2178,7 +2178,6 @@ Released 2018-09-13 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens -[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument @@ -2264,11 +2263,9 @@ Released 2018-09-13 [`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 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division -[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage -[`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 @@ -2403,7 +2400,6 @@ Released 2018-09-13 [`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_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 [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -2489,7 +2485,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 @@ -2518,7 +2513,6 @@ Released 2018-09-13 [`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 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold @@ -2542,7 +2536,6 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount -[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 89088c533ed..66e9f8c8af7 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -93,15 +93,6 @@ declare_deprecated_lint! { "the replacement suggested by this lint had substantially different behavior" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been superseded by the warn-by-default - /// `invalid_value` rustc lint. - pub INVALID_REF, - "superseded by rustc lint `invalid_value`" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// @@ -110,24 +101,6 @@ declare_deprecated_lint! { "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" } -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 - /// `array_into_iter`. - pub INTO_ITER_ON_ARRAY, - "this lint has been uplifted to rustc and is now called `array_into_iter`" -} - -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 - /// `unused_labels`. - pub UNUSED_LABEL, - "this lint has been uplifted to rustc and is now called `unused_labels`" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// @@ -144,42 +117,6 @@ declare_deprecated_lint! { "the regex! macro has been removed from the regex crate in 2018" } -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 - /// `drop_bounds`. - 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`" -} - -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 - /// `panic_fmt`. - pub PANIC_PARAMS, - "this lint has been uplifted to rustc and is now called `panic_fmt`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been integrated into the `unknown_lints` - /// rustc lint. - pub UNKNOWN_CLIPPY_LINTS, - "this lint has been integrated into the `unknown_lints` rustc lint" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b066643b568..e97f363a793 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -493,22 +493,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::unsafe_vector_initialization", "the replacement suggested by this lint had substantially different behavior", ); - store.register_removed( - "clippy::invalid_ref", - "superseded by rustc lint `invalid_value`", - ); store.register_removed( "clippy::unused_collect", "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", ); - store.register_removed( - "clippy::into_iter_on_array", - "this lint has been uplifted to rustc and is now called `array_into_iter`", - ); - store.register_removed( - "clippy::unused_label", - "this lint has been uplifted to rustc and is now called `unused_labels`", - ); store.register_removed( "clippy::replace_consts", "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", @@ -517,22 +505,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::regex_macro", "the regex! macro has been removed from the regex crate in 2018", ); - store.register_removed( - "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`", - ); - store.register_removed( - "clippy::panic_params", - "this lint has been uplifted to rustc and is now called `panic_fmt`", - ); - store.register_removed( - "clippy::unknown_clippy_lints", - "this lint has been integrated into the `unknown_lints` rustc lint", - ); store.register_removed( "clippy::find_map", "this lint has been replaced by `manual_find_map`, a more specific lint", @@ -2155,6 +2127,15 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); + + // uplifted lints + ls.register_renamed("clippy::invalid_ref", "invalid_value"); + ls.register_renamed("clippy::into_iter_on_array", "array_into_iter"); + ls.register_renamed("clippy::unused_label", "unused_labels"); + ls.register_renamed("clippy::drop_bounds", "drop_bounds"); + ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); + ls.register_renamed("clippy::panic_params", "non_fmt_panic"); + ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); } // only exists to let the dogfood integration test works. diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 64efcd18f88..1c92fb6ff04 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -24,23 +24,23 @@ error: lint `clippy::unused_collect` has been removed: `collect` has been marked LL | #[warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::invalid_ref` has been removed: superseded by rustc lint `invalid_value` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> $DIR/deprecated.rs:5:8 | LL | #[warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::into_iter_on_array` has been removed: this lint has been uplifted to rustc and is now called `array_into_iter` +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> $DIR/deprecated.rs:6:8 | LL | #[warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::unused_label` has been removed: this lint has been uplifted to rustc and is now called `unused_labels` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/deprecated.rs:7:8 | LL | #[warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 --> $DIR/deprecated.rs:8:8 @@ -48,29 +48,29 @@ error: lint `clippy::regex_macro` has been removed: the regex! macro has been re LL | #[warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::drop_bounds` has been removed: this lint has been uplifted to rustc and is now called `drop_bounds` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> $DIR/deprecated.rs:9:8 | LL | #[warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `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` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` --> $DIR/deprecated.rs:10:8 | LL | #[warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` -error: lint `clippy::panic_params` has been removed: this lint has been uplifted to rustc and is now called `panic_fmt` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panic` --> $DIR/deprecated.rs:11:8 | LL | #[warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic` -error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been integrated into the `unknown_lints` rustc lint +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/deprecated.rs:12:8 | LL | #[warn(clippy::unknown_clippy_lints)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint --> $DIR/deprecated.rs:13:8 -- cgit 1.4.1-3-g733a5 From 297e84f3f4b7ff3c2648e65b0f2c144982cfad63 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 10 Apr 2021 10:26:53 +0200 Subject: Handle imports which are nested directly --- clippy_lints/src/single_component_path_imports.rs | 73 +++++++++++++++------- .../single_component_path_imports_nested_first.rs | 17 +++++ ...ngle_component_path_imports_nested_first.stderr | 25 ++++++++ 3 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 tests/ui/single_component_path_imports_nested_first.rs create mode 100644 tests/ui/single_component_path_imports_nested_first.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index adf0d7998f8..6104103580e 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; @@ -66,15 +66,27 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { for single_use in &single_use_usages { if !imports_reused_with_self.contains(&single_use.0) { - span_lint_and_sugg( - cx, - SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, - "this import is redundant", - "remove it entirely", - String::new(), - Applicability::MachineApplicable, - ); + let can_suggest = single_use.2; + if can_suggest { + span_lint_and_sugg( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + "remove it entirely", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_help( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + None, + "remove this import", + ); + } } } } @@ -83,7 +95,7 @@ fn track_uses( cx: &EarlyContext<'_>, item: &Item, imports_reused_with_self: &mut Vec, - single_use_usages: &mut Vec<(Symbol, Span)>, + single_use_usages: &mut Vec<(Symbol, Span, bool)>, ) { if in_macro(item.span) || item.vis.kind.is_pub() { return; @@ -100,25 +112,40 @@ fn track_uses( if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { let ident = &segments[0].ident; - single_use_usages.push((ident.name, item.span)); + single_use_usages.push((ident.name, item.span, true)); } return; } - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } - - // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + let ident = &segments[0].ident; + single_use_usages.push((ident.name, tree.0.span, false)); + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } } } } diff --git a/tests/ui/single_component_path_imports_nested_first.rs b/tests/ui/single_component_path_imports_nested_first.rs new file mode 100644 index 00000000000..94117061b27 --- /dev/null +++ b/tests/ui/single_component_path_imports_nested_first.rs @@ -0,0 +1,17 @@ +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use regex; +use serde as edres; +pub use serde; + +fn main() { + regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); +} + +mod root_nested_use_mod { + use {regex, serde}; + #[allow(dead_code)] + fn root_nested_use_mod() {} +} diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr new file mode 100644 index 00000000000..0c3256c1ce4 --- /dev/null +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -0,0 +1,25 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:14:10 + | +LL | use {regex, serde}; + | ^^^^^ + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + = help: remove this import + +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:14:17 + | +LL | use {regex, serde}; + | ^^^^^ + | + = help: remove this import + +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:5:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From dde46c9340994ad396a8677702be58122384a5ed Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 10 Apr 2021 14:42:33 +0200 Subject: Replace complex conditional with pattern matching --- clippy_lints/src/tabs_in_doc_comments.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 3f9692540f7..d68227545a6 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -106,7 +106,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { let char_indices: Vec<_> = the_str.char_indices().collect(); - if char_indices.len() == 1 && char_indices.first().unwrap().1 == '\t' { + if let &[(_, '\t')] = &char_indices.as_slice() { return vec![(0, 1)]; } -- cgit 1.4.1-3-g733a5 From 47a4865406ec748729a6e7ec23e5a4d5f9b88230 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 10 Apr 2021 15:30:51 +0200 Subject: Fix dogfood --- clippy_lints/src/tabs_in_doc_comments.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index d68227545a6..51581b75ce3 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -106,7 +106,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { let char_indices: Vec<_> = the_str.char_indices().collect(); - if let &[(_, '\t')] = &char_indices.as_slice() { + if let [(_, '\t')] = char_indices.as_slice() { return vec![(0, 1)]; } @@ -121,12 +121,12 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { // as ['\t', '\t'] is excluded, this has to be a start of a tab group, // set indices accordingly is_active = true; - current_start = *index_b as u32; + current_start = u32::try_from(*index_b).unwrap(); }, [(_, '\t'), (index_b, _)] => { // this now has to be an end of the group, hence we have to push a new tuple is_active = false; - spans.push((current_start, *index_b as u32)); + spans.push((current_start, u32::try_from(*index_b).unwrap())); }, _ => {}, } @@ -149,7 +149,7 @@ mod tests_for_get_chunks_of_tabs { #[test] fn test_unicode_han_string() { - let res = get_chunks_of_tabs(" 位\t"); + let res = get_chunks_of_tabs(" \u{4f4d}\t"); assert_eq!(res, vec![(4, 5)]); } -- cgit 1.4.1-3-g733a5 From 43e6c656ba0f221caace290b8a8deace2abd89cd Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 10 Apr 2021 17:38:04 +0200 Subject: Remove `debug_assert` from `panic_in_result_fn` --- clippy_lints/src/panic_in_result_fn.rs | 3 -- tests/ui/panic_in_result_fn_debug_assertions.rs | 16 +++--- .../ui/panic_in_result_fn_debug_assertions.stderr | 57 ---------------------- 3 files changed, 9 insertions(+), 67 deletions(-) delete mode 100644 tests/ui/panic_in_result_fn_debug_assertions.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index d32b937b209..791a2bf0798 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -61,9 +61,6 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir "assert", "assert_eq", "assert_ne", - "debug_assert", - "debug_assert_eq", - "debug_assert_ne", ], body, ); diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index b60c79f97c8..1f8485b8cd8 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -1,43 +1,45 @@ #![warn(clippy::panic_in_result_fn)] #![allow(clippy::unnecessary_wraps)] +// debug_assert should never trigger the `panic_in_result_fn` lint + struct A; impl A { - fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint + fn result_with_debug_assert_with_message(x: i32) -> Result { debug_assert!(x == 5, "wrong argument"); Ok(true) } - fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint + fn result_with_debug_assert_eq(x: i32) -> Result { debug_assert_eq!(x, 5); Ok(true) } - fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint + fn result_with_debug_assert_ne(x: i32) -> Result { debug_assert_ne!(x, 1); Ok(true) } - fn other_with_debug_assert_with_message(x: i32) // should not emit lint + fn other_with_debug_assert_with_message(x: i32) { debug_assert!(x == 5, "wrong argument"); } - fn other_with_debug_assert_eq(x: i32) // should not emit lint + fn other_with_debug_assert_eq(x: i32) { debug_assert_eq!(x, 5); } - fn other_with_debug_assert_ne(x: i32) // should not emit lint + fn other_with_debug_assert_ne(x: i32) { debug_assert_ne!(x, 1); } - fn result_without_banned_functions() -> Result // should not emit lint + fn result_without_banned_functions() -> Result { let debug_assert = "debug_assert!"; println!("No {}", debug_assert); diff --git a/tests/ui/panic_in_result_fn_debug_assertions.stderr b/tests/ui/panic_in_result_fn_debug_assertions.stderr deleted file mode 100644 index ec18e89698c..00000000000 --- a/tests/ui/panic_in_result_fn_debug_assertions.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 - | -LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert!(x == 5, "wrong argument"); -LL | | Ok(true) -LL | | } - | |_____^ - | - = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 - | -LL | debug_assert!(x == 5, "wrong argument"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5 - | -LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert_eq!(x, 5); -LL | | Ok(true) -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9 - | -LL | debug_assert_eq!(x, 5); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5 - | -LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert_ne!(x, 1); -LL | | Ok(true) -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9 - | -LL | debug_assert_ne!(x, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 271c163ba319ce8518913057cef32978f38b2f6a Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 10 Apr 2021 20:54:40 +0200 Subject: Fix false-positive `debug_assert` --- clippy_lints/src/panic_in_result_fn.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 791a2bf0798..cef74d87e7c 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{find_macro_calls, return_ty}; +use clippy_utils::{find_macro_calls, is_expn_of, return_ty}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let panics = find_macro_calls( + let mut panics = find_macro_calls( &[ "unimplemented", "unreachable", @@ -64,6 +64,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir ], body, ); + panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); if !panics.is_empty() { span_lint_and_then( cx, -- cgit 1.4.1-3-g733a5 From cb14e7ebf40bb6edf9307813b341a9119800ef49 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 10 Apr 2021 23:37:18 +0200 Subject: Fix false-positive `debug_assert` in `panic` --- clippy_lints/src/panic_unimplemented.rs | 2 +- tests/ui/panicking_macros.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 1e946858947..1a680e7607e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -74,7 +74,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() { + if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { span_lint( diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 77fcb8dfd02..93b236f7473 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,5 +1,5 @@ #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::eq_op)] extern crate core; @@ -43,6 +43,18 @@ fn core_versions() { unreachable!(); } +fn debug_assert() { + debug_assert!(true); + debug_assert_eq!(true, true); + debug_assert_ne!(true, false); +} + +fn debug_assert_msg() { + debug_assert!(true, "test"); + debug_assert_eq!(true, true, "test"); + debug_assert_ne!(true, false, "test"); +} + fn main() { panic(); todo(); -- cgit 1.4.1-3-g733a5 From a45faf66f3f35f644be2e121a1cd478bb2b908e0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 9 Apr 2021 16:25:39 -0500 Subject: Deprecate filter_map --- clippy_lints/src/deprecated_lints.rs | 9 ++++++ clippy_lints/src/lib.rs | 6 ++-- clippy_lints/src/methods/filter_flat_map.rs | 18 ----------- clippy_lints/src/methods/filter_map_flat_map.rs | 18 ----------- clippy_lints/src/methods/filter_map_map.rs | 17 ----------- clippy_lints/src/methods/mod.rs | 40 +------------------------ clippy_utils/src/attrs.rs | 1 - lintcheck/src/main.rs | 2 +- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 ++++- tests/ui/filter_methods.rs | 25 ---------------- tests/ui/filter_methods.stderr | 39 ------------------------ 12 files changed, 23 insertions(+), 161 deletions(-) delete mode 100644 clippy_lints/src/methods/filter_flat_map.rs delete mode 100644 clippy_lints/src/methods/filter_map_flat_map.rs delete mode 100644 clippy_lints/src/methods/filter_map_map.rs delete mode 100644 tests/ui/filter_methods.rs delete mode 100644 tests/ui/filter_methods.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 89088c533ed..6f61229e133 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -188,3 +188,12 @@ declare_deprecated_lint! { pub FIND_MAP, "this lint has been replaced by `manual_find_map`, a more specific lint" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a + /// more specific lint. + pub FILTER_MAP, + "this lint has been replaced by `manual_filter_map`, a more specific lint" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b066643b568..03454271da5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -537,6 +537,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::find_map", "this lint has been replaced by `manual_find_map`, a more specific lint", ); + store.register_removed( + "clippy::filter_map", + "this lint has been replaced by `manual_filter_map`, a more specific lint", + ); // 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` @@ -788,7 +792,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::EXPECT_FUN_CALL, methods::EXPECT_USED, methods::FILETYPE_IS_FILE, - methods::FILTER_MAP, methods::FILTER_MAP_IDENTITY, methods::FILTER_MAP_NEXT, methods::FILTER_NEXT, @@ -1404,7 +1407,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs deleted file mode 100644 index 1588eec8882..00000000000 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter().flat_map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter().flat_map()` is an Iterator - if is_trait_method(cx, expr, sym::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); - } -} diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs deleted file mode 100644 index 741b1e7e361..00000000000 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter_map().flat_map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter_map().flat_map()` is an Iterator - if is_trait_method(cx, expr, sym::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); - } -} diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs deleted file mode 100644 index 713bbf25837..00000000000 --- a/clippy_lints/src/methods/filter_map_map.rs +++ /dev/null @@ -1,17 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter_map().map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter_map().map()` is an Iterator - if is_trait_method(cx, expr, sym::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); - } -} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b1ade5addd6..fc18849b07c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -11,11 +11,8 @@ mod clone_on_ref_ptr; mod expect_fun_call; mod expect_used; mod filetype_is_file; -mod filter_flat_map; mod filter_map; -mod filter_map_flat_map; mod filter_map_identity; -mod filter_map_map; mod filter_map_next; mod filter_next; mod flat_map_identity; @@ -472,35 +469,6 @@ declare_clippy_lint! { "using combinations of `flatten` and `map` which can usually be written as a single method call" } -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 - /// `_.filter_map(_)`. - /// - /// **Known problems:** Often requires a condition + Option/Iterator creation - /// inside the closure. - /// - /// **Example:** - /// ```rust - /// let vec = vec![1]; - /// - /// // Bad - /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); - /// - /// // Good - /// vec.iter().filter_map(|x| if *x == 0 { - /// Some(*x * 2) - /// } else { - /// None - /// }); - /// ``` - pub FILTER_MAP, - pedantic, - "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" -} - declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply /// as `filter_map(_)`. @@ -1677,7 +1645,6 @@ impl_lint_pass!(Methods => [ SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, - FILTER_MAP, FILTER_MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, @@ -1965,11 +1932,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); }, - ("flat_map", [flm_arg]) => match method_call!(recv) { - Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr), - Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr), - _ => flat_map_identity::check(cx, expr, flm_arg, span), - }, + ("flat_map", [flm_arg]) => flat_map_identity::check(cx, expr, flm_arg, span), ("flatten", []) => { if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { map_flatten::check(cx, expr, recv, map_arg); @@ -1993,7 +1956,6 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false) }, - ("filter_map", [_]) => filter_map_map::check(cx, expr), ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), _ => {}, } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 7ec8452bf4c..5e44c7b3ee0 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -151,7 +151,6 @@ pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { /// Return true if the attributes contain `#[doc(hidden)]` pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { - #[allow(clippy::filter_map)] attrs .iter() .filter(|attr| attr.has_name(sym::doc)) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 2041aed2b97..bfb0c3b3f74 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -5,7 +5,7 @@ // When a new lint is introduced, we can search the results for new warnings and check for false // positives. -#![allow(clippy::filter_map, clippy::collapsible_else_if)] +#![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; use std::process::Command; diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index fc444c0bea7..dbf0b03af76 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -11,5 +11,6 @@ #[warn(clippy::panic_params)] #[warn(clippy::unknown_clippy_lints)] #[warn(clippy::find_map)] +#[warn(clippy::filter_map)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 64efcd18f88..aa3ed28d40b 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -78,11 +78,17 @@ error: lint `clippy::find_map` has been removed: this lint has been replaced by LL | #[warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ +error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint + --> $DIR/deprecated.rs:14:8 + | +LL | #[warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ + error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/filter_methods.rs b/tests/ui/filter_methods.rs deleted file mode 100644 index 51450241619..00000000000 --- a/tests/ui/filter_methods.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] - -fn main() { - let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter(|&x| x == 0) - .flat_map(|x| x.checked_mul(2)) - .collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter_map(|x| x.checked_mul(2)) - .flat_map(|x| x.checked_mul(2)) - .collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter_map(|x| x.checked_mul(2)) - .map(|x| x.checked_mul(2)) - .collect(); -} diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr deleted file mode 100644 index c7b4f28be3a..00000000000 --- a/tests/ui/filter_methods.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: called `filter(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:8:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter(|&x| x == 0) -LL | | .flat_map(|x| x.checked_mul(2)) - | |_______________________________________^ - | - = note: `-D clippy::filter-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` - -error: called `filter_map(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:14:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .flat_map(|x| x.checked_mul(2)) - | |_______________________________________^ - | - = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` - -error: called `filter_map(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:20:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .map(|x| x.checked_mul(2)) - | |__________________________________^ - | - = help: this is more succinctly expressed by only calling `.filter_map(..)` instead - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 3ce6f0d022014ba13ed328a372c1ac1f37d9045e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 11 Apr 2021 13:29:08 +0200 Subject: Fix FP in `wrong_self_convention` lint --- clippy_lints/src/methods/wrong_self_convention.rs | 8 ++++++++ tests/ui/wrong_self_convention2.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 1e0de249a91..6e2bcb113c2 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -102,6 +102,14 @@ pub(super) fn check<'tcx>( .iter() .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) }) { + // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032) + if implements_trait + && !conventions + .iter() + .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_))) + { + return; + } if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { if conventions.len() > 1 { diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 8b42aa59e13..ae3a740d405 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -30,3 +30,15 @@ mod issue6983 { } } } + +mod issue7032 { + trait Foo { + fn from_usize(x: usize) -> Self; + } + // don't trigger + impl Foo for usize { + fn from_usize(x: usize) -> Self { + x + } + } +} -- cgit 1.4.1-3-g733a5 From fb54b703c7e6cfda05991fbe7e6f292fc82c4a27 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sat, 10 Apr 2021 17:50:54 +1200 Subject: Add a note on the issue #5953 --- clippy_lints/src/pass_by_ref_or_value.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 9a5b1c3b944..48b1d2b94a1 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -41,6 +41,14 @@ declare_clippy_lint! { /// false positives in cases involving multiple lifetimes that are bounded by /// each other. /// + /// Also, it does not take account of other similar cases where getting memory addresses + /// matters; namely, returning the pointer to the argument in question, + /// and passing the argument, as both references and pointers, + /// to a function that needs the memory address. For further details, refer to + /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953) + /// that explains a real case in which this false positive + /// led to an **undefined behaviour** introduced with unsafe code. + /// /// **Example:** /// /// ```rust -- cgit 1.4.1-3-g733a5 From 53260df2fa499f1e1908fb2a6b5ce3442f9a023e Mon Sep 17 00:00:00 2001 From: Takayuki Date: Mon, 12 Apr 2021 21:36:49 +0900 Subject: fix a false negative on `needless_return` --- clippy_lints/src/returns.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index af772cf4a14..3a6151dce71 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -223,6 +223,7 @@ fn check_final_expr<'tcx>( }, _ => (), }, + ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), _ => (), } } -- cgit 1.4.1-3-g733a5 From d6beb1841152a80ec10a5039e2294cd074fc3231 Mon Sep 17 00:00:00 2001 From: boxdot Date: Mon, 12 Apr 2021 18:34:16 +0200 Subject: Remove paths::STD_PTR_NULL --- clippy_lints/src/transmuting_null.rs | 14 +++++++------- clippy_utils/src/paths.rs | 2 -- 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 0be05d3e0cf..755132da591 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_qpath, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -38,10 +38,10 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { if_chain! { if let ExprKind::Call(func, args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &paths::STD_MEM_TRANSMUTE); if args.len() == 1; - + if let ExprKind::Path(ref path) = func.kind; + if let Some(func_def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, func_def_id, &paths::TRANSMUTE); then { // Catching transmute over constants that resolve to `null`. @@ -69,10 +69,10 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::())` if_chain! { - if let ExprKind::Call(func1, args1) = args[0].kind; + if let ExprKind::Call(func1, []) = args[0].kind; if let ExprKind::Path(ref path1) = func1.kind; - if match_qpath(path1, &paths::STD_PTR_NULL); - if args1.is_empty(); + if let Some(func1_def_id) = cx.qpath_res(path1, func1.hir_id).opt_def_id(); + if match_def_path(cx, func1_def_id, &paths::PTR_NULL); then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5da9c624ac4..ed8915f59e1 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -142,8 +142,6 @@ 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"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; -- cgit 1.4.1-3-g733a5 From 76bd5d232c767dbdab63005eca07a9563982e9dd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 12 Apr 2021 18:01:32 -0500 Subject: Refactor diagnostic item methods --- clippy_lints/src/mem_replace.rs | 21 ++++++++++---------- clippy_lints/src/methods/implicit_clone.rs | 28 ++++++++++++++++---------- clippy_lints/src/methods/mod.rs | 7 +++---- clippy_lints/src/misc.rs | 6 +++--- clippy_lints/src/to_string_in_display.rs | 4 ++-- clippy_utils/src/lib.rs | 32 ++++++++++++++++-------------- 6 files changed, 53 insertions(+), 45 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 1f521ec6090..ec60bffe955 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, match_def_path, meets_msrv, paths}; -use clippy_utils::{is_diagnostic_assoc_item, is_lang_ctor}; +use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -211,17 +210,17 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< sym::BinaryHeap, ]; - if std_types_symbols - .iter() - .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol)) - { - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - return true; + if let QPath::TypeRelative(_, method) = path { + if method.ident.name == sym::new { + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { + if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { + return std_types_symbols + .iter() + .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did)); + } } } } - false } @@ -231,7 +230,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< if !in_external_macro(cx.tcx.sess, expr_span); if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default) + if is_diag_trait_item(cx, repl_def_id, sym::Default) || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); then { diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 1211e2f2bf7..81c42de145f 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,28 +1,36 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_diag_item_method, is_diag_trait_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::ExprKind; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; use super::IMPLICIT_CLONE; -use clippy_utils::is_diagnostic_assoc_item; -pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) { +pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) { if_chain! { - if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match method_name { + "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), + "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), + "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), + "to_vec" => cx.tcx.impl_of_method(method_def_id) + .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl()) + == Some(true), + _ => false, + }; let return_type = cx.typeck_results().expr_ty(expr); - let input_type = cx.typeck_results().expr_ty(arg).peel_refs(); - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + let input_type = cx.typeck_results().expr_ty(recv).peel_refs(); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); if TyS::same_type(return_type, input_type); - if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic); then { span_lint_and_sugg( - cx,IMPLICIT_CLONE,method_path.ident.span, - &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name), + cx, + IMPLICIT_CLONE, + span, + &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name), "consider using", "clone".to_string(), Applicability::MachineApplicable diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc18849b07c..b2faaef0786 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1987,10 +1987,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr), - ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned), - ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path), - ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice), + ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { + implicit_clone::check(cx, name, expr, recv, span); + }, ("unwrap", []) => match method_call!(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index afced5a5ce5..d156c1d5bf4 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -20,7 +20,7 @@ use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, + get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, last_path_segment, match_qpath, unsext, SpanlessEq, }; @@ -555,8 +555,8 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: ExprKind::MethodCall(.., args, _) if args.len() == 1 => { if_chain!( if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString) - || is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned); + if is_diag_trait_item(cx, expr_def_id, sym::ToString) + || is_diag_trait_item(cx, expr_def_id, sym::ToOwned); then { (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) } else { diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index ae05a8da37b..4fb297ac6c6 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -95,7 +95,7 @@ impl LateLintPass<'_> for ToStringInDisplay { if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString); + if is_diag_trait_item(cx, expr_def_id, sym::ToString); if path_to_local_id(&args[0], self_hir_id); then { span_lint( diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e99ffbe269b..ec140463a8a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -293,27 +293,29 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } -/// Checks if the method call given in `def_id` belongs to a trait or other container with a given -/// diagnostic item -pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - cx.tcx - .opt_associated_item(def_id) - .and_then(|associated_item| match associated_item.container { - rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id), - rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() { - rustc_ty::Adt(adt, _) => Some(adt.did), - rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works - _ => None, - }, - }) - .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id)) +/// Checks if a method is defined in an impl of a diagnostic item +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { + if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { + return cx.tcx.is_diagnostic_item(diag_item, adt.did); + } + } + false +} + +/// Checks if a method is in a diagnostic item trait +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { + if let Some(trait_did) = cx.tcx.trait_of_item(def_id) { + return cx.tcx.is_diagnostic_item(diag_item, trait_did); + } + false } /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() .type_dependent_def_id(expr.hir_id) - .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item)) + .map_or(false, |did| is_diag_trait_item(cx, did, diag_item)) } /// Checks if an expression references a variable of the given name. -- cgit 1.4.1-3-g733a5 From 26a1989041393116c001f17d0323d6b4fc989dd9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 13 Apr 2021 17:13:49 +1200 Subject: Fix a FP in `missing_const_for_fn` where a function that calls a standard library function whose constness is unstable is considered as being able to be a const function --- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_utils/src/lib.rs | 1 + clippy_utils/src/qualify_min_const_fn.rs | 40 +++++++++++++++++++--- tests/ui/missing_const_for_fn/auxiliary/helper.rs | 8 +++++ tests/ui/missing_const_for_fn/cant_be_const.rs | 19 ++++++++++ tests/ui/missing_const_for_fn/could_be_const.rs | 10 ++++++ .../ui/missing_const_for_fn/could_be_const.stderr | 26 +++++++++----- 7 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 tests/ui/missing_const_for_fn/auxiliary/helper.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 0dc02431ad5..93b7a897405 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e99ffbe269b..8c20f8a0fbc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -9,6 +9,7 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b52cbf31e35..b2ce58b597b 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -1,3 +1,8 @@ +// This code used to be a part of `rustc` but moved to Clippy as a result of +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some +// of terminologies might not be relevant in the context of Clippy. Note that its behavior might +// differ from the time of `rustc` even if the name stays the same. + use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ @@ -6,6 +11,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; +use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; @@ -13,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { +pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { )?; for bb in body.basic_blocks() { - check_terminator(tcx, body, bb.terminator())?; + check_terminator(tcx, body, bb.terminator(), msrv)?; for stmt in &bb.statements { check_statement(tcx, body, def_id, stmt)?; } @@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t 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>, + msrv: Option<&RustcVersion>, +) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin } => { 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 !is_const_fn(tcx, fn_def_id, msrv) { return Err(( span, format!( @@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } + +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { + rustc_mir::const_eval::is_const_fn(tcx, def_id) + && if let Some(const_stab) = tcx.lookup_const_stability(def_id) { + if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { + // Checking MSRV is manually necessary because `rustc` has no such concept. This entire + // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. + // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. + crate::meets_msrv( + msrv, + &RustcVersion::parse(&since.as_str()) + .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), + ) + } else { + // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions. + unreachable!(); + } + } else { + true + } +} diff --git a/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/tests/ui/missing_const_for_fn/auxiliary/helper.rs new file mode 100644 index 00000000000..7b9dc76b8f1 --- /dev/null +++ b/tests/ui/missing_const_for_fn/auxiliary/helper.rs @@ -0,0 +1,8 @@ +// This file provides a const function that is unstably const forever. + +#![feature(staged_api)] +#![stable(feature = "1", since = "1.0.0")] + +#[stable(feature = "1", since = "1.0.0")] +#[rustc_const_unstable(feature = "foo", issue = "none")] +pub const fn unstably_const_fn() {} diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index ba352ef9ee9..7cda1aaa3c2 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -2,9 +2,14 @@ //! compilation error. //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. +// aux-build:helper.rs + #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features)] #![feature(start, const_generics)] +#![feature(custom_inner_attributes)] + +extern crate helper; struct Game; @@ -101,3 +106,17 @@ fn const_generic_return(t: &[T]) -> &[T; N] { unsafe { &*p } } + +// Do not lint this because it calls a function whose constness is unstable. +fn unstably_const_fn() { + helper::unstably_const_fn() +} + +mod const_fn_stabilized_after_msrv { + #![clippy::msrv = "1.46.0"] + + // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0. + fn const_fn_stabilized_after_msrv(byte: u8) { + byte.is_ascii_digit(); + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index c6f44b7daa3..0accb516f5f 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,7 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] #![feature(const_generics)] +#![feature(custom_inner_attributes)] use std::mem::transmute; @@ -70,5 +71,14 @@ mod with_drop { } } +mod const_fn_stabilized_before_msrv { + #![clippy::msrv = "1.47.0"] + + // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47. + fn const_fn_stabilized_before_msrv(byte: u8) { + byte.is_ascii_digit(); + } +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 74d32b8a1aa..63c211f39fa 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:13:5 + --> $DIR/could_be_const.rs:14:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:17:5 + --> $DIR/could_be_const.rs:18:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:24:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:28:1 + --> $DIR/could_be_const.rs:29:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:35:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:40:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:44:1 + --> $DIR/could_be_const.rs:45:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,12 +58,20 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:67:9 + --> $DIR/could_be_const.rs:68:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B LL | | } | |_________^ -error: aborting due to 8 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:78:5 + | +LL | / fn const_fn_stabilized_before_msrv(byte: u8) { +LL | | byte.is_ascii_digit(); +LL | | } + | |_____^ + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From cbdebd97ec4846391dc0f9a1288a3ab1fc053f99 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Wed, 14 Apr 2021 06:30:51 +0200 Subject: Explain why we use `char_indices()` instead of `chars()` --- clippy_lints/src/tabs_in_doc_comments.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 51581b75ce3..1995981b905 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -104,6 +104,9 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { // tracker to decide if the last group of tabs is not closed by a non-tab character let mut is_active = false; + // Note that we specifically need the char _byte_ indices here, not the positional indexes + // within the char array to deal with multi-byte characters properly. `char_indices` does + // exactly that. It provides an iterator over tuples of the form `(byte position, char)`. let char_indices: Vec<_> = the_str.char_indices().collect(); if let [(_, '\t')] = char_indices.as_slice() { -- cgit 1.4.1-3-g733a5 From cbde4f2c67667c717adecf3fac9df84954154fe0 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 10 Apr 2021 14:45:51 +0200 Subject: parent_node_is_if_expr now also recognizes if let as parent if --- clippy_lints/src/copies.rs | 7 +++++ clippy_utils/src/lib.rs | 30 +++++++++++++++++----- tests/ui/branches_sharing_code/shared_at_bottom.rs | 14 ++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 8b503c9a030..dfc79ac365b 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -290,6 +290,13 @@ fn lint_same_then_else<'tcx>( } } +/// The return tuple is structured as follows: +/// 1. The amount of equal statements from the start +/// 2. The amount of equal statements from the end +/// 3. An indication if the block expressions are the same. This will also be true if both are `None` +/// +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, false)` +/// to aboard any further processing and avoid duplicate lint triggers. fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e1a2105b96..1ef5d9d32c5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1212,13 +1212,29 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); let parent_node = map.get(parent_id); - matches!( - parent_node, - Node::Expr(Expr { - kind: ExprKind::If(_, _, _), - .. - }) - ) + + // Check for `if` + if_chain! { + if let Node::Expr(expr) = parent_node; + if let ExprKind::If(_, _, _) = expr.kind; + then { + return true; + } + } + + // Check for `if let` + if_chain! { + if let Node::Arm(arm) = parent_node; + let arm_parent_id = map.get_parent_node(arm.hir_id); + let arm_parent_node = map.get(arm_parent_id); + if let Node::Expr(expr) = arm_parent_node; + if let ExprKind::Match(_, _, MatchSource::IfLetDesugar { .. }) = expr.kind; + then { + return true; + } + } + + false } // Finds the `#[must_use]` attribute, if any diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index c389c243d44..ce2040bdeb8 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -206,4 +206,18 @@ fn fp_test() { } } +fn fp_if_let_issue7054() { + // This shouldn't trigger the lint + let string; + let _x = if let true = true { + "" + } else if true { + string = "x".to_owned(); + &string + } else { + string = "y".to_owned(); + &string + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 2992b19c82c0c5f96327b3be037777bf42862230 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 13 Apr 2021 22:55:47 +0200 Subject: Added inferred local type comparion to SpanlessEq --- clippy_lints/src/copies.rs | 9 ++--- clippy_utils/src/hir_utils.rs | 48 +++++++++++++++++++++++++ clippy_utils/src/lib.rs | 2 +- tests/ui/branches_sharing_code/shared_at_top.rs | 11 ++++++ 4 files changed, 65 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index dfc79ac365b..34ea014dd67 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -293,10 +293,11 @@ fn lint_same_then_else<'tcx>( /// The return tuple is structured as follows: /// 1. The amount of equal statements from the start /// 2. The amount of equal statements from the end -/// 3. An indication if the block expressions are the same. This will also be true if both are `None` +/// 3. An indication if the block expressions are the same. This will also be true if both are +/// `None` /// -/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, false)` -/// to aboard any further processing and avoid duplicate lint triggers. +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, +/// false)` to aboard any further processing and avoid duplicate lint triggers. fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; @@ -307,7 +308,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. // The comparison therefore needs to be done in a way that builds the correct context. - let mut evaluator = SpanlessEq::new(cx); + let mut evaluator = SpanlessEq::new(cx).enable_check_inferred_local_types(); let mut evaluator = evaluator.inter_expr(); let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f695f1a61e7..97db58ae12b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,6 +1,7 @@ use crate::consts::{constant_context, constant_simple}; use crate::differing_macro_contexts; use crate::source::snippet_opt; +use if_chain::if_chain; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::Res; @@ -29,6 +30,30 @@ pub struct SpanlessEq<'a, 'tcx> { maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, allow_side_effects: bool, expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, + /// This adds an additional type comparison to locals that insures that even the + /// inferred of the value is the same. + /// + /// **Example** + /// * Context 1 + /// ```ignore + /// let vec = Vec::new(); + /// vec.push("A string"); + /// ``` + /// + /// * Context 2 + /// ```ignore + /// let vec = Vec::new(); + /// vec.push(0); // An integer + /// ``` + /// + /// Only comparing the first local definition would usually return that they are + /// equal, since they are identical. However, they are different due to the context + /// as they have different inferred types. + /// + /// This option enables or disables the specific check of the inferred type. + /// + /// Note: This check will only be done if `self.maybe_typeck_results` is `Some()`. + check_inferred_local_types: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -38,6 +63,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { maybe_typeck_results: cx.maybe_typeck_results(), allow_side_effects: true, expr_fallback: None, + check_inferred_local_types: false, } } @@ -56,6 +82,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } + pub fn enable_check_inferred_local_types(self) -> Self { + Self { + check_inferred_local_types: true, + ..self + } + } + /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { @@ -96,6 +129,21 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { + // See `SpanlessEq::check_inferred_local_types` for an explication of this check + if_chain! { + if l.ty.is_none() && r.ty.is_none(); + if self.inner.check_inferred_local_types; + if let Some(tcx) = self.inner.maybe_typeck_results; + + // Check the inferred types + let l_ty = tcx.pat_ty(&l.pat); + let r_ty = tcx.pat_ty(&r.pat); + if l_ty != r_ty; + then { + return false; + } + } + // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1ef5d9d32c5..280bde73ed7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1212,7 +1212,7 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); let parent_node = map.get(parent_id); - + // Check for `if` if_chain! { if let Node::Expr(expr) = parent_node; diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index e65bcfd7873..51a46481399 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -100,4 +100,15 @@ fn check_if_same_than_else_mask() { } } +#[allow(clippy::vec_init_then_push)] +fn pf_local_with_inferred_type_issue7053() { + if true { + let mut v = Vec::new(); + v.push(0); + } else { + let mut v = Vec::new(); + v.push(""); + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 0b4af7257ddea5cf508305635fbdc4c0d1e6bdd5 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 14 Apr 2021 19:40:51 +0200 Subject: PR suggestions and removing utils::parent_node_is_if_expr --- clippy_lints/src/comparison_chain.rs | 4 +-- clippy_lints/src/copies.rs | 43 ++++++++++++++++--------- clippy_lints/src/if_then_some_else_none.rs | 4 +-- clippy_lints/src/needless_bool.rs | 4 +-- clippy_utils/src/hir_utils.rs | 50 ++++-------------------------- clippy_utils/src/lib.rs | 31 ------------------ 6 files changed, 40 insertions(+), 96 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 31ae63b5184..42e153909ce 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq}; +use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } // We only care about the top-most `if` in the chain - if parent_node_is_if_expr(expr, cx) { + if is_else_clause(cx.tcx, expr) { return; } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 34ea014dd67..f956d171bfb 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::{ - both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr, + both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause, run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash, }; use if_chain::if_chain; @@ -188,13 +188,18 @@ fn lint_same_then_else<'tcx>( expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks - if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { + if blocks.len() < 2 || is_else_clause(cx.tcx, expr) { return; } // Check if each block has shared code let has_expr = blocks[0].expr.is_some(); - let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); + + let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) { + (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq) + } else { + return; + }; // BRANCHES_SHARING_CODE prerequisites if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { @@ -290,15 +295,19 @@ fn lint_same_then_else<'tcx>( } } -/// The return tuple is structured as follows: -/// 1. The amount of equal statements from the start -/// 2. The amount of equal statements from the end -/// 3. An indication if the block expressions are the same. This will also be true if both are -/// `None` -/// -/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, -/// false)` to aboard any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { +struct BlockEqual { + /// The amount statements that are equal from the start + start_eq: usize, + /// The amount statements that are equal from the end + end_eq: usize, + /// An indication if the block expressions are the same. This will also be true if both are + /// `None` + expr_eq: bool, +} + +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to +/// abort any further processing and avoid duplicate lint triggers. +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; @@ -308,7 +317,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. // The comparison therefore needs to be done in a way that builds the correct context. - let mut evaluator = SpanlessEq::new(cx).enable_check_inferred_local_types(); + let mut evaluator = SpanlessEq::new(cx); let mut evaluator = evaluator.inter_expr(); let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); @@ -340,7 +349,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, "same as this", ); - return (0, 0, false); + return None; } } @@ -360,7 +369,11 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, end_eq = min_block_size - start_eq; } - (start_eq, end_eq, expr_eq) + Some(BlockEqual { + start_eq, + end_eq, + expr_eq, + }) } fn check_for_warn_of_moved_symbol( diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index f73b3a1fe67..85c95f1151f 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{is_lang_ctor, meets_msrv, parent_node_is_if_expr}; +use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv}; use if_chain::if_chain; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -68,7 +68,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } // We only care about the top-most `if` in the chain - if parent_node_is_if_expr(expr, cx) { + if is_else_clause(cx.tcx, expr) { return; } diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 96a58d1410f..dd458198637 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_expn_of, parent_node_is_if_expr}; +use clippy_utils::{is_else_clause, is_expn_of}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.make_return(); } - if parent_node_is_if_expr(e, cx) { + if is_else_clause(cx.tcx, e) { snip = snip.blockify() } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 97db58ae12b..f0d7f76bea2 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,7 +1,6 @@ use crate::consts::{constant_context, constant_simple}; use crate::differing_macro_contexts; use crate::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::Res; @@ -30,30 +29,6 @@ pub struct SpanlessEq<'a, 'tcx> { maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, allow_side_effects: bool, expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, - /// This adds an additional type comparison to locals that insures that even the - /// inferred of the value is the same. - /// - /// **Example** - /// * Context 1 - /// ```ignore - /// let vec = Vec::new(); - /// vec.push("A string"); - /// ``` - /// - /// * Context 2 - /// ```ignore - /// let vec = Vec::new(); - /// vec.push(0); // An integer - /// ``` - /// - /// Only comparing the first local definition would usually return that they are - /// equal, since they are identical. However, they are different due to the context - /// as they have different inferred types. - /// - /// This option enables or disables the specific check of the inferred type. - /// - /// Note: This check will only be done if `self.maybe_typeck_results` is `Some()`. - check_inferred_local_types: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -63,7 +38,6 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { maybe_typeck_results: cx.maybe_typeck_results(), allow_side_effects: true, expr_fallback: None, - check_inferred_local_types: false, } } @@ -82,13 +56,6 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - pub fn enable_check_inferred_local_types(self) -> Self { - Self { - check_inferred_local_types: true, - ..self - } - } - /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { @@ -129,17 +96,12 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { - // See `SpanlessEq::check_inferred_local_types` for an explication of this check - if_chain! { - if l.ty.is_none() && r.ty.is_none(); - if self.inner.check_inferred_local_types; - if let Some(tcx) = self.inner.maybe_typeck_results; - - // Check the inferred types - let l_ty = tcx.pat_ty(&l.pat); - let r_ty = tcx.pat_ty(&r.pat); - if l_ty != r_ty; - then { + // This additional check ensures that the type of the locals are equivalent even if the init + // expression or type have some inferred parts. + if let Some(typeck) = self.inner.maybe_typeck_results { + let l_ty = typeck.pat_ty(&l.pat); + let r_ty = typeck.pat_ty(&r.pat); + if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) { return false; } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 280bde73ed7..c21cef61df4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1206,37 +1206,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } -/// This function returns true if the given expression is the `else` or `if else` part of an if -/// statement -pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - let map = cx.tcx.hir(); - let parent_id = map.get_parent_node(expr.hir_id); - let parent_node = map.get(parent_id); - - // Check for `if` - if_chain! { - if let Node::Expr(expr) = parent_node; - if let ExprKind::If(_, _, _) = expr.kind; - then { - return true; - } - } - - // Check for `if let` - if_chain! { - if let Node::Arm(arm) = parent_node; - let arm_parent_id = map.get_parent_node(arm.hir_id); - let arm_parent_node = map.get(arm_parent_id); - if let Node::Expr(expr) = arm_parent_node; - if let ExprKind::Match(_, _, MatchSource::IfLetDesugar { .. }) = expr.kind; - then { - return true; - } - } - - false -} - // Finds the `#[must_use]` attribute, if any pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { attrs.iter().find(|a| a.has_name(sym::must_use)) -- cgit 1.4.1-3-g733a5 From ce5e927713ddd32096f4e73f7a2c339cc8163d82 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 09:25:04 -0400 Subject: Improve `map_entry` lint Fix false positives where the map is used before inserting into the map. Fix false positives where two insertions happen. Suggest using `if let Entry::Vacant(e) = _.entry(_)` when `or_insert` might be a semantic change --- clippy_lints/src/entry.rs | 489 ++++++++++++++++++++++++++++++---------- clippy_utils/src/lib.rs | 125 ++++++++-- clippy_utils/src/paths.rs | 4 + clippy_utils/src/source.rs | 9 + tests/ui/entry.fixed | 101 +++++++++ tests/ui/entry.rs | 103 +++++++++ tests/ui/entry.stderr | 171 ++++++++++++++ tests/ui/entry_fixable.fixed | 15 -- tests/ui/entry_fixable.rs | 17 -- tests/ui/entry_fixable.stderr | 12 - tests/ui/entry_unfixable.rs | 59 ++--- tests/ui/entry_unfixable.stderr | 57 ----- 12 files changed, 882 insertions(+), 280 deletions(-) create mode 100644 tests/ui/entry.fixed create mode 100644 tests/ui/entry.rs create mode 100644 tests/ui/entry.stderr delete mode 100644 tests/ui/entry_fixable.fixed delete mode 100644 tests/ui/entry_fixable.rs delete mode 100644 tests/ui/entry_fixable.stderr delete mode 100644 tests/ui/entry_unfixable.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index a815df1691a..ca01d0a7f87 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,17 +1,18 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::SpanlessEq; -use clippy_utils::{get_item_name, paths}; -use if_chain::if_chain; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, + is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, + source::{snippet_indent, snippet_with_applicability, snippet_with_context}, + SpanlessEq, +}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp}; +use rustc_hir::{ + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Expr, ExprKind, Guard, Local, Stmt, StmtKind, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; -use rustc_span::sym; +use rustc_span::{Span, SyntaxContext, DUMMY_SP}; +use std::fmt::Write; declare_clippy_lint! { /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` @@ -19,15 +20,14 @@ declare_clippy_lint! { /// /// **Why is this bad?** Using `entry` is more efficient. /// - /// **Known problems:** Some false negatives, eg.: + /// **Known problems:** The suggestion may have type inference errors in some cases. e.g. /// ```rust - /// # use std::collections::HashMap; - /// # let mut map = HashMap::new(); - /// # let v = 1; - /// # let k = 1; - /// if !map.contains_key(&k) { - /// map.insert(k.clone(), v); - /// } + /// let mut map = std::collections::HashMap::new(); + /// let _ = if !map.contains_key(&0) { + /// map.insert(0, 0) + /// } else { + /// None + /// }; /// ``` /// /// **Example:** @@ -56,132 +56,379 @@ declare_clippy_lint! { declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { + #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::If(check, then_block, ref else_block) = expr.kind { - if let ExprKind::Unary(UnOp::Not, check) = check.kind { - if let Some((ty, map, key)) = check_cond(cx, check) { - // in case of `if !m.contains_key(&k) { m.insert(k, v); }` - // we can give a better error message - let sole_expr = { - else_block.is_none() - && if let ExprKind::Block(then_block, _) = then_block.kind { - (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 - } else { - true - } - // XXXManishearth we can also check for if/else blocks containing `None`. - }; + let (cond_expr, then_expr, else_expr) = match expr.kind { + ExprKind::If(c, t, e) => (c, t, e), + _ => return, + }; + let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) { + Some(x) => x, + None => return, + }; - let mut visitor = InsertVisitor { - cx, - span: expr.span, - ty, - map, - key, - sole_expr, - }; + let then_search = match find_insert_calls(cx, &contains_expr, then_expr) { + Some(x) => x, + None => return, + }; - walk_expr(&mut visitor, then_block); - } - } else if let Some(else_block) = *else_block { - if let Some((ty, map, key)) = check_cond(cx, check) { - let mut visitor = InsertVisitor { - cx, - span: expr.span, - ty, - map, - key, - sole_expr: false, + let mut app = Applicability::MachineApplicable; + let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; + let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; + let sugg = if !contains_expr.negated || else_expr.is_some() || then_search.insertions.is_empty() { + return; + } else { + // if .. { insert } + match then_search.as_single_insertion() { + Some(insertion) if !insertion.value.can_have_side_effects() => { + format!( + "{}.entry({}).or_insert({});", + map_str, + key_str, + snippet_with_context(cx, insertion.value.span, insertion.call.span.ctxt(), "..", &mut app).0, + ) + }, + _ => { + let (body_str, entry_kind) = if contains_expr.negated { + (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + "Occupied(mut e)", + ) }; - - walk_expr(&mut visitor, else_block); - } + format!( + "if let {}::{} = {}.entry({}) {}", + map_ty.entry_path(), + entry_kind, + map_str, + key_str, + body_str, + ) + }, } + }; + + span_lint_and_sugg( + cx, + MAP_ENTRY, + expr.span, + &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), + "try this", + sugg, + app, + ); + } +} + +#[derive(Clone, Copy)] +enum MapType { + Hash, + BTree, +} +impl MapType { + fn name(self) -> &'static str { + match self { + Self::Hash => "HashMap", + Self::BTree => "BTreeMap", + } + } + fn entry_path(self) -> &'staic str { + match self { + Self::Hash => "std::collections::hash_map::Entry", + Self::BTree => "std::collections::btree_map::Entry", } } } -fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> { - if_chain! { - if let ExprKind::MethodCall(path, _, params, _) = check.kind; - if params.len() >= 2; - if path.ident.name == sym!(contains_key); - if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind; - then { - let map = ¶ms[0]; - let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); - - return if match_type(cx, obj_ty, &paths::BTREEMAP) { - Some(("BTreeMap", map, key)) - } - else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) { - Some(("HashMap", map, key)) - } - else { - None +struct ContainsExpr<'tcx> { + negated: bool, + map: &'tcx Expr<'tcx>, + key: &'tcx Expr<'tcx>, + call_ctxt: SyntaxContext, +} +fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { + let mut negated = false; + let expr = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::Unary(UnOp::Not, e) => { + negated = !negated; + Some(e) + }, + _ => None, + }); + match expr.kind { + ExprKind::MethodCall( + _, + _, + [map, Expr { + kind: ExprKind::AddrOf(_, _, key), + span: key_span, + .. + }], + _, + ) if key_span.ctxt() == expr.span.ctxt() => { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + let expr = ContainsExpr { + negated, + map, + key, + call_ctxt: expr.span.ctxt(), }; + if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) { + Some((MapType::BTree, expr)) + } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) { + Some((MapType::Hash, expr)) + } else { + None + } + }, + _ => None, + } +} + +struct InsertExpr<'tcx> { + map: &'tcx Expr<'tcx>, + key: &'tcx Expr<'tcx>, + value: &'tcx Expr<'tcx>, +} +fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { + Some(InsertExpr { map, key, value }) + } else { + None } + } else { + None } +} - None +#[derive(Clone, Copy)] +struct Insertion<'tcx> { + call: &'tcx Expr<'tcx>, + value: &'tcx Expr<'tcx>, } -struct InsertVisitor<'a, 'tcx, 'b> { - cx: &'a LateContext<'tcx>, - span: Span, - ty: &'static str, - map: &'b Expr<'b>, - key: &'b Expr<'b>, - sole_expr: bool, +// This visitor needs to do a multiple things: +// * Find all usages of the map. Only insertions into the map which share the same key are +// permitted. All others will prevent the lint. +// * Determine if the final statement executed is an insertion. This is needed to use `insert_with`. +// * Determine if there's any sub-expression that can't be placed in a closure. +// * Determine if there's only a single insert statement. This is needed to give better suggestions. + +#[allow(clippy::struct_excessive_bools)] +struct InsertSearcher<'cx, 'i, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// The map expression used in the contains call. + map: &'tcx Expr<'tcx>, + /// The key expression used in the contains call. + key: &'tcx Expr<'tcx>, + /// The context of the top level block. All insert calls must be in the same context. + ctxt: SyntaxContext, + /// Whether this expression can use the entry api. + can_use_entry: bool, + // A single insert expression has a slightly different suggestion. + is_single_insert: bool, + is_map_used: bool, + insertions: &'i mut Vec>, +} +impl<'tcx> InsertSearcher<'_, '_, 'tcx> { + /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but + /// only if they are on separate code paths. This will return whether the map was used in the + /// given expression. + fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool { + let is_map_used = self.is_map_used; + self.visit_expr(e); + let res = self.is_map_used; + self.is_map_used = is_map_used; + res + } } +impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } -impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { - type Map = Map<'tcx>; + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + match stmt.kind { + StmtKind::Semi(e) | StmtKind::Expr(e) => self.visit_expr(e), + StmtKind::Local(Local { init: Some(e), .. }) => { + self.is_single_insert = false; + self.visit_expr(e); + }, + _ => { + self.is_single_insert = false; + }, + } + } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, _, params, _) = expr.kind; - if params.len() == 3; - if path.ident.name == sym!(insert); - if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); - if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]); - if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span); - then { - span_lint_and_then(self.cx, MAP_ENTRY, self.span, - &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| { - if self.sole_expr { - let mut app = Applicability::MachineApplicable; - let help = format!("{}.entry({}).or_insert({});", - snippet_with_applicability(self.cx, self.map.span, "map", &mut app), - snippet_with_applicability(self.cx, params[1].span, "..", &mut app), - snippet_with_applicability(self.cx, params[2].span, "..", &mut app)); - - diag.span_suggestion( - self.span, - "consider using", - help, - Applicability::MachineApplicable, // snippet - ); + if !self.can_use_entry { + return; + } + + match try_parse_insert(self.cx, expr) { + Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => { + // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api. + if self.is_map_used + || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key) + || expr.span.ctxt() != self.ctxt + { + self.can_use_entry = false; + return; + } + + self.insertions.push(Insertion { + call: expr, + value: insert_expr.value, + }); + self.is_map_used = true; + + // The value doesn't affect whether there is only a single insert expression. + let is_single_insert = self.is_single_insert; + self.visit_expr(insert_expr.value); + self.is_single_insert = is_single_insert; + }, + _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => { + self.is_map_used = true; + }, + _ => match expr.kind { + ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { + self.is_single_insert = false; + self.visit_expr(cond_expr); + // Each branch may contain it's own insert expression. + let mut is_map_used = self.visit_cond_arm(then_expr); + is_map_used |= self.visit_cond_arm(else_expr); + self.is_map_used = is_map_used; + }, + ExprKind::Match(scrutinee_expr, arms, _) => { + self.is_single_insert = false; + self.visit_expr(scrutinee_expr); + // Each branch may contain it's own insert expression. + let mut is_map_used = self.is_map_used; + for arm in arms { + if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { + self.visit_expr(guard) + } + is_map_used |= self.visit_cond_arm(arm.body); } - else { - let help = format!("consider using `{}.entry({})`", - snippet(self.cx, self.map.span, "map"), - snippet(self.cx, params[1].span, "..")); - - diag.span_label( - self.span, - &help, - ); + self.is_map_used = is_map_used; + }, + ExprKind::Loop(block, ..) => { + // Don't allow insertions inside of a loop. + let insertions_len = self.insertions.len(); + self.visit_block(block); + if self.insertions.len() != insertions_len { + self.can_use_entry = false; } - }); - } + }, + ExprKind::Block(block, _) => self.visit_block(block), + ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => { + self.can_use_entry = false; + }, + _ => { + self.is_single_insert = false; + walk_expr(self, expr); + }, + }, } + } +} - if !self.sole_expr { - walk_expr(self, expr); +struct InsertSearchResults<'tcx> { + insertions: Vec>, + is_single_insert: bool, +} +impl InsertSearchResults<'tcx> { + fn as_single_insertion(&self) -> Option> { + self.is_single_insert.then(|| self.insertions[0]) + } + + fn snippet_occupied(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + let ctxt = span.ctxt(); + let mut res = String::new(); + for insertion in self.insertions.iter() { + res.push_str(&snippet_with_applicability( + cx, + span.until(insertion.call.span), + "..", + app, + )); + if is_expr_used_or_unified(cx.tcx, insertion.call) { + res.push_str("Some(e.insert("); + res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); + res.push_str("))"); + } else { + res.push_str("e.insert("); + res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); + res.push(')'); + } + span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); } + res.push_str(&snippet_with_applicability(cx, span, "..", app)); + res } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None + + fn snippet_vacant(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + let ctxt = span.ctxt(); + let mut res = String::new(); + for insertion in self.insertions.iter() { + res.push_str(&snippet_with_applicability( + cx, + span.until(insertion.call.span), + "..", + app, + )); + if is_expr_used_or_unified(cx.tcx, insertion.call) { + if is_expr_final_block_expr(cx.tcx, insertion.call) { + let _ = write!( + res, + "e.insert({});\n{}None", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, + snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""), + ); + } else { + let _ = write!( + res, + "{{ e.insert({}); None }}", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, + ); + } + } else { + let _ = write!( + res, + "e.insert({})", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, + ); + } + span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); + } + res.push_str(&snippet_with_applicability(cx, span, "..", app)); + res } } +fn find_insert_calls( + cx: &LateContext<'tcx>, + contains_expr: &ContainsExpr<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option> { + let mut insertions = Vec::new(); + let mut s = InsertSearcher { + cx, + map: contains_expr.map, + key: contains_expr.key, + ctxt: expr.span.ctxt(), + insertions: &mut insertions, + is_map_used: false, + can_use_entry: true, + is_single_insert: true, + }; + s.visit_expr(expr); + let is_single_insert = s.is_single_insert; + s.can_use_entry.then(|| InsertSearchResults { + insertions, + is_single_insert, + }) +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e1a2105b96..0abee49f40c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,9 +63,9 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, - TraitItem, TraitItemKind, TraitRef, TyKind, + def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, + Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1245,6 +1245,82 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } +pub fn get_expr_use_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { + let map = tcx.hir(); + let mut child_id = expr.hir_id; + let mut iter = map.parent_iter(child_id); + loop { + match iter.next() { + None => break None, + Some((id, Node::Block(_))) => child_id = id, + Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id, + Some((_, Node::Expr(expr))) => match expr.kind { + ExprKind::Break( + Destination { + target_id: Ok(dest), .. + }, + _, + ) => { + iter = map.parent_iter(dest); + child_id = dest; + }, + ExprKind::DropTemps(_) | ExprKind::Block(..) => child_id = expr.hir_id, + ExprKind::If(control_expr, ..) | ExprKind::Match(control_expr, ..) + if control_expr.hir_id != child_id => + { + child_id = expr.hir_id + }, + _ => break Some(Node::Expr(expr)), + }, + Some((_, node)) => break Some(node), + } + } +} + +pub fn is_expr_used(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + !matches!( + get_expr_use_node(tcx, expr), + Some(Node::Stmt(Stmt { + kind: StmtKind::Expr(_) | StmtKind::Semi(_), + .. + })) + ) +} + +pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { + let map = tcx.hir(); + let mut child_id = expr.hir_id; + let mut iter = map.parent_iter(child_id); + loop { + match iter.next() { + None => break None, + Some((id, Node::Block(_))) => child_id = id, + Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id, + Some((_, Node::Expr(expr))) => match expr.kind { + ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id, + ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id, + ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None, + _ => break Some(Node::Expr(expr)), + }, + Some((_, node)) => break Some(node), + } + } +} + +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + !matches!( + get_expr_use_or_unification_node(tcx, expr), + None | Some(Node::Stmt(Stmt { + kind: StmtKind::Expr(_) | StmtKind::Semi(_), + .. + })) + ) +} + +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..))) +} + pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { if let ast::AttrKind::Normal(ref attr, _) = attr.kind { @@ -1414,28 +1490,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { peel(pat, 0) } +/// Peels of expressions while the given closure returns `Some`. +pub fn peel_hir_expr_while<'tcx>( + mut expr: &'tcx Expr<'tcx>, + mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>, +) -> &'tcx Expr<'tcx> { + while let Some(e) = f(expr) { + expr = e; + } + expr +} + /// Peels off up to the given number of references on the expression. Returns the underlying /// expression and the number of references removed. pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { - fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) { - match expr.kind { - ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target), - _ => (expr, count), - } - } - f(expr, 0, count) + let mut remaining = count; + let e = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => { + remaining -= 1; + Some(e) + }, + _ => None, + }); + (e, count - remaining) } /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { - fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { - match expr.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1), - _ => (expr, count), - } - } - f(expr, 0) + let mut count = 0; + let e = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, e) => { + count += 1; + Some(e) + }, + _ => None, + }); + (e, count) } #[macro_export] diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ed8915f59e1..8066f6c223e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -13,7 +13,9 @@ pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_ pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; @@ -45,7 +47,9 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; #[cfg(feature = "internal-lints")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 2d794d48dc5..53180d1f9f5 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -66,6 +66,15 @@ pub fn indent_of(cx: &T, span: Span) -> Option { snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } +/// Gets a snippet of the indentation of the line of a span +pub fn snippet_indent(cx: &T, span: Span) -> Option { + snippet_opt(cx, line_span(cx, span)).map(|mut s| { + let len = s.len() - s.trim_start().len(); + s.truncate(len); + s + }) +} + // 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. diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed new file mode 100644 index 00000000000..60371c9833c --- /dev/null +++ b/tests/ui/entry.fixed @@ -0,0 +1,101 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { + m.entry(k).or_insert(v); + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + if true { + e.insert(v); + } else { + e.insert(v2); + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + if true { + e.insert(v); + } else { + e.insert(v2); + return; + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + foo(); + e.insert(v); + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + match 0 { + 1 if true => { + e.insert(v); + }, + _ => { + e.insert(v2); + }, + }; + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + match 0 { + 0 => {}, + 1 => { + e.insert(v); + }, + _ => { + e.insert(v2); + }, + }; + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + foo(); + match 0 { + 0 if false => { + e.insert(v); + }, + 1 => { + foo(); + e.insert(v); + }, + 2 | 3 => { + for _ in 0..2 { + foo(); + } + if true { + e.insert(v); + } else { + e.insert(v2); + }; + }, + _ => { + e.insert(v2); + }, + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { + e.insert(m!(v)); + } +} + +fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs new file mode 100644 index 00000000000..4d3e241de78 --- /dev/null +++ b/tests/ui/entry.rs @@ -0,0 +1,103 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } + + if !m.contains_key(&k) { + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + } + } + + if !m.contains_key(&k) { + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + return; + } + } + + if !m.contains_key(&k) { + foo(); + m.insert(k, v); + } + + if !m.contains_key(&k) { + match 0 { + 1 if true => { + m.insert(k, v); + }, + _ => { + m.insert(k, v2); + }, + }; + } + + if !m.contains_key(&k) { + match 0 { + 0 => {}, + 1 => { + m.insert(k, v); + }, + _ => { + m.insert(k, v2); + }, + }; + } + + if !m.contains_key(&k) { + foo(); + match 0 { + 0 if false => { + m.insert(k, v); + }, + 1 => { + foo(); + m.insert(k, v); + }, + 2 | 3 => { + for _ in 0..2 { + foo(); + } + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + }; + }, + _ => { + m.insert(k, v2); + }, + } + } + + if !m.contains_key(&m!(k)) { + m.insert(m!(k), m!(v)); + } +} + +fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr new file mode 100644 index 00000000000..ab108ed6861 --- /dev/null +++ b/tests/ui/entry.stderr @@ -0,0 +1,171 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:16:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } + | |_____^ help: try this: `m.entry(k).or_insert(v);` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:20:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | if true { +LL | e.insert(v); +LL | } else { +LL | e.insert(v2); +LL | } + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:28:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v); +LL | | } else { +... | +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | if true { +LL | e.insert(v); +LL | } else { +LL | e.insert(v2); +LL | return; + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:37:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | foo(); +LL | e.insert(v); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:42:5 + | +LL | / if !m.contains_key(&k) { +LL | | match 0 { +LL | | 1 if true => { +LL | | m.insert(k, v); +... | +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | match 0 { +LL | 1 if true => { +LL | e.insert(v); +LL | }, +LL | _ => { + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:53:5 + | +LL | / if !m.contains_key(&k) { +LL | | match 0 { +LL | | 0 => {}, +LL | | 1 => { +... | +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | match 0 { +LL | 0 => {}, +LL | 1 => { +LL | e.insert(v); +LL | }, + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:65:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | match 0 { +LL | | 0 if false => { +... | +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | foo(); +LL | match 0 { +LL | 0 if false => { +LL | e.insert(v); +LL | }, + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:91:5 + | +LL | / if !m.contains_key(&m!(k)) { +LL | | m.insert(m!(k), m!(v)); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { +LL | e.insert(m!(v)); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry.rs:97:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | foo(); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { +LL | e.insert(v); +LL | foo(); +LL | } + | + +error: aborting due to 9 previous errors + diff --git a/tests/ui/entry_fixable.fixed b/tests/ui/entry_fixable.fixed deleted file mode 100644 index dcdaae7e724..00000000000 --- a/tests/ui/entry_fixable.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - m.entry(k).or_insert(v); -} - -fn main() {} diff --git a/tests/ui/entry_fixable.rs b/tests/ui/entry_fixable.rs deleted file mode 100644 index 55d5b21568d..00000000000 --- a/tests/ui/entry_fixable.rs +++ /dev/null @@ -1,17 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m.insert(k, v); - } -} - -fn main() {} diff --git a/tests/ui/entry_fixable.stderr b/tests/ui/entry_fixable.stderr deleted file mode 100644 index 87403200ced..00000000000 --- a/tests/ui/entry_fixable.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_fixable.rs:12:5 - | -LL | / if !m.contains_key(&k) { -LL | | m.insert(k, v); -LL | | } - | |_____^ help: consider using: `m.entry(k).or_insert(v);` - | - = note: `-D clippy::map-entry` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs index f530fc023cf..beb2d5c97b1 100644 --- a/tests/ui/entry_unfixable.rs +++ b/tests/ui/entry_unfixable.rs @@ -4,50 +4,14 @@ use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; -fn foo() {} - -fn insert_if_absent2(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m.insert(k, v) - } else { - None - }; -} - -fn insert_if_present2(m: &mut HashMap, k: K, v: V) { - if m.contains_key(&k) { - None - } else { - m.insert(k, v) - }; -} - -fn insert_if_absent3(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - foo(); - m.insert(k, v) - } else { - None - }; -} - -fn insert_if_present3(m: &mut HashMap, k: K, v: V) { - if m.contains_key(&k) { - None - } else { - foo(); - m.insert(k, v) +macro_rules! m { + ($map:expr, $key:expr, $value:expr) => { + $map.insert($key, $value) }; + ($e:expr) => {{ $e }}; } -fn insert_in_btreemap(m: &mut BTreeMap, k: K, v: V) { - if !m.contains_key(&k) { - foo(); - m.insert(k, v) - } else { - None - }; -} +fn foo() {} // should not trigger fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { @@ -70,4 +34,17 @@ fn insert_from_different_map2(m: &mut HashMap, n: &mut Ha } } +fn insert_in_macro(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + m!(m, k, v); + } +} + +fn use_map_then_insert(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } +} + fn main() {} diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr deleted file mode 100644 index e58c8d22dc4..00000000000 --- a/tests/ui/entry_unfixable.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:10:5 - | -LL | / if !m.contains_key(&k) { -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - | - = note: `-D clippy::map-entry` implied by `-D warnings` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:18:5 - | -LL | / if m.contains_key(&k) { -LL | | None -LL | | } else { -LL | | m.insert(k, v) -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:26:5 - | -LL | / if !m.contains_key(&k) { -LL | | foo(); -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:35:5 - | -LL | / if m.contains_key(&k) { -LL | | None -LL | | } else { -LL | | foo(); -LL | | m.insert(k, v) -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry_unfixable.rs:44:5 - | -LL | / if !m.contains_key(&k) { -LL | | foo(); -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: aborting due to 5 previous errors - -- cgit 1.4.1-3-g733a5 From b63a5b56d6a37029970445ab5cbb77aeb5e19e84 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 09:36:38 -0400 Subject: `map_entry` improvements Lint `if _.[!]contains_key(&_) { .. } else { .. }` so long as one of the branches contains an insertion. --- clippy_lints/src/entry.rs | 81 ++++++++++++++++++++++- tests/ui/entry_with_else.fixed | 73 +++++++++++++++++++++ tests/ui/entry_with_else.rs | 60 +++++++++++++++++ tests/ui/entry_with_else.stderr | 142 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 tests/ui/entry_with_else.fixed create mode 100644 tests/ui/entry_with_else.rs create mode 100644 tests/ui/entry_with_else.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index ca01d0a7f87..3e1d70b2e40 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,7 +1,7 @@ use clippy_utils::{ diagnostics::span_lint_and_sugg, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, - source::{snippet_indent, snippet_with_applicability, snippet_with_context}, + source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, SpanlessEq, }; use rustc_errors::Applicability; @@ -75,7 +75,84 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; - let sugg = if !contains_expr.negated || else_expr.is_some() || then_search.insertions.is_empty() { + let sugg = if let Some(else_expr) = else_expr { + // if .. { .. } else { .. } + let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { + Some(search) if !(then_search.insertions.is_empty() && search.insertions.is_empty()) => search, + _ => return, + }; + + if then_search.insertions.is_empty() || else_search.insertions.is_empty() { + // if .. { insert } else { .. } or if .. { .. } else { then } of + let (then_str, else_str, entry_kind) = if else_search.insertions.is_empty() { + if contains_expr.negated { + ( + then_search.snippet_vacant(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + "Vacant(e)", + ) + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + "Occupied(mut e)", + ) + } + } else if contains_expr.negated { + ( + else_search.snippet_occupied(cx, else_expr.span, &mut app), + snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), + "Occupied(mut e)", + ) + } else { + ( + else_search.snippet_vacant(cx, else_expr.span, &mut app), + snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), + "Vacant(e)", + ) + }; + format!( + "if let {}::{} = {}.entry({}) {} else {}", + map_ty.entry_path(), + entry_kind, + map_str, + key_str, + then_str, + else_str, + ) + } else { + // if .. { insert } else { insert } + let (then_str, else_str, then_entry, else_entry) = if contains_expr.negated { + ( + then_search.snippet_vacant(cx, then_expr.span, &mut app), + else_search.snippet_occupied(cx, else_expr.span, &mut app), + "Vacant(e)", + "Occupied(mut e)", + ) + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + else_search.snippet_vacant(cx, else_expr.span, &mut app), + "Occupied(mut e)", + "Vacant(e)", + ) + }; + let indent_str = snippet_indent(cx, expr.span); + let indent_str = indent_str.as_deref().unwrap_or(""); + format!( + "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\ + {indent} {entry}::{} => {}\n{indent}}}", + map_str, + key_str, + then_entry, + reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), + else_entry, + reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), + entry = map_ty.entry_path(), + indent = indent_str, + ) + } + } else if then_search.insertions.is_empty() || !contains_expr.negated { return; } else { // if .. { insert } diff --git a/tests/ui/entry_with_else.fixed b/tests/ui/entry_with_else.fixed new file mode 100644 index 00000000000..2332fa6313f --- /dev/null +++ b/tests/ui/entry_with_else.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V, v2: V) { + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v2); + } + } + + match m.entry(k) { + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v2); + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + } else { + foo(); + } + + if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { + e.insert(v); + } else { + foo(); + } + + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v2); + } + } + + match m.entry(k) { + std::collections::hash_map::Entry::Occupied(mut e) => { + if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + None + } + }; + + if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { + foo(); + Some(e.insert(v)) + } else { + None + }; +} + +fn main() {} diff --git a/tests/ui/entry_with_else.rs b/tests/ui/entry_with_else.rs new file mode 100644 index 00000000000..2ff0c038efe --- /dev/null +++ b/tests/ui/entry_with_else.rs @@ -0,0 +1,60 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if !m.contains_key(&k) { + m.insert(k, v); + } else { + foo(); + } + + if !m.contains_key(&k) { + foo(); + } else { + m.insert(k, v); + } + + if !m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if m.contains_key(&k) { + if true { m.insert(k, v) } else { m.insert(k, v2) } + } else { + m.insert(k, v) + }; + + if m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +fn main() {} diff --git a/tests/ui/entry_with_else.stderr b/tests/ui/entry_with_else.stderr new file mode 100644 index 00000000000..6f62ff8d374 --- /dev/null +++ b/tests/ui/entry_with_else.stderr @@ -0,0 +1,142 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:16:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:22:5 + | +LL | / if m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:28:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | foo(); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | e.insert(v); +LL | } else { +LL | foo(); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:34:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | } else { +LL | | m.insert(k, v); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { +LL | e.insert(v); +LL | } else { +LL | foo(); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:40:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:46:5 + | +LL | / if m.contains_key(&k) { +LL | | if true { m.insert(k, v) } else { m.insert(k, v2) } +LL | | } else { +LL | | m.insert(k, v) +LL | | }; + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } +LL | } +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:52:5 + | +LL | / if m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { +LL | foo(); +LL | Some(e.insert(v)) +LL | } else { +LL | None +LL | }; + | + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 3323ff71455c763a03353527d6203f90b53058fd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 09:47:56 -0400 Subject: `map_entry` improvements Suggest using `or_insert_with` when possible --- clippy_lints/src/entry.rs | 220 ++++++++++++++++++++++++++++++++--------- clippy_lints/src/manual_map.rs | 53 +--------- clippy_utils/src/lib.rs | 71 ++++++++++++- tests/ui/entry.fixed | 52 +++++----- tests/ui/entry.rs | 8 ++ tests/ui/entry.stderr | 65 +++++++----- 6 files changed, 322 insertions(+), 147 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 3e1d70b2e40..0decb22a491 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,4 +1,5 @@ use clippy_utils::{ + can_move_expr_to_closure_no_visit, diagnostics::span_lint_and_sugg, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, @@ -7,7 +8,7 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Expr, ExprKind, Guard, Local, Stmt, StmtKind, UnOp, + Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -78,13 +79,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let sugg = if let Some(else_expr) = else_expr { // if .. { .. } else { .. } let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) if !(then_search.insertions.is_empty() && search.insertions.is_empty()) => search, + Some(search) if !(then_search.edits.is_empty() && search.edits.is_empty()) => search, _ => return, }; - if then_search.insertions.is_empty() || else_search.insertions.is_empty() { + if then_search.edits.is_empty() || else_search.edits.is_empty() { // if .. { insert } else { .. } or if .. { .. } else { then } of - let (then_str, else_str, entry_kind) = if else_search.insertions.is_empty() { + let (then_str, else_str, entry_kind) = if else_search.edits.is_empty() { if contains_expr.negated { ( then_search.snippet_vacant(cx, then_expr.span, &mut app), @@ -152,37 +153,48 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { indent = indent_str, ) } - } else if then_search.insertions.is_empty() || !contains_expr.negated { + } else if then_search.edits.is_empty() { + // no insertions return; } else { // if .. { insert } - match then_search.as_single_insertion() { - Some(insertion) if !insertion.value.can_have_side_effects() => { - format!( - "{}.entry({}).or_insert({});", - map_str, - key_str, - snippet_with_context(cx, insertion.value.span, insertion.call.span.ctxt(), "..", &mut app).0, + if !then_search.allow_insert_closure { + let (body_str, entry_kind) = if contains_expr.negated { + (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + "Occupied(mut e)", ) - }, - _ => { - let (body_str, entry_kind) = if contains_expr.negated { - (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + }; + format!( + "if let {}::{} = {}.entry({}) {}", + map_ty.entry_path(), + entry_kind, + map_str, + key_str, + body_str, + ) + } else if let Some(insertion) = then_search.as_single_insertion() { + let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; + if contains_expr.negated { + if insertion.value.can_have_side_effects() { + format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str) } else { - ( - then_search.snippet_occupied(cx, then_expr.span, &mut app), - "Occupied(mut e)", - ) - }; - format!( - "if let {}::{} = {}.entry({}) {}", - map_ty.entry_path(), - entry_kind, - map_str, - key_str, - body_str, - ) - }, + format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) + } + } else { + // Todo: if let Some(v) = map.get_mut(k) + return; + } + } else { + let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); + if contains_expr.negated { + format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) + } else { + // Todo: if let Some(v) = map.get_mut(k) + return; + } } }; @@ -281,6 +293,19 @@ fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { + /// A semicolon that needs to be removed. Used to create a closure for `insert_with`. + RemoveSemi(Span), + /// An insertion into the map. + Insertion(Insertion<'tcx>), +} +impl Edit<'tcx> { + fn as_insertion(self) -> Option> { + if let Self::Insertion(i) = self { Some(i) } else { None } + } +} #[derive(Clone, Copy)] struct Insertion<'tcx> { call: &'tcx Expr<'tcx>, @@ -303,12 +328,17 @@ struct InsertSearcher<'cx, 'i, 'tcx> { key: &'tcx Expr<'tcx>, /// The context of the top level block. All insert calls must be in the same context. ctxt: SyntaxContext, + /// Whether this expression can be safely moved into a closure. + allow_insert_closure: bool, /// Whether this expression can use the entry api. can_use_entry: bool, + /// Whether this expression is the final expression in this code path. This may be a statement. + in_tail_pos: bool, // A single insert expression has a slightly different suggestion. is_single_insert: bool, is_map_used: bool, - insertions: &'i mut Vec>, + edits: &'i mut Vec>, + loops: Vec, } impl<'tcx> InsertSearcher<'_, '_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but @@ -316,11 +346,22 @@ impl<'tcx> InsertSearcher<'_, '_, 'tcx> { /// given expression. fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool { let is_map_used = self.is_map_used; + let in_tail_pos = self.in_tail_pos; self.visit_expr(e); let res = self.is_map_used; self.is_map_used = is_map_used; + self.in_tail_pos = in_tail_pos; res } + + /// Visits an expression which is not itself in a tail position, but other sibling expressions + /// may be. e.g. if conditions + fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) { + let in_tail_pos = self.in_tail_pos; + self.in_tail_pos = false; + self.visit_expr(e); + self.in_tail_pos = in_tail_pos; + } } impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; @@ -330,17 +371,63 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Semi(e) | StmtKind::Expr(e) => self.visit_expr(e), + StmtKind::Semi(e) => { + self.visit_expr(e); + + if self.in_tail_pos && self.allow_insert_closure { + // The spans are used to slice the top level expression into multiple parts. This requires that + // they all come from the same part of the source code. + if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt { + self.edits + .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP))); + } else { + self.allow_insert_closure = false; + } + } + }, + StmtKind::Expr(e) => self.visit_expr(e), StmtKind::Local(Local { init: Some(e), .. }) => { + self.allow_insert_closure &= !self.in_tail_pos; + self.in_tail_pos = false; self.is_single_insert = false; self.visit_expr(e); }, _ => { + self.allow_insert_closure &= !self.in_tail_pos; self.is_single_insert = false; }, } } + fn visit_block(&mut self, block: &'tcx Block<'_>) { + // If the block is in a tail position, then the last expression (possibly a statement) is in the + // tail position. The rest, however, are not. + match (block.stmts, block.expr) { + ([], None) => { + self.allow_insert_closure &= !self.in_tail_pos; + }, + ([], Some(expr)) => self.visit_expr(expr), + (stmts, Some(expr)) => { + let in_tail_pos = self.in_tail_pos; + self.in_tail_pos = false; + for stmt in stmts { + self.visit_stmt(stmt); + } + self.in_tail_pos = in_tail_pos; + self.visit_expr(expr); + }, + ([stmts @ .., stmt], None) => { + let in_tail_pos = self.in_tail_pos; + self.in_tail_pos = false; + for stmt in stmts { + self.visit_stmt(stmt); + } + self.in_tail_pos = in_tail_pos; + self.visit_stmt(stmt); + }, + } + } + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if !self.can_use_entry { return; @@ -357,15 +444,16 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { return; } - self.insertions.push(Insertion { + self.edits.push(Edit::Insertion(Insertion { call: expr, value: insert_expr.value, - }); + })); self.is_map_used = true; + self.allow_insert_closure &= self.in_tail_pos; // The value doesn't affect whether there is only a single insert expression. let is_single_insert = self.is_single_insert; - self.visit_expr(insert_expr.value); + self.visit_non_tail_expr(insert_expr.value); self.is_single_insert = is_single_insert; }, _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => { @@ -374,7 +462,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { _ => match expr.kind { ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { self.is_single_insert = false; - self.visit_expr(cond_expr); + self.visit_non_tail_expr(cond_expr); // Each branch may contain it's own insert expression. let mut is_map_used = self.visit_cond_arm(then_expr); is_map_used |= self.visit_cond_arm(else_expr); @@ -382,31 +470,38 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { }, ExprKind::Match(scrutinee_expr, arms, _) => { self.is_single_insert = false; - self.visit_expr(scrutinee_expr); + self.visit_non_tail_expr(scrutinee_expr); // Each branch may contain it's own insert expression. let mut is_map_used = self.is_map_used; for arm in arms { if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { - self.visit_expr(guard) + self.visit_non_tail_expr(guard) } is_map_used |= self.visit_cond_arm(arm.body); } self.is_map_used = is_map_used; }, ExprKind::Loop(block, ..) => { + self.loops.push(expr.hir_id); + self.allow_insert_closure &= !self.in_tail_pos; // Don't allow insertions inside of a loop. - let insertions_len = self.insertions.len(); + let edit_len = self.edits.len(); self.visit_block(block); - if self.insertions.len() != insertions_len { + if self.edits.len() != edit_len { self.can_use_entry = false; } + self.loops.pop(); }, ExprKind::Block(block, _) => self.visit_block(block), ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => { self.can_use_entry = false; }, _ => { + self.allow_insert_closure &= !self.in_tail_pos; + self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops); + // Sub expressions are no longer in the tail position. self.is_single_insert = false; + self.in_tail_pos = false; walk_expr(self, expr); }, }, @@ -415,18 +510,19 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { } struct InsertSearchResults<'tcx> { - insertions: Vec>, + edits: Vec>, + allow_insert_closure: bool, is_single_insert: bool, } impl InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option> { - self.is_single_insert.then(|| self.insertions[0]) + self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } fn snippet_occupied(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { let ctxt = span.ctxt(); let mut res = String::new(); - for insertion in self.insertions.iter() { + for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { res.push_str(&snippet_with_applicability( cx, span.until(insertion.call.span), @@ -451,7 +547,7 @@ impl InsertSearchResults<'tcx> { fn snippet_vacant(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { let ctxt = span.ctxt(); let mut res = String::new(); - for insertion in self.insertions.iter() { + for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { res.push_str(&snippet_with_applicability( cx, span.until(insertion.call.span), @@ -485,27 +581,57 @@ impl InsertSearchResults<'tcx> { res.push_str(&snippet_with_applicability(cx, span, "..", app)); res } + + fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + let ctxt = span.ctxt(); + let mut res = String::new(); + for edit in &self.edits { + match *edit { + Edit::Insertion(insertion) => { + res.push_str(&snippet_with_applicability( + cx, + span.until(insertion.call.span), + "..", + app, + )); + res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); + span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); + }, + Edit::RemoveSemi(semi_span) => { + res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app)); + span = span.trim_start(semi_span).unwrap_or(DUMMY_SP); + }, + } + } + res.push_str(&snippet_with_applicability(cx, span, "..", app)); + res + } } fn find_insert_calls( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - let mut insertions = Vec::new(); + let mut edits = Vec::new(); let mut s = InsertSearcher { cx, map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - insertions: &mut insertions, + edits: &mut edits, is_map_used: false, + allow_insert_closure: true, can_use_entry: true, + in_tail_pos: true, is_single_insert: true, + loops: Vec::new(), }; s.visit_expr(expr); + let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; s.can_use_entry.then(|| InsertSearchResults { - insertions, + edits, + allow_insert_closure, is_single_insert, }) } diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 29be0739977..3f8b976fcac 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,15 +1,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::{can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ - def::Res, - intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, + Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, QPath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -193,51 +191,6 @@ impl LateLintPass<'_> for ManualMap { } } -// Checks if the expression can be moved into a closure as is. -fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, - make_closure: bool, - } - impl Visitor<'tcx> for V<'_, 'tcx> { - type Map = ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - match e.kind { - ExprKind::Break(..) - | ExprKind::Continue(_) - | ExprKind::Ret(_) - | ExprKind::Yield(..) - | ExprKind::InlineAsm(_) - | ExprKind::LlvmInlineAsm(_) => { - self.make_closure = false; - }, - // Accessing a field of a local value can only be done if the type isn't - // partially moved. - ExprKind::Field(base_expr, _) - if matches!( - base_expr.kind, - ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) - ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) => - { - // TODO: check if the local has been partially moved. Assume it has for now. - self.make_closure = false; - return; - } - _ => (), - }; - walk_expr(self, e); - } - } - - let mut v = V { cx, make_closure: true }; - v.visit_expr(expr); - v.make_closure -} - // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0abee49f40c..5d6c1337be6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -60,7 +60,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, @@ -82,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::is_recursively_primitive_type; +use crate::ty::{can_partially_move_ty, is_recursively_primitive_type}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -539,6 +539,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio None } +/// Checks if the top level expression can be moved into a closure as is. +pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool { + match expr.kind { + ExprKind::Break(Destination { target_id: Ok(id), .. }, _) + | ExprKind::Continue(Destination { target_id: Ok(id), .. }) + if jump_targets.contains(&id) => + { + true + }, + ExprKind::Break(..) + | ExprKind::Continue(_) + | ExprKind::Ret(_) + | ExprKind::Yield(..) + | ExprKind::InlineAsm(_) + | ExprKind::LlvmInlineAsm(_) => false, + // Accessing a field of a local value can only be done if the type isn't + // partially moved. + ExprKind::Field(base_expr, _) + if matches!( + base_expr.kind, + ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) + ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) => + { + // TODO: check if the local has been partially moved. Assume it has for now. + false + } + _ => true, + } +} + +/// Checks if the expression can be moved into a closure as is. +pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + loops: Vec, + allow_closure: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if !self.allow_closure { + return; + } + if let ExprKind::Loop(b, ..) = e.kind { + self.loops.push(e.hir_id); + self.visit_block(b); + self.loops.pop(); + } else { + self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops); + walk_expr(self, e); + } + } + } + + let mut v = V { + cx, + allow_closure: true, + loops: Vec::new(), + }; + v.visit_expr(expr); + v.allow_closure +} + /// Returns the method names and argument list of nested method call expressions that make up /// `expr`. method/span lists are sorted with the most recent call first. pub fn method_calls<'tcx>( diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index 60371c9833c..524f2132797 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -15,13 +15,21 @@ fn foo() {} fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { m.entry(k).or_insert(v); - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { if true { - e.insert(v); + v } else { - e.insert(v2); + v2 } - } + }); + + m.entry(k).or_insert_with(|| { + if true { + v + } else { + v2 + } + }); if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { if true { @@ -32,21 +40,21 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { foo(); - e.insert(v); - } + v + }); - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { match 0 { 1 if true => { - e.insert(v); + v }, _ => { - e.insert(v2); + v2 }, - }; - } + } + }); if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { match 0 { @@ -60,35 +68,33 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: }; } - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { foo(); match 0 { 0 if false => { - e.insert(v); + v }, 1 => { foo(); - e.insert(v); + v }, 2 | 3 => { for _ in 0..2 { foo(); } if true { - e.insert(v); + v } else { - e.insert(v2); - }; + v2 + } }, _ => { - e.insert(v2); + v2 }, } - } + }); - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { - e.insert(m!(v)); - } + m.entry(m!(k)).or_insert_with(|| m!(v)); } fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index 4d3e241de78..ff4890eeeb6 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -25,6 +25,14 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + if !m.contains_key(&k) { + if true { + m.insert(k, v) + } else { + m.insert(k, v2) + }; + } + if !m.contains_key(&k) { if true { m.insert(k, v); diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index ab108ed6861..63b5f5a0b2c 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -22,11 +22,11 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | if true { -LL | e.insert(v); +LL | v LL | } else { -LL | e.insert(v2); +LL | v2 LL | } ... @@ -35,6 +35,28 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` | LL | / if !m.contains_key(&k) { LL | | if true { +LL | | m.insert(k, v) +LL | | } else { +LL | | m.insert(k, v2) +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | if true { +LL | v +LL | } else { +LL | v2 +LL | } + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:36:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { LL | | m.insert(k, v); LL | | } else { ... | @@ -53,7 +75,7 @@ LL | return; ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:37:5 + --> $DIR/entry.rs:45:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -63,14 +85,14 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | foo(); -LL | e.insert(v); -LL | } +LL | v +LL | }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:42:5 + --> $DIR/entry.rs:50:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -83,16 +105,16 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | match 0 { LL | 1 if true => { -LL | e.insert(v); +LL | v LL | }, LL | _ => { ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:53:5 + --> $DIR/entry.rs:61:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -114,7 +136,7 @@ LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:65:5 + --> $DIR/entry.rs:73:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -127,31 +149,24 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | foo(); LL | match 0 { LL | 0 if false => { -LL | e.insert(v); +LL | v LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:91:5 + --> $DIR/entry.rs:99:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); LL | | } - | |_____^ - | -help: try this - | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { -LL | e.insert(m!(v)); -LL | } - | + | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry.rs:97:5 + --> $DIR/entry.rs:105:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -167,5 +182,5 @@ LL | foo(); LL | } | -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From bcf348800751e8a03fdbaa49130e53a463e3a441 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 29 Mar 2021 20:17:03 -0400 Subject: Minor cleanup of `map_entry` and a few additional tests. --- clippy_lints/src/entry.rs | 187 ++++++++++++++++++------------------ clippy_lints/src/manual_map.rs | 8 +- clippy_utils/src/lib.rs | 59 +++--------- tests/ui/entry.fixed | 58 ++++++++++- tests/ui/entry.rs | 58 ++++++++++- tests/ui/entry.stderr | 30 +++--- tests/ui/entry_unfixable.rs | 50 ---------- tests/ui/string_lit_as_bytes.fixed | 2 +- tests/ui/string_lit_as_bytes.rs | 2 +- tests/ui/string_lit_as_bytes.stderr | 4 +- 10 files changed, 238 insertions(+), 220 deletions(-) delete mode 100644 tests/ui/entry_unfixable.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 0decb22a491..8db5050a5ac 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -77,40 +77,33 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; let sugg = if let Some(else_expr) = else_expr { - // if .. { .. } else { .. } let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) if !(then_search.edits.is_empty() && search.edits.is_empty()) => search, - _ => return, + Some(search) => search, + None => return, }; - if then_search.edits.is_empty() || else_search.edits.is_empty() { - // if .. { insert } else { .. } or if .. { .. } else { then } of - let (then_str, else_str, entry_kind) = if else_search.edits.is_empty() { - if contains_expr.negated { - ( - then_search.snippet_vacant(cx, then_expr.span, &mut app), - snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), - "Vacant(e)", - ) - } else { - ( - then_search.snippet_occupied(cx, then_expr.span, &mut app), - snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), - "Occupied(mut e)", - ) - } - } else if contains_expr.negated { - ( + if then_search.edits.is_empty() && else_search.edits.is_empty() { + // No insertions + return; + } else if then_search.edits.is_empty() || else_search.edits.is_empty() { + // if .. { insert } else { .. } or if .. { .. } else { insert } + let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) { + (true, true) => ( + then_search.snippet_vacant(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + ), + (true, false) => ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + ), + (false, true) => ( else_search.snippet_occupied(cx, else_expr.span, &mut app), snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), - "Occupied(mut e)", - ) - } else { - ( + ), + (false, false) => ( else_search.snippet_vacant(cx, else_expr.span, &mut app), snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), - "Vacant(e)", - ) + ), }; format!( "if let {}::{} = {}.entry({}) {} else {}", @@ -123,19 +116,15 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { ) } else { // if .. { insert } else { insert } - let (then_str, else_str, then_entry, else_entry) = if contains_expr.negated { + let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated { ( then_search.snippet_vacant(cx, then_expr.span, &mut app), else_search.snippet_occupied(cx, else_expr.span, &mut app), - "Vacant(e)", - "Occupied(mut e)", ) } else { ( then_search.snippet_occupied(cx, then_expr.span, &mut app), else_search.snippet_vacant(cx, else_expr.span, &mut app), - "Occupied(mut e)", - "Vacant(e)", ) }; let indent_str = snippet_indent(cx, expr.span); @@ -153,19 +142,18 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { indent = indent_str, ) } - } else if then_search.edits.is_empty() { - // no insertions - return; } else { + if then_search.edits.is_empty() { + // no insertions + return; + } + // if .. { insert } if !then_search.allow_insert_closure { let (body_str, entry_kind) = if contains_expr.negated { - (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + then_search.snippet_vacant(cx, then_expr.span, &mut app) } else { - ( - then_search.snippet_occupied(cx, then_expr.span, &mut app), - "Occupied(mut e)", - ) + then_search.snippet_occupied(cx, then_expr.span, &mut app) }; format!( "if let {}::{} = {}.entry({}) {}", @@ -184,7 +172,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) } } else { - // Todo: if let Some(v) = map.get_mut(k) + // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. + // This would need to be a different lint. return; } } else { @@ -192,7 +181,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { if contains_expr.negated { format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) } else { - // Todo: if let Some(v) = map.get_mut(k) + // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. + // This would need to be a different lint. return; } } @@ -222,7 +212,7 @@ impl MapType { Self::BTree => "BTreeMap", } } - fn entry_path(self) -> &'staic str { + fn entry_path(self) -> &'static str { match self { Self::Hash => "std::collections::hash_map::Entry", Self::BTree => "std::collections::btree_map::Entry", @@ -312,15 +302,16 @@ struct Insertion<'tcx> { value: &'tcx Expr<'tcx>, } -// This visitor needs to do a multiple things: -// * Find all usages of the map. Only insertions into the map which share the same key are -// permitted. All others will prevent the lint. -// * Determine if the final statement executed is an insertion. This is needed to use `insert_with`. -// * Determine if there's any sub-expression that can't be placed in a closure. -// * Determine if there's only a single insert statement. This is needed to give better suggestions. - +/// This visitor needs to do a multiple things: +/// * Find all usages of the map. An insertion can only be made before any other usages of the map. +/// * Determine if there's an insertion using the same key. There's no need for the entry api +/// otherwise. +/// * Determine if the final statement executed is an insertion. This is needed to use +/// `or_insert_with`. +/// * Determine if there's any sub-expression that can't be placed in a closure. +/// * Determine if there's only a single insert statement. `or_insert` can be used in this case. #[allow(clippy::struct_excessive_bools)] -struct InsertSearcher<'cx, 'i, 'tcx> { +struct InsertSearcher<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, /// The map expression used in the contains call. map: &'tcx Expr<'tcx>, @@ -334,13 +325,16 @@ struct InsertSearcher<'cx, 'i, 'tcx> { can_use_entry: bool, /// Whether this expression is the final expression in this code path. This may be a statement. in_tail_pos: bool, - // A single insert expression has a slightly different suggestion. + // Is this expression a single insert. A slightly better suggestion can be made in this case. is_single_insert: bool, + /// If the visitor has seen the map being used. is_map_used: bool, - edits: &'i mut Vec>, + /// The locations where changes need to be made for the suggestion. + edits: Vec>, + /// A stack of loops the visitor is currently in. loops: Vec, } -impl<'tcx> InsertSearcher<'_, '_, 'tcx> { +impl<'tcx> InsertSearcher<'_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but /// only if they are on separate code paths. This will return whether the map was used in the /// given expression. @@ -363,7 +357,7 @@ impl<'tcx> InsertSearcher<'_, '_, 'tcx> { self.in_tail_pos = in_tail_pos; } } -impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { +impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -483,6 +477,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { }, ExprKind::Loop(block, ..) => { self.loops.push(expr.hir_id); + self.is_single_insert = false; self.allow_insert_closure &= !self.in_tail_pos; // Don't allow insertions inside of a loop. let edit_len = self.edits.len(); @@ -519,7 +514,13 @@ impl InsertSearchResults<'tcx> { self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } - fn snippet_occupied(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + fn snippet( + &self, + cx: &LateContext<'_>, + mut span: Span, + app: &mut Applicability, + write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability), + ) -> String { let ctxt = span.ctxt(); let mut res = String::new(); for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { @@ -530,13 +531,13 @@ impl InsertSearchResults<'tcx> { app, )); if is_expr_used_or_unified(cx.tcx, insertion.call) { - res.push_str("Some(e.insert("); - res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); - res.push_str("))"); + write_wrapped(&mut res, insertion, ctxt, app); } else { - res.push_str("e.insert("); - res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); - res.push(')'); + let _ = write!( + res, + "e.insert({})", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0 + ); } span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); } @@ -544,42 +545,41 @@ impl InsertSearchResults<'tcx> { res } - fn snippet_vacant(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { - let ctxt = span.ctxt(); - let mut res = String::new(); - for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { - res.push_str(&snippet_with_applicability( - cx, - span.until(insertion.call.span), - "..", - app, - )); - if is_expr_used_or_unified(cx.tcx, insertion.call) { - if is_expr_final_block_expr(cx.tcx, insertion.call) { - let _ = write!( + fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) { + ( + self.snippet(cx, span, app, |res, insertion, ctxt, app| { + // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value` + let _ = write!( + res, + "Some(e.insert({}))", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0 + ); + }), + "Occupied(mut e)", + ) + } + + fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) { + ( + self.snippet(cx, span, app, |res, insertion, ctxt, app| { + // Insertion into a map would return `None`, but the entry returns a mutable reference. + let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) { + write!( res, "e.insert({});\n{}None", snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""), - ); + ) } else { - let _ = write!( + write!( res, "{{ e.insert({}); None }}", snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, - ); - } - } else { - let _ = write!( - res, - "e.insert({})", - snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, - ); - } - span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); - } - res.push_str(&snippet_with_applicability(cx, span, "..", app)); - res + ) + }; + }), + "Vacant(e)", + ) } fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { @@ -588,6 +588,7 @@ impl InsertSearchResults<'tcx> { for edit in &self.edits { match *edit { Edit::Insertion(insertion) => { + // Cut out the value from `map.insert(key, value)` res.push_str(&snippet_with_applicability( cx, span.until(insertion.call.span), @@ -598,6 +599,7 @@ impl InsertSearchResults<'tcx> { span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); }, Edit::RemoveSemi(semi_span) => { + // Cut out the semicolon. This allows the value to be returned from the closure. res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app)); span = span.trim_start(semi_span).unwrap_or(DUMMY_SP); }, @@ -607,18 +609,18 @@ impl InsertSearchResults<'tcx> { res } } + fn find_insert_calls( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - let mut edits = Vec::new(); let mut s = InsertSearcher { cx, map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: &mut edits, + edits: Vec::new(), is_map_used: false, allow_insert_closure: true, can_use_entry: true, @@ -629,6 +631,7 @@ fn find_insert_calls( s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; + let edits = s.edits; s.can_use_entry.then(|| InsertSearchResults { edits, allow_insert_closure, diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 3f8b976fcac..d9b75149b9f 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,13 +2,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; +use clippy_utils::{ + can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs, +}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, QPath, -}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5d6c1337be6..90a6fd225ab 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -64,8 +64,8 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, - Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, + QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1312,48 +1312,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } -pub fn get_expr_use_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { - let map = tcx.hir(); - let mut child_id = expr.hir_id; - let mut iter = map.parent_iter(child_id); - loop { - match iter.next() { - None => break None, - Some((id, Node::Block(_))) => child_id = id, - Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id, - Some((_, Node::Expr(expr))) => match expr.kind { - ExprKind::Break( - Destination { - target_id: Ok(dest), .. - }, - _, - ) => { - iter = map.parent_iter(dest); - child_id = dest; - }, - ExprKind::DropTemps(_) | ExprKind::Block(..) => child_id = expr.hir_id, - ExprKind::If(control_expr, ..) | ExprKind::Match(control_expr, ..) - if control_expr.hir_id != child_id => - { - child_id = expr.hir_id - }, - _ => break Some(Node::Expr(expr)), - }, - Some((_, node)) => break Some(node), - } - } -} - -pub fn is_expr_used(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { - !matches!( - get_expr_use_node(tcx, expr), - Some(Node::Stmt(Stmt { - kind: StmtKind::Expr(_) | StmtKind::Semi(_), - .. - })) - ) -} - +/// Gets the node where an expression is either used, or it's type is unified with another branch. pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { let map = tcx.hir(); let mut child_id = expr.hir_id; @@ -1374,16 +1333,26 @@ pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> O } } +/// Checks if the result of an expression is used, or it's type is unified with another branch. pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { !matches!( get_expr_use_or_unification_node(tcx, expr), None | Some(Node::Stmt(Stmt { - kind: StmtKind::Expr(_) | StmtKind::Semi(_), + kind: StmtKind::Expr(_) + | StmtKind::Semi(_) + | StmtKind::Local(Local { + pat: Pat { + kind: PatKind::Wild, + .. + }, + .. + }), .. })) ) } +/// Checks if the expression is the final expression returned from a block. pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..))) } diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index 524f2132797..cfad3090ba3 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -2,6 +2,7 @@ #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] #![warn(clippy::map_entry)] +#![feature(asm)] use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; @@ -10,11 +11,19 @@ macro_rules! m { ($e:expr) => {{ $e }}; } +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + fn foo() {} -fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { +fn hash_map(m: &mut HashMap, m2: &mut HashMap, k: K, k2: K, v: V, v2: V) { + // or_insert(v) m.entry(k).or_insert(v); + // semicolon on insert, use or_insert_with(..) m.entry(k).or_insert_with(|| { if true { v @@ -23,6 +32,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // semicolon on if, use or_insert_with(..) m.entry(k).or_insert_with(|| { if true { v @@ -31,6 +41,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // early return, use if let if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { if true { e.insert(v); @@ -40,11 +51,13 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // use or_insert_with(..) m.entry(k).or_insert_with(|| { foo(); v }); + // semicolon on insert and match, use or_insert_with(..) m.entry(k).or_insert_with(|| { match 0 { 1 if true => { @@ -56,18 +69,17 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // one branch doesn't insert, use if let if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { match 0 { - 0 => {}, - 1 => { - e.insert(v); - }, + 0 => foo(), _ => { e.insert(v2); }, }; } + // use or_insert_with m.entry(k).or_insert_with(|| { foo(); match 0 { @@ -94,10 +106,46 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // ok, insert in loop + if !m.contains_key(&k) { + for _ in 0..2 { + m.insert(k, v); + } + } + + // macro_expansion test, use or_insert(..) m.entry(m!(k)).or_insert_with(|| m!(v)); + + // ok, map used before insertion + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } + + // ok, inline asm + if !m.contains_key(&k) { + unsafe { asm!("nop") } + m.insert(k, v); + } + + // ok, different keys. + if !m.contains_key(&k) { + m.insert(k2, v); + } + + // ok, different maps + if !m.contains_key(&k) { + m2.insert(k, v); + } + + // ok, insert in macro + if !m.contains_key(&k) { + insert!(m, k, v); + } } fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + // insert then do something, use if let if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { e.insert(v); foo(); diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index ff4890eeeb6..fa9280b58de 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -2,6 +2,7 @@ #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] #![warn(clippy::map_entry)] +#![feature(asm)] use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; @@ -10,13 +11,21 @@ macro_rules! m { ($e:expr) => {{ $e }}; } +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + fn foo() {} -fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { +fn hash_map(m: &mut HashMap, m2: &mut HashMap, k: K, k2: K, v: V, v2: V) { + // or_insert(v) if !m.contains_key(&k) { m.insert(k, v); } + // semicolon on insert, use or_insert_with(..) if !m.contains_key(&k) { if true { m.insert(k, v); @@ -25,6 +34,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // semicolon on if, use or_insert_with(..) if !m.contains_key(&k) { if true { m.insert(k, v) @@ -33,6 +43,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: }; } + // early return, use if let if !m.contains_key(&k) { if true { m.insert(k, v); @@ -42,11 +53,13 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // use or_insert_with(..) if !m.contains_key(&k) { foo(); m.insert(k, v); } + // semicolon on insert and match, use or_insert_with(..) if !m.contains_key(&k) { match 0 { 1 if true => { @@ -58,18 +71,17 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: }; } + // one branch doesn't insert, use if let if !m.contains_key(&k) { match 0 { - 0 => {}, - 1 => { - m.insert(k, v); - }, + 0 => foo(), _ => { m.insert(k, v2); }, }; } + // use or_insert_with if !m.contains_key(&k) { foo(); match 0 { @@ -96,12 +108,48 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // ok, insert in loop + if !m.contains_key(&k) { + for _ in 0..2 { + m.insert(k, v); + } + } + + // macro_expansion test, use or_insert(..) if !m.contains_key(&m!(k)) { m.insert(m!(k), m!(v)); } + + // ok, map used before insertion + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } + + // ok, inline asm + if !m.contains_key(&k) { + unsafe { asm!("nop") } + m.insert(k, v); + } + + // ok, different keys. + if !m.contains_key(&k) { + m.insert(k2, v); + } + + // ok, different maps + if !m.contains_key(&k) { + m2.insert(k, v); + } + + // ok, insert in macro + if !m.contains_key(&k) { + insert!(m, k, v); + } } fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + // insert then do something, use if let if !m.contains_key(&k) { m.insert(k, v); foo(); diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 63b5f5a0b2c..2f075a97010 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:16:5 + --> $DIR/entry.rs:24:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::map-entry` implied by `-D warnings` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:20:5 + --> $DIR/entry.rs:29:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -31,7 +31,7 @@ LL | } ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:28:5 + --> $DIR/entry.rs:38:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -53,7 +53,7 @@ LL | } ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:36:5 + --> $DIR/entry.rs:47:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -75,7 +75,7 @@ LL | return; ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:45:5 + --> $DIR/entry.rs:57:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -92,7 +92,7 @@ LL | }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:50:5 + --> $DIR/entry.rs:63:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -114,12 +114,12 @@ LL | _ => { ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:61:5 + --> $DIR/entry.rs:75:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { -LL | | 0 => {}, -LL | | 1 => { +LL | | 0 => foo(), +LL | | _ => { ... | LL | | }; LL | | } @@ -129,14 +129,14 @@ help: try this | LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { LL | match 0 { -LL | 0 => {}, -LL | 1 => { -LL | e.insert(v); +LL | 0 => foo(), +LL | _ => { +LL | e.insert(v2); LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:73:5 + --> $DIR/entry.rs:85:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -158,7 +158,7 @@ LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:99:5 + --> $DIR/entry.rs:119:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); @@ -166,7 +166,7 @@ LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry.rs:105:5 + --> $DIR/entry.rs:153:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs deleted file mode 100644 index beb2d5c97b1..00000000000 --- a/tests/ui/entry_unfixable.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -macro_rules! m { - ($map:expr, $key:expr, $value:expr) => { - $map.insert($key, $value) - }; - ($e:expr) => {{ $e }}; -} - -fn foo() {} - -// should not trigger -fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { - if !m.contains_key(&k) { - m.insert(o, v); - } -} - -// should not trigger, because the one uses different HashMap from another one -fn insert_from_different_map(m: HashMap, n: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - n.insert(k, v); - } -} - -// should not trigger, because the one uses different HashMap from another one -fn insert_from_different_map2(m: &mut HashMap, n: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - n.insert(k, v); - } -} - -fn insert_in_macro(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m!(m, k, v); - } -} - -fn use_map_then_insert(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - let _ = m.len(); - m.insert(k, v); - } -} - -fn main() {} diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index dd22bfa5c53..df2256e4f97 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -22,7 +22,7 @@ fn str_lit_as_bytes() { let current_version = env!("CARGO_PKG_VERSION").as_bytes(); - let includestr = include_bytes!("entry_unfixable.rs"); + let includestr = include_bytes!("string_lit_as_bytes.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 d2a710ed6b8..c6bf8f732ed 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -22,7 +22,7 @@ fn str_lit_as_bytes() { let current_version = env!("CARGO_PKG_VERSION").as_bytes(); - let includestr = include_str!("entry_unfixable.rs").as_bytes(); + let includestr = include_str!("string_lit_as_bytes.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 e0ddb070b50..f47d6161c6c 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -27,8 +27,8 @@ LL | let bs = "lit to owned".to_owned().into_bytes(); error: calling `as_bytes()` on `include_str!(..)` --> $DIR/string_lit_as_bytes.rs:25:22 | -LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` +LL | let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")` error: calling `as_bytes()` on a string literal --> $DIR/string_lit_as_bytes.rs:27:13 -- cgit 1.4.1-3-g733a5 From 779d98f6ccbd76807e811a134bb00a4e0d92c6db Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 10:37:42 -0400 Subject: Don't allow adjustments for `manual_map` --- clippy_lints/src/manual_map.rs | 6 ++++++ tests/ui/manual_map_option.fixed | 7 +++++++ tests/ui/manual_map_option.rs | 7 +++++++ 3 files changed, 20 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 29be0739977..f16ed4104fe 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -104,12 +104,18 @@ impl LateLintPass<'_> for ManualMap { None => return, }; + // These two lints will go back and forth with each other. if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { return; } + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { + return; + } + if !can_move_expr_to_closure(cx, some_expr) { return; } diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index ee015845777..40d01df6379 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -146,4 +146,11 @@ fn main() { None => None, }; } + + // #7077 + let s = &String::new(); + let _: Option<&str> = match Some(s) { + Some(s) => Some(s), + None => None, + }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 29509bddfd9..cfef0c5cc4e 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -212,4 +212,11 @@ fn main() { None => None, }; } + + // #7077 + let s = &String::new(); + let _: Option<&str> = match Some(s) { + Some(s) => Some(s), + None => None, + }; } -- cgit 1.4.1-3-g733a5 From 014bf4390c3dcce701749d09ea253126181bc37a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 15 Apr 2021 12:57:31 -0500 Subject: Add note for pre-expansion passes --- clippy_lints/src/lib.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..93631c5669b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -390,6 +390,7 @@ pub use crate::utils::conf::Conf; /// /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { + // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. 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); -- cgit 1.4.1-3-g733a5 From f6c5d8d599070bacad800ac7014fa3a6f140eadc Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 7 Apr 2021 16:19:25 -0400 Subject: Remove all usages of `match_path`, `match_qpath` and `match_path_ast` except the `author` lint. Add note to fix `MATCH_TYPE_ON_DIAG_ITEM` Add false negative test for `uninit_assumed_init` --- clippy_lints/src/assertions_on_constants.rs | 5 +- clippy_lints/src/excessive_bools.rs | 6 +- clippy_lints/src/implicit_hasher.rs | 7 +- clippy_lints/src/implicit_saturating_sub.rs | 43 ++++++++--- clippy_lints/src/infinite_iter.rs | 4 +- clippy_lints/src/map_identity.rs | 4 +- clippy_lints/src/matches.rs | 24 ++++-- clippy_lints/src/methods/filter_map_identity.rs | 12 +-- clippy_lints/src/methods/flat_map_identity.rs | 18 ++--- .../src/methods/from_iter_instead_of_collect.rs | 11 +-- .../src/methods/manual_saturating_arithmetic.rs | 6 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/uninit_assumed_init.rs | 5 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 2 - clippy_lints/src/misc.rs | 18 ++--- clippy_lints/src/ptr.rs | 17 ++--- clippy_lints/src/question_mark.rs | 11 ++- clippy_lints/src/returns.rs | 7 +- clippy_lints/src/slow_vector_initialization.rs | 69 ++++++++--------- clippy_lints/src/transmuting_null.rs | 23 +++--- clippy_lints/src/types/borrowed_box.rs | 9 ++- clippy_lints/src/unnecessary_wraps.rs | 10 +-- clippy_lints/src/utils/internal_lints.rs | 14 ++-- clippy_utils/src/lib.rs | 89 ++++++++++++++-------- clippy_utils/src/paths.rs | 12 +-- doc/adding_lints.md | 2 +- .../ui-internal/collapsible_span_lint_calls.fixed | 48 +----------- tests/ui-internal/collapsible_span_lint_calls.rs | 48 +----------- .../ui-internal/collapsible_span_lint_calls.stderr | 10 +-- tests/ui-internal/match_type_on_diag_item.rs | 21 ++--- tests/ui-internal/match_type_on_diag_item.stderr | 30 +++----- tests/ui/repl_uninit.rs | 4 +- tests/ui/uninit.rs | 5 +- tests/ui/uninit.stderr | 8 +- 34 files changed, 269 insertions(+), 335 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a0993bb6913..c565e29d078 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -127,10 +127,9 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) _ => &block.expr, }; // function call - if let Some(args) = match_panic_call(cx, begin_panic_call); - if args.len() == 1; + if let Some(arg) = match_panic_call(cx, begin_panic_call); // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, args[0].span); + if let panic_message = snippet_opt(cx, arg.span); // second argument of begin_panic is irrelevant // as is the second match arm then { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 249ee27330b..4e2dbf005d5 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{in_macro, match_path_ast}; +use clippy_utils::in_macro; use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -126,7 +126,9 @@ impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_B fn is_bool_ty(ty: &Ty) -> bool { if let TyKind::Path(None, path) = &ty.kind { - return match_path_ast(path, &["bool"]); + if let [name] = path.segments.as_slice() { + return name.ident.name == sym::bool; + } } false } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 77a38544edc..03fe0d16d48 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -22,7 +22,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, match_path}; +use clippy_utils::{differing_macro_contexts, match_def_path}; declare_clippy_lint! { /// **What it does:** Checks for public `impl` or `fn` missing generalization @@ -333,12 +333,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't if let ExprKind::Call(fun, args) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; + if let Some(ty_did) = ty_path.res.opt_def_id(); then { if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { return; } - if match_path(ty_path, &paths::HASHMAP) { + if match_def_path(self.cx, ty_did, &paths::HASHMAP) { if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashMap::default()".to_string()); @@ -351,7 +352,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't ), ); } - } else if match_path(ty_path, &paths::HASHSET) { + } else if match_def_path(self.cx, ty_did, &paths::HASHSET) { if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashSet::default()".to_string()); diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index cba3183e869..4069a685ea0 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{in_macro, match_qpath, SpanlessEq}; +use clippy_utils::{in_macro, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind}; +use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -87,7 +87,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Get the variable name let var_name = ares_path.segments[0].ident.name.as_str(); - const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"]; + const INT_TYPES: [LangItem; 5] = [ + LangItem::I8, + LangItem::I16, + LangItem::I32, + LangItem::I64, + LangItem::Isize + ]; match cond_num_val.kind { ExprKind::Lit(ref cond_lit) => { @@ -99,17 +105,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { }; } }, - ExprKind::Path(ref cond_num_path) => { - if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) { - print_lint_and_sugg(cx, &var_name, expr); - }; + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if_chain! { + if name.ident.as_str() == "MIN"; + if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(const_id); + let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); + if int_ids.any(|int_id| int_id == impl_id); + then { + print_lint_and_sugg(cx, &var_name, expr) + } + } }, - ExprKind::Call(func, _) => { - if let ExprKind::Path(ref cond_num_path) = func.kind { - if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { - print_lint_and_sugg(cx, &var_name, expr); + ExprKind::Call(func, []) => { + if_chain! { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind; + if name.ident.as_str() == "min_value"; + if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(func_id); + let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); + if int_ids.any(|int_id| int_id == impl_id); + then { + print_lint_and_sugg(cx, &var_name, expr) } - }; + } }, _ => (), } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index bbb4ddc613a..afee20ce43e 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{get_trait_def_id, higher, match_qpath, paths}; +use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -163,7 +163,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { - match_qpath(qpath, &paths::REPEAT).into() + is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into() } else { Finite } diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index e7719e7663d..41cda23510e 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; +use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; @@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a 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(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), + ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY), _ => false, } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 30091e0e2d7..8f1112cff7b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1701,7 +1701,7 @@ mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; - use clippy_utils::{is_lang_ctor, is_trait_method, match_qpath, paths}; + use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -1735,8 +1735,8 @@ mod redundant_pattern_match { kind = &inner.kind; } let good_method = match kind { - PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + PatKind::TupleStruct(ref path, [sub_pat], _) => { + if let PatKind::Wild = sub_pat.kind { if is_lang_ctor(cx, path, ResultOk) { "is_ok()" } else if is_lang_ctor(cx, path, ResultErr) { @@ -1745,9 +1745,9 @@ mod redundant_pattern_match { "is_some()" } else if is_lang_ctor(cx, path, PollReady) { "is_ready()" - } else if match_qpath(path, &paths::IPADDR_V4) { + } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) { "is_ipv4()" - } else if match_qpath(path, &paths::IPADDR_V6) { + } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) { "is_ipv6()" } else { return; @@ -1821,6 +1821,7 @@ mod redundant_pattern_match { ) 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( + cx, arms, path_left, path_right, @@ -1831,6 +1832,7 @@ mod redundant_pattern_match { ) .or_else(|| { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1850,6 +1852,7 @@ mod redundant_pattern_match { { if let PatKind::Wild = patterns[0].kind { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1860,6 +1863,7 @@ mod redundant_pattern_match { ) .or_else(|| { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1900,7 +1904,9 @@ mod redundant_pattern_match { } } + #[allow(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( + cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>, path_right: &QPath<'_>, @@ -1909,9 +1915,13 @@ mod redundant_pattern_match { should_be_left: &'a str, should_be_right: &'a str, ) -> Option<&'a str> { - let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left) + && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right) + { (&(*arms[0].body).kind, &(*arms[1].body).kind) - } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left) + && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right) + { (&(*arms[1].body).kind, &(*arms[0].body).kind) } else { return None; diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 3a61f4ccad7..403fe8d3546 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -33,14 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: } } - if_chain! { - if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); - } + if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) { + apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); } } } diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index dd613d0cd63..25f8434cb94 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,8 +16,6 @@ pub(super) fn check<'tcx>( flat_map_span: Span, ) { if is_trait_method(cx, expr, sym::Iterator) { - let arg_node = &flat_map_arg.kind; - let apply_lint = |message: &str| { span_lint_and_sugg( cx, @@ -31,8 +29,8 @@ pub(super) fn check<'tcx>( }; if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); + if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind; + let body = cx.tcx.hir().body(body_id); if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; @@ -45,14 +43,8 @@ pub(super) fn check<'tcx>( } } - if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } + if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) { + apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); } } } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 707c54f7a3c..28d0e8cd4ae 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,26 +1,23 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg}; +use clippy_utils::{is_expr_path_def_path, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::ExprKind; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::Path(path) = func_kind; - if match_qpath(path, &["from_iter"]); + if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD); let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + if implements_trait(cx, arg_ty, iter_id, &[]); then { // `expr` implements `FromIterator` trait let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index ecb8b72ef46..2fddea7068d 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_qpath; +use clippy_utils::is_qpath_def_path; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; @@ -94,11 +94,11 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option LateLintPass<'tcx> for Methods { match expr.kind { hir::ExprKind::Call(func, args) => { - from_iter_instead_of_collect::check(cx, expr, args, &func.kind); + from_iter_instead_of_collect::check(cx, expr, args, func); }, hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 0ae65c0c01d..1a5894e48d1 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_def_path, match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, match_def_path, paths}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,8 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if_chain! { if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); - if let hir::ExprKind::Path(ref path) = callee.kind; - if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT); if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 66255b77331..b61c4ffe9b3 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -61,8 +61,6 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } return (true, false); } - // We don't know. It might do anything. - return (true, true); } (true, true) }, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index d156c1d5bf4..0b0cd9be46c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -20,8 +20,8 @@ use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, - last_path_segment, match_qpath, unsext, SpanlessEq, + expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, + iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -564,13 +564,13 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } ) }, - ExprKind::Call(path, 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.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, "..")) - } else { - return; - } + ExprKind::Call(path, [arg]) => { + if expr_path_res(cx, path) + .opt_def_id() + .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) + .is_some() + { + (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, "..")) } else { return; } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2ab1e958ec8..b0674f90678 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; -use clippy_utils::{is_allowed, match_def_path, paths}; +use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -417,14 +417,11 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(path, []) = expr.kind; - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(fn_def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - then { - match_def_path(cx, fn_def_id, &paths::PTR_NULL) || match_def_path(cx, fn_def_id, &paths::PTR_NULL_MUT) - } else { - false - } + if let ExprKind::Call(pathexp, []) = expr.kind { + expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| { + match_any_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some() + }) + } else { + false } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ea9ff37e13f..30bee213900 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,10 +3,10 @@ use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, match_qpath}; +use clippy_utils::{eq_expr_value, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::OptionNone; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -101,15 +101,14 @@ impl QuestionMark { if Self::is_option(cx, subject); if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind; - if match_qpath(path1, &["Some"]); - if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind; + if is_lang_ctor(cx, path1, OptionSome); + if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); if let ExprKind::Block(block, None) = &arms[0].body.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; - if let ExprKind::Path(path) = &trailing_expr.kind; - if match_qpath(path, &[&bind.as_str()]); + if path_to_local_id(trailing_expr, bind_id); if let PatKind::Wild = arms[1].pat.kind; if Self::expression_returns_none(cx, arms[1].body); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3a6151dce71..b565c77aaec 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use clippy_utils::{fn_def_id, in_macro, match_qpath}; +use clippy_utils::{fn_def_id, in_macro, path_to_local_id}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; @@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Return { if local.ty.is_none(); if cx.tcx.hir().attrs(local.hir_id).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 let PatKind::Binding(_, local_id, _, _) = local.pat.kind; + if path_to_local_id(retexpr, local_id); if !last_statement_borrows(cx, initexpr); if !in_external_macro(cx.sess(), initexpr.span); if !in_external_macro(cx.sess(), retexpr.span); diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 8cf89ae456e..191781be000 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -9,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** Checks slow zero-filled vector initialization @@ -46,8 +47,8 @@ declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]); /// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or /// `vec = Vec::with_capacity(0)` struct VecAllocation<'tcx> { - /// Symbol of the local variable name - variable_name: Symbol, + /// HirId of the variable + local_id: HirId, /// Reference to the expression which allocates the vector allocation_expr: &'tcx Expr<'tcx>, @@ -72,16 +73,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { if_chain! { if let ExprKind::Assign(left, right, _) = expr.kind; - // Extract variable name - if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; - if let Some(variable_name) = path.segments.get(0); + // Extract variable + if let Some(local_id) = path_to_local(left); // Extract len argument - if let Some(len_arg) = Self::is_vec_with_capacity(right); + if let Some(len_arg) = Self::is_vec_with_capacity(cx, right); then { let vi = VecAllocation { - variable_name: variable_name.ident.name, + local_id, allocation_expr: right, len_expr: len_arg, }; @@ -95,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; + if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind; if let Some(init) = local.init; - if let Some(len_arg) = Self::is_vec_with_capacity(init); + if let Some(len_arg) = Self::is_vec_with_capacity(cx, init); then { let vi = VecAllocation { - variable_name: variable_name.name, + local_id, allocation_expr: init, len_expr: len_arg, }; @@ -115,19 +115,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { impl SlowVectorInit { /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression /// of the first argument of `with_capacity` call if it matches or `None` if it does not. - fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["Vec", "with_capacity"]); - if args.len() == 1; - + if let ExprKind::Call(func, [arg]) = expr.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind; + if name.ident.as_str() == "with_capacity"; + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type); then { - return Some(&args[0]); + Some(arg) + } else { + None } } - - None } /// Search initialization for the given vector @@ -208,11 +207,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; - if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind; + if path_to_local_id(self_arg, self.vec_alloc.local_id); if path.ident.name == sym!(extend); - if let Some(extend_arg) = args.get(1); if self.is_repeat_take(extend_arg); then { @@ -225,11 +222,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; - if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind; + if path_to_local_id(self_arg, self.vec_alloc.local_id); if path.ident.name == sym!(resize); - if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); // Check that is filled with 0 if let ExprKind::Lit(ref lit) = fill_arg.kind; @@ -252,7 +247,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { // Check that take is applied to `repeat(0)` if let Some(repeat_expr) = take_args.get(0); - if Self::is_repeat_zero(repeat_expr); + if self.is_repeat_zero(repeat_expr); // Check that len expression is equals to `with_capacity` expression if let Some(len_arg) = take_args.get(1); @@ -267,21 +262,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { } /// Returns `true` if given expression is `repeat(0)` - fn is_repeat_zero(expr: &Expr<'_>) -> bool { + fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::Call(fn_expr, repeat_args) = expr.kind; - if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; - if match_qpath(qpath_repeat, &["repeat"]); - if let Some(repeat_arg) = repeat_args.get(0); + if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; + if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT); if let ExprKind::Lit(ref lit) = repeat_arg.kind; if let LitKind::Int(0, _) = lit.node; then { - return true + true + } else { + false } } - - false } } diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 755132da591..888ecab1046 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::{is_expr_path_def_path, paths}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -37,18 +37,15 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { } if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if args.len() == 1; - if let ExprKind::Path(ref path) = func.kind; - if let Some(func_def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if match_def_path(cx, func_def_id, &paths::TRANSMUTE); - then { + if let ExprKind::Call(func, [arg]) = expr.kind; + if is_expr_path_def_path(cx, func, &paths::TRANSMUTE); + then { // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if_chain! { - if let ExprKind::Path(ref _qpath) = args[0].kind; - let x = const_eval_context.expr(&args[0]); + if let ExprKind::Path(ref _qpath) = arg.kind; + let x = const_eval_context.expr(arg); if let Some(Constant::RawPtr(0)) = x; then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) @@ -58,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(0 as *const i32)` if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind; + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; if let ExprKind::Lit(ref lit) = inner_expr.kind; if let LitKind::Int(0, _) = lit.node; then { @@ -69,10 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::())` if_chain! { - if let ExprKind::Call(func1, []) = args[0].kind; - if let ExprKind::Path(ref path1) = func1.kind; - if let Some(func1_def_id) = cx.qpath_res(path1, func1.hir_id).opt_def_id(); - if match_def_path(cx, func1_def_id, &paths::PTR_NULL); + if let ExprKind::Call(func1, []) = arg.kind; + if is_expr_path_def_path(cx, func1, &paths::PTR_NULL); then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 1425d8f3f37..bdeff035e5e 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{match_path, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m _ => None, }); then { - if is_any_trait(inner) { + if is_any_trait(cx, inner) { // Ignore `Box` types; see issue #1884 for details. return false; } @@ -84,13 +84,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m } // Returns true if given type is `Any` trait. -fn is_any_trait(t: &hir::Ty<'_>) -> bool { +fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { if_chain! { if let TyKind::TraitObject(traits, ..) = t.kind; if !traits.is_empty(); + if let Some(trait_did) = traits[0].trait_ref.trait_def_id(); // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT); + if match_def_path(cx, trait_did, &paths::ANY_TRAIT); then { return true; } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index d2c0f60d296..f2f1410aed7 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -104,14 +104,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if_chain! { if !in_macro(ret_expr.span); // Check if a function call. - if let ExprKind::Call(func, args) = ret_expr.kind; - // Get the Path of the function call. - if let ExprKind::Path(ref qpath) = func.kind; + if let ExprKind::Call(func, [arg]) = ret_expr.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. + if let ExprKind::Path(qpath) = &func.kind; if is_lang_ctor(cx, qpath, lang_item); - if args.len() == 1; // Make sure the function argument does not contain a return expression. - if !contains_return(&args[0]); + if !contains_return(arg); then { suggs.push( ( @@ -119,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if inner_type.is_unit() { "".to_string() } else { - snippet(cx, args[0].span.source_callsite(), "..").to_string() + snippet(cx, arg.span.source_callsite(), "..").to_string() } ) ); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index cf8039d6059..3d3d0e19d26 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -3,7 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ - is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq, + is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints, + SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; @@ -578,8 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if_chain! { if let ExprKind::Call(func, and_then_args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["span_lint_and_then"]); + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); if and_then_args.len() == 5; if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; let body = cx.tcx.hir().body(*body_id); @@ -761,8 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { 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"]); + if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "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(); @@ -771,6 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { 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 { + // TODO: check paths constants from external crates. let cx_snippet = snippet(cx, context.span, "_"); let ty_snippet = snippet(cx, ty.span, "_"); @@ -778,9 +778,9 @@ 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 `clippy_utils::ty::match_type()` on a type diagnostic item", "try", - format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), + format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, ); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e1a2105b96..4a523520a43 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -397,6 +397,29 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { } } +/// If the expression is a path, resolve it. Otherwise, return `Res::Err`. +pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res { + if let ExprKind::Path(p) = &expr.kind { + cx.qpath_res(p, expr.hir_id) + } else { + Res::Err + } +} + +/// Resolves the path to a `DefId` and checks if it matches the given path. +pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool { + cx.qpath_res(path, hir_id) + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, segments)) +} + +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { + expr_path_res(cx, expr) + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, segments)) +} + /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from /// `QPath::Resolved.1.res.opt_def_id()`. @@ -425,20 +448,6 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { .all(|(a, b)| a.ident.name.as_str() == *b) } -/// Matches a `Path` against a slice of segment string literals, e.g. -/// -/// # Examples -/// ```rust,ignore -/// match_path_ast(path, &["std", "rt", "begin_unwind"]) -/// ``` -pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { @@ -1148,29 +1157,47 @@ pub fn match_function_call<'tcx>( None } +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if +/// any. +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option { + let search_path = cx.get_def_path(did); + paths + .iter() + .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().cloned())) +} + +/// Checks if the given `DefId` matches the path. pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { - // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path` - // accepts only that. We should probably move to Symbols in Clippy as well. - let syms = syms.iter().map(|p| Symbol::intern(p)).collect::>(); - cx.match_def_path(did, &syms) + // We should probably move to Symbols in Clippy as well rather than interning every time. + let path = cx.get_def_path(did); + syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().cloned()) } -pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> { - match_function_call(cx, expr, &paths::BEGIN_PANIC) - .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT)) - .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR)) +pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(func, [arg]) = expr.kind { + expr_path_res(cx, func) + .opt_def_id() + .map_or(false, |id| match_panic_def_id(cx, id)) + .then(|| arg) + } else { + None + } } pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { - match_def_path(cx, did, &paths::BEGIN_PANIC) - || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT) - || match_def_path(cx, did, &paths::PANIC_ANY) - || match_def_path(cx, did, &paths::PANICKING_PANIC) - || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT) - || match_def_path(cx, did, &paths::PANICKING_PANIC_STR) + match_any_def_paths( + cx, + did, + &[ + &paths::BEGIN_PANIC, + &paths::BEGIN_PANIC_FMT, + &paths::PANIC_ANY, + &paths::PANICKING_PANIC, + &paths::PANICKING_PANIC_FMT, + &paths::PANICKING_PANIC_STR, + ], + ) + .is_some() } /// Returns the list of condition expressions and the list of blocks in a diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ed8915f59e1..21011dbded6 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,7 +4,7 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; +pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; @@ -42,6 +42,8 @@ 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_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; @@ -58,8 +60,9 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; -pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; -pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] @@ -126,7 +129,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"]; pub const RESULT: [&str; 3] = ["core", "result", "Result"]; pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; @@ -140,7 +142,7 @@ pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; -pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; 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"]; diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 99b86953d51..50f0d724016 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -625,7 +625,7 @@ in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here (`implements_trait`, `match_path`, `snippet`, etc) + is already in here (`implements_trait`, `match_def_path`, `snippet`, etc) * [Clippy diagnostics][diagnostics] * [The `if_chain` macro][if_chain] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed index e588c23345e..7764cc8da78 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -2,58 +2,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_ast; extern crate rustc_errors; extern crate rustc_lint; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -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>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index d5dd3bb562b..bdd296db832 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -2,58 +2,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_ast; extern crate rustc_errors; extern crate rustc_lint; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -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>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 874d4a9f255..0632b038577 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -1,5 +1,5 @@ error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:75:9 + --> $DIR/collapsible_span_lint_calls.rs:35:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); @@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:78:9 + --> $DIR/collapsible_span_lint_calls.rs:38:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_help(expr.span, help_msg); @@ -22,7 +22,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:81:9 + --> $DIR/collapsible_span_lint_calls.rs:41:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.help(help_msg); @@ -30,7 +30,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:84:9 + --> $DIR/collapsible_span_lint_calls.rs:44:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_note(expr.span, note_msg); @@ -38,7 +38,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:87:9 + --> $DIR/collapsible_span_lint_calls.rs:47:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.note(note_msg); diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs index fe950b0aa7c..063f0c6460c 100644 --- a/tests/ui-internal/match_type_on_diag_item.rs +++ b/tests/ui-internal/match_type_on_diag_item.rs @@ -1,29 +1,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_hir; extern crate rustc_lint; extern crate rustc_middle; + #[macro_use] extern crate rustc_session; +use clippy_utils::{paths, ty::match_type}; 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, @@ -38,12 +27,12 @@ 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, &paths::VEC); // FIXME: Doesn't lint external paths 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); + let _ = clippy_utils::ty::match_type(cx, ty, rc_path); } } diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr index 82465dbaf6e..71472960565 100644 --- a/tests/ui-internal/match_type_on_diag_item.stderr +++ b/tests/ui-internal/match_type_on_diag_item.stderr @@ -1,8 +1,8 @@ -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:41:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:31:17 | -LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)` | note: the lint level is defined here --> $DIR/match_type_on_diag_item.rs:1:9 @@ -11,23 +11,17 @@ 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 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:32:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::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 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:35:17 | -LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` +LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/repl_uninit.rs b/tests/ui/repl_uninit.rs index ad5b8e4857d..6c7e2b854dc 100644 --- a/tests/ui/repl_uninit.rs +++ b/tests/ui/repl_uninit.rs @@ -1,5 +1,5 @@ -#![allow(deprecated, invalid_value)] -#![warn(clippy::all)] +#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)] +#![warn(clippy::mem_replace_with_uninit)] use std::mem; diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index f42b884e0f0..1ed3883c1f0 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -1,6 +1,6 @@ #![feature(stmt_expr_attributes)] -use std::mem::MaybeUninit; +use std::mem::{self, MaybeUninit}; fn main() { let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; @@ -19,4 +19,7 @@ fn main() { // This is OK, because all constitutent types are uninit-compatible. let _: (MaybeUninit, [MaybeUninit; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; + + // Was a false negative. + let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; } diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index a37233ecdda..85b64a8419a 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -12,5 +12,11 @@ error: this call for this type may be undefined behavior LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:24:29 + | +LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From b6581636bd9cfb16337a9ffcecd1b38e68a6abce Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 3 Jan 2021 14:01:01 -0500 Subject: Improve `redundant_pattern_matching` Add a note when the drop order change may result in different behaviour. --- clippy_lints/src/matches.rs | 229 ++++++++++++++++++--- .../ui/redundant_pattern_matching_drop_order.fixed | 58 ++++++ tests/ui/redundant_pattern_matching_drop_order.rs | 58 ++++++ .../redundant_pattern_matching_drop_order.stderr | 171 +++++++++++++++ tests/ui/redundant_pattern_matching_option.fixed | 7 +- tests/ui/redundant_pattern_matching_option.rs | 7 +- tests/ui/redundant_pattern_matching_option.stderr | 38 ++-- tests/ui/redundant_pattern_matching_poll.fixed | 7 +- tests/ui/redundant_pattern_matching_poll.rs | 7 +- tests/ui/redundant_pattern_matching_poll.stderr | 36 ++-- tests/ui/redundant_pattern_matching_result.fixed | 3 +- tests/ui/redundant_pattern_matching_result.rs | 3 +- tests/ui/redundant_pattern_matching_result.stderr | 44 ++-- 13 files changed, 579 insertions(+), 89 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_drop_order.fixed create mode 100644 tests/ui/redundant_pattern_matching_drop_order.rs create mode 100644 tests/ui/redundant_pattern_matching_drop_order.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f1112cff7b..dc5232085ed 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -423,7 +423,12 @@ declare_clippy_lint! { /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function /// - /// **Known problems:** None. + /// **Known problems:** This will change the drop order for the matched type. Both `if let` and + /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the + /// value before entering the block. For most types this change will not matter, but for a few + /// types this will not be an acceptable change (e.g. locks). See the + /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about + /// drop order. /// /// **Example:** /// @@ -1700,55 +1705,204 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; - use clippy_utils::source::snippet; + use clippy_utils::source::{snippet, snippet_with_applicability}; + use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; - use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; + use rustc_hir::{ + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath, + }; use rustc_lint::LateContext; + use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_span::sym; pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { 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"), + MatchSource::IfLetDesugar { contains_else_clause } => { + find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause) + }, + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false), _ => {}, } } } + // Check if the drop order for a type matters + fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if !ty.needs_drop(cx.tcx, cx.param_env) { + false + } else if cx + .tcx + .lang_items() + .drop_trait() + .map_or(false, |id| !implements_trait(cx, ty, id, &[])) + { + // This type doesn't implement drop, so no side effects here. + // Check if any component type has any. + match ty.kind() { + ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)), + ty::Array(ty, _) => type_needs_ordered_drop(cx, ty), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| type_needs_ordered_drop(cx, ty)), + _ => true, + } + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_diagnostic_item(cx, ty, sym::vec_type) + || is_type_lang_item(cx, ty, LangItem::OwnedBox) + || is_type_diagnostic_item(cx, ty, sym::Rc) + || is_type_diagnostic_item(cx, ty, sym::Arc) + || is_type_diagnostic_item(cx, ty, sym::cstring_type) + || match_type(cx, ty, &paths::BTREEMAP) + || match_type(cx, ty, &paths::LINKED_LIST) + || match_type(cx, ty, &paths::WEAK_RC) + || match_type(cx, ty, &paths::WEAK_ARC) + { + // Check all of the generic arguments. + if let ty::Adt(_, subs) = ty.kind() { + subs.types().any(|ty| type_needs_ordered_drop(cx, ty)) + } else { + true + } + } else { + true + } + } + + // Extract the generic arguments out of a type + fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { + if_chain! { + if let ty::Adt(_, subs) = ty.kind(); + if let Some(sub) = subs.get(index); + if let GenericArgKind::Type(sub_ty) = sub.unpack(); + then { + Some(sub_ty) + } else { + None + } + } + } + + // Checks if there are any temporaries created in the given expression for which drop order + // matters. + fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + struct V<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + res: bool, + } + impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + // Taking the reference of a value leaves a temporary + // e.g. In `&String::new()` the string is a temporary value. + // Remaining fields are temporary values + // e.g. In `(String::new(), 0).1` the string is a temporary value. + ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => { + if !matches!(expr.kind, ExprKind::Path(_)) { + if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { + self.res = true; + } else { + self.visit_expr(expr); + } + } + }, + // the base type is alway taken by reference. + // e.g. In `(vec![0])[0]` the vector is a temporary value. + ExprKind::Index(base, index) => { + if !matches!(base.kind, ExprKind::Path(_)) { + if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { + self.res = true; + } else { + self.visit_expr(base); + } + } + self.visit_expr(index); + }, + // Method calls can take self by reference. + // e.g. In `String::new().len()` the string is a temporary value. + ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => { + if !matches!(self_arg.kind, ExprKind::Path(_)) { + let self_by_ref = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref()); + if self_by_ref + && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) + { + self.res = true; + } else { + self.visit_expr(self_arg) + } + } + args.iter().for_each(|arg| self.visit_expr(arg)); + }, + // Either explicitly drops values, or changes control flow. + ExprKind::DropTemps(_) + | ExprKind::Ret(_) + | ExprKind::Break(..) + | ExprKind::Yield(..) + | ExprKind::Block(Block { expr: None, .. }, _) + | ExprKind::Loop(..) => (), + + // Only consider the final expression. + ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr), + + _ => walk_expr(self, expr), + } + } + } + + let mut v = V { cx, res: false }; + v.visit_expr(expr); + v.res + } + fn find_sugg_for_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], + op: &'tcx Expr<'tcx>, + arm: &Arm<'_>, keyword: &'static str, + has_else: bool, ) { // also look inside refs - let mut kind = &arms[0].pat.kind; + let mut kind = &arm.pat.kind; // if we have &None for example, peel it so we can detect "if let None = x" if let PatKind::Ref(inner, _mutability) = kind { kind = &inner.kind; } - let good_method = match kind { + let op_ty = cx.typeck_results().expr_ty(op); + // Determine which function should be used, and the type contained by the corresponding + // variant. + let (good_method, inner_ty) = match kind { PatKind::TupleStruct(ref path, [sub_pat], _) => { if let PatKind::Wild = sub_pat.kind { if is_lang_ctor(cx, path, ResultOk) { - "is_ok()" + ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)) } else if is_lang_ctor(cx, path, ResultErr) { - "is_err()" + ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty)) } else if is_lang_ctor(cx, path, OptionSome) { - "is_some()" + ("is_some()", op_ty) } else if is_lang_ctor(cx, path, PollReady) { - "is_ready()" + ("is_ready()", op_ty) } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) { - "is_ipv4()" + ("is_ipv4()", op_ty) } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) { - "is_ipv6()" + ("is_ipv6()", op_ty) } else { return; } @@ -1757,17 +1911,36 @@ mod redundant_pattern_match { } }, PatKind::Path(ref path) => { - if is_lang_ctor(cx, path, OptionNone) { + let method = if is_lang_ctor(cx, path, OptionNone) { "is_none()" } else if is_lang_ctor(cx, path, PollPending) { "is_pending()" } else { return; - } + }; + // `None` and `Pending` don't have an inner type. + (method, cx.tcx.types.unit) }, _ => return, }; + // If this is the last expression in a block or there is an else clause then the whole + // type needs to be considered, not just the inner type of the branch being matched on. + // Note the last expression in a block is dropped after all local bindings. + let check_ty = if has_else + || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..))))) + { + op_ty + } else { + inner_ty + }; + + // All temporaries created in the scrutinee expression are dropped at the same time as the + // scrutinee would be, so they have to be considered as well. + // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held + // for the duration if body. + let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op); + // check that `while_let_on_iterator` lint does not trigger if_chain! { if keyword == "while"; @@ -1786,7 +1959,7 @@ mod redundant_pattern_match { span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, - arms[0].pat.span, + arm.pat.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { // while let ... = ... { ... } @@ -1800,12 +1973,20 @@ mod redundant_pattern_match { // 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 - ); + + let mut app = if needs_drop { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let sugg = snippet_with_applicability(cx, op_span, "_", &mut app); + + diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app); + + if needs_drop { + diag.note("this will change drop order of the result, as well as all temporaries"); + diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important"); + } }, ); } diff --git a/tests/ui/redundant_pattern_matching_drop_order.fixed b/tests/ui/redundant_pattern_matching_drop_order.fixed new file mode 100644 index 00000000000..794ed542435 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +// Issue #5746 +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] +use std::task::Poll::{Pending, Ready}; + +fn main() { + let m = std::sync::Mutex::new((0, 0)); + + // Result + if m.lock().is_ok() {} + if Err::<(), _>(m.lock().unwrap().0).is_err() {} + + { + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + } + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() { + } else { + } + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + if Err::, _>(()).is_err() {} + + if Ok::<_, ()>(String::new()).is_ok() {} + if Err::<(), _>((String::new(), ())).is_err() {} + + // Option + if Some(m.lock()).is_some() {} + if Some(m.lock().unwrap().0).is_some() {} + + { + if None::>.is_none() {} + } + if None::>.is_none() { + } else { + } + + if None::>.is_none() {} + + if Some(String::new()).is_some() {} + if Some((String::new(), ())).is_some() {} + + // Poll + if Ready(m.lock()).is_ready() {} + if Ready(m.lock().unwrap().0).is_ready() {} + + { + if Pending::>.is_pending() {} + } + if Pending::>.is_pending() { + } else { + } + + if Pending::>.is_pending() {} + + if Ready(String::new()).is_ready() {} + if Ready((String::new(), ())).is_ready() {} +} diff --git a/tests/ui/redundant_pattern_matching_drop_order.rs b/tests/ui/redundant_pattern_matching_drop_order.rs new file mode 100644 index 00000000000..b9c82d86f61 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.rs @@ -0,0 +1,58 @@ +// run-rustfix + +// Issue #5746 +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] +use std::task::Poll::{Pending, Ready}; + +fn main() { + let m = std::sync::Mutex::new((0, 0)); + + // Result + if let Ok(_) = m.lock() {} + if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} + + { + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + } + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { + } else { + } + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + if let Err(_) = Err::, _>(()) {} + + if let Ok(_) = Ok::<_, ()>(String::new()) {} + if let Err(_) = Err::<(), _>((String::new(), ())) {} + + // Option + if let Some(_) = Some(m.lock()) {} + if let Some(_) = Some(m.lock().unwrap().0) {} + + { + if let None = None::> {} + } + if let None = None::> { + } else { + } + + if let None = None::> {} + + if let Some(_) = Some(String::new()) {} + if let Some(_) = Some((String::new(), ())) {} + + // Poll + if let Ready(_) = Ready(m.lock()) {} + if let Ready(_) = Ready(m.lock().unwrap().0) {} + + { + if let Pending = Pending::> {} + } + if let Pending = Pending::> { + } else { + } + + if let Pending = Pending::> {} + + if let Ready(_) = Ready(String::new()) {} + if let Ready(_) = Ready((String::new(), ())) {} +} diff --git a/tests/ui/redundant_pattern_matching_drop_order.stderr b/tests/ui/redundant_pattern_matching_drop_order.stderr new file mode 100644 index 00000000000..eb7aa70ee27 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.stderr @@ -0,0 +1,171 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:12:12 + | +LL | if let Ok(_) = m.lock() {} + | -------^^^^^----------- help: try this: `if m.lock().is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:13:12 + | +LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} + | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:16:16 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:18:12 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:21:12 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:22:12 + | +LL | if let Err(_) = Err::, _>(()) {} + | -------^^^^^^------------------------------------------ help: try this: `if Err::, _>(()).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:24:12 + | +LL | if let Ok(_) = Ok::<_, ()>(String::new()) {} + | -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:25:12 + | +LL | if let Err(_) = Err::<(), _>((String::new(), ())) {} + | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:28:12 + | +LL | if let Some(_) = Some(m.lock()) {} + | -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:29:12 + | +LL | if let Some(_) = Some(m.lock().unwrap().0) {} + | -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:32:16 + | +LL | if let None = None::> {} + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:34:12 + | +LL | if let None = None::> { + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:38: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_drop_order.rs:40:12 + | +LL | if let Some(_) = Some(String::new()) {} + | -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:41:12 + | +LL | if let Some(_) = Some((String::new(), ())) {} + | -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:44:12 + | +LL | if let Ready(_) = Ready(m.lock()) {} + | -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:45:12 + | +LL | if let Ready(_) = Ready(m.lock().unwrap().0) {} + | -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:48:16 + | +LL | if let Pending = Pending::> {} + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:50:12 + | +LL | if let Pending = Pending::> { + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:54:12 + | +LL | if let Pending = Pending::> {} + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:56:12 + | +LL | if let Ready(_) = Ready(String::new()) {} + | -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:57:12 + | +LL | if let Ready(_) = Ready((String::new(), ())) {} + | -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()` + +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 index 66f580a0a68..99714477266 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index f18b27b8b95..8309847e181 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 58482a0ab70..613a30d4a48 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:8:12 + --> $DIR/redundant_pattern_matching_option.rs:13:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` @@ -7,43 +7,43 @@ LL | if let None = None::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:10:12 + --> $DIR/redundant_pattern_matching_option.rs:15: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:12:12 + --> $DIR/redundant_pattern_matching_option.rs:17: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:15 + --> $DIR/redundant_pattern_matching_option.rs:23: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:20:15 + --> $DIR/redundant_pattern_matching_option.rs:25: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:22:15 + --> $DIR/redundant_pattern_matching_option.rs:27: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:25:15 + --> $DIR/redundant_pattern_matching_option.rs:30: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:33:5 + --> $DIR/redundant_pattern_matching_option.rs:38:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:38:5 + --> $DIR/redundant_pattern_matching_option.rs:43:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:43:13 + --> $DIR/redundant_pattern_matching_option.rs:48:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,49 +71,49 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:49:20 + --> $DIR/redundant_pattern_matching_option.rs:54:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:53:20 + --> $DIR/redundant_pattern_matching_option.rs:58: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:55:19 + --> $DIR/redundant_pattern_matching_option.rs:60: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:74:12 + --> $DIR/redundant_pattern_matching_option.rs:79: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:76:12 + --> $DIR/redundant_pattern_matching_option.rs:81: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:78:15 + --> $DIR/redundant_pattern_matching_option.rs:83: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:80:15 + --> $DIR/redundant_pattern_matching_option.rs:85: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:82:5 + --> $DIR/redundant_pattern_matching_option.rs:87:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:87:5 + --> $DIR/redundant_pattern_matching_option.rs:92:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed index 465aa80dac2..c2977453804 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] use std::task::Poll::{self, Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index 7891ff353b1..665c8c41750 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] use std::task::Poll::{self, Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 5ffc6c47c90..5ecf024a733 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:10:12 + --> $DIR/redundant_pattern_matching_poll.rs:15:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` @@ -7,37 +7,37 @@ LL | if let Pending = Pending::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:12:12 + --> $DIR/redundant_pattern_matching_poll.rs:17:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:14:12 + --> $DIR/redundant_pattern_matching_poll.rs:19:12 | LL | if let Ready(_) = Ready(42) { | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:20:15 + --> $DIR/redundant_pattern_matching_poll.rs:25:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:22:15 + --> $DIR/redundant_pattern_matching_poll.rs:27:15 | LL | while let Pending = Ready(42) {} | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:24:15 + --> $DIR/redundant_pattern_matching_poll.rs:29:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:30:5 + --> $DIR/redundant_pattern_matching_poll.rs:35:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -46,7 +46,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:35:5 + --> $DIR/redundant_pattern_matching_poll.rs:40:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, @@ -55,7 +55,7 @@ LL | | }; | |_____^ help: try this: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:40:13 + --> $DIR/redundant_pattern_matching_poll.rs:45:13 | LL | let _ = match Pending::<()> { | _____________^ @@ -65,49 +65,49 @@ LL | | }; | |_____^ help: try this: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:46:20 + --> $DIR/redundant_pattern_matching_poll.rs:51:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try this: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:50:20 + --> $DIR/redundant_pattern_matching_poll.rs:55:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:52:19 + --> $DIR/redundant_pattern_matching_poll.rs:57:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:68:12 + --> $DIR/redundant_pattern_matching_poll.rs:73:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:70:12 + --> $DIR/redundant_pattern_matching_poll.rs:75:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:72:15 + --> $DIR/redundant_pattern_matching_poll.rs:77:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:74:15 + --> $DIR/redundant_pattern_matching_poll.rs:79:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:76:5 + --> $DIR/redundant_pattern_matching_poll.rs:81:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -116,7 +116,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:81:5 + --> $DIR/redundant_pattern_matching_poll.rs:86:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index e94c5704b48..d7af5d762ae 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -7,7 +7,8 @@ clippy::needless_bool, clippy::match_like_matches_macro, clippy::unnecessary_wraps, - deprecated + deprecated, + clippy::if_same_then_else )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index 5d175294232..e06d4485ae4 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -7,7 +7,8 @@ clippy::needless_bool, clippy::match_like_matches_macro, clippy::unnecessary_wraps, - deprecated + deprecated, + clippy::if_same_then_else )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d6a46babb77..e06f095da20 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:15:12 + --> $DIR/redundant_pattern_matching_result.rs:16:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:17:12 + --> $DIR/redundant_pattern_matching_result.rs:18:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:19:12 + --> $DIR/redundant_pattern_matching_result.rs:20: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_result.rs:21:15 + --> $DIR/redundant_pattern_matching_result.rs:22:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:23:15 + --> $DIR/redundant_pattern_matching_result.rs:24:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:33:5 + --> $DIR/redundant_pattern_matching_result.rs:34:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:38:5 + --> $DIR/redundant_pattern_matching_result.rs:39:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:43:5 + --> $DIR/redundant_pattern_matching_result.rs:44:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:48:5 + --> $DIR/redundant_pattern_matching_result.rs:49:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:53:20 + --> $DIR/redundant_pattern_matching_result.rs:54:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:59:20 + --> $DIR/redundant_pattern_matching_result.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:61:19 + --> $DIR/redundant_pattern_matching_result.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:84:19 + --> $DIR/redundant_pattern_matching_result.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:16 + --> $DIR/redundant_pattern_matching_result.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:91:12 + --> $DIR/redundant_pattern_matching_result.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:15 + --> $DIR/redundant_pattern_matching_result.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:110:12 + --> $DIR/redundant_pattern_matching_result.rs:111:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:112:12 + --> $DIR/redundant_pattern_matching_result.rs:113:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:114:15 + --> $DIR/redundant_pattern_matching_result.rs:115:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:116:15 + --> $DIR/redundant_pattern_matching_result.rs:117:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:118:5 + --> $DIR/redundant_pattern_matching_result.rs:119:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:123:5 + --> $DIR/redundant_pattern_matching_result.rs:124:5 | LL | / match Err::(42) { LL | | Ok(_) => false, -- cgit 1.4.1-3-g733a5 From c02baba1dcd9847d07762f42412d10eed7cff3e9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 19:04:45 -0400 Subject: `redundant_pattern_matching` fix inverted boolean when missing `Drop` trait # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Fri Apr 2 19:04:45 2021 -0400 # # On branch redundant_pattern_matching # Your branch is ahead of 'origin/redundant_pattern_matching' by 1 commit. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: clippy_lints/src/matches.rs # # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Fri Apr 2 19:04:45 2021 -0400 # # interactive rebase in progress; onto ebc64690d # Last commands done (6 commands done): # pick 25d211ad8 Code cleanup and additional std types checked # r 0c71ce56f `redundant_pattern_matching` fix inverted boolean when missing `Drop` trait # No commands remaining. # You are currently editing a commit while rebasing branch 'redundant_pattern_matching' on 'ebc64690d'. # # Changes to be committed: # modified: clippy_lints/src/matches.rs # --- clippy_lints/src/matches.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index dc5232085ed..ddfc27e8460 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1733,15 +1733,17 @@ mod redundant_pattern_match { } } - // Check if the drop order for a type matters + /// Checks if the drop order for a type matters. Some std types implement drop solely to + /// deallocate memory. For these types, and composites containing them, changing the drop order + /// won't result in any observable side effects. fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if !ty.needs_drop(cx.tcx, cx.param_env) { false - } else if cx + } else if !cx .tcx .lang_items() .drop_trait() - .map_or(false, |id| !implements_trait(cx, ty, id, &[])) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // This type doesn't implement drop, so no side effects here. // Check if any component type has any. -- cgit 1.4.1-3-g733a5 From 9a55c0c1763cc5894ce1dbf1c40d03119e3de5f3 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 22:05:37 -0400 Subject: Fix `single_match` Check for `PartialEq` in addition to `StructuralPartialEq` before suggesting `==` --- clippy_lints/src/matches.rs | 7 +++++-- tests/ui/single_match.rs | 8 ++++++++ tests/ui/single_match.stderr | 11 ++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f1112cff7b..e4d1451b369 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -738,8 +738,11 @@ fn report_single_match_single_pattern( let (msg, sugg) = if_chain! { if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); - if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); - if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); + if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait(); + if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait(); + if ty.is_integral() || ty.is_char() || ty.is_str() + || (implements_trait(cx, ty, spe_trait_id, &[]) + && implements_trait(cx, ty, pe_trait_id, &[ty.into()])); then { // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index ca884b41c45..b1819e08d53 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -135,6 +135,14 @@ fn if_suggestion() { Bar::A => println!(), _ => (), } + + // issue #7038 + struct X; + let x = Some(X); + match x { + None => println!(), + _ => (), + }; } macro_rules! single_match { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 7ea6955b740..9ef2a8668a6 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -119,5 +119,14 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` -error: aborting due to 12 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:142:5 + | +LL | / match x { +LL | | None => println!(), +LL | | _ => (), +LL | | }; + | |_____^ help: try this: `if let None = x { println!() }` + +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 61230f4cb822081ee17e8a660bc45a7b6ed0220d Mon Sep 17 00:00:00 2001 From: Yawara ISHIDA Date: Fri, 16 Apr 2021 22:50:04 +0900 Subject: Fixed incosistent_struct_constructor triggers in macro-generated code --- clippy_lints/src/inconsistent_struct_constructor.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index d7ca24487a8..7532f182360 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -66,6 +66,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl LateLintPass<'_> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { + if !expr.span.from_expansion(); if let ExprKind::Struct(qpath, fields, base) = expr.kind; let ty = cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); -- cgit 1.4.1-3-g733a5 From 0462666c7067d8992501aa8997d16e18da021ee5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Apr 2021 11:00:08 -0500 Subject: Add cloned_instead_of_copied lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ .../src/methods/cloned_instead_of_copied.rs | 38 ++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 26 +++++++++++++++ clippy_utils/src/ty.rs | 21 +++++++++++- tests/ui/cloned_instead_of_copied.fixed | 15 +++++++++ tests/ui/cloned_instead_of_copied.rs | 15 +++++++++ tests/ui/cloned_instead_of_copied.stderr | 34 +++++++++++++++++++ 8 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/cloned_instead_of_copied.rs create mode 100644 tests/ui/cloned_instead_of_copied.fixed create mode 100644 tests/ui/cloned_instead_of_copied.rs create mode 100644 tests/ui/cloned_instead_of_copied.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7885bdfb12c..483365b5e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2148,6 +2148,7 @@ Released 2018-09-13 [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 93631c5669b..0f48799d339 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -759,6 +759,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::BYTES_NTH, methods::CHARS_LAST_CMP, methods::CHARS_NEXT_CMP, + methods::CLONED_INSTEAD_OF_COPIED, methods::CLONE_DOUBLE_REF, methods::CLONE_ON_COPY, methods::CLONE_ON_REF_PTR, @@ -1380,6 +1381,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), LintId::of(matches::SINGLE_MATCH_ELSE), + LintId::of(methods::CLONED_INSTEAD_OF_COPIED), LintId::of(methods::FILTER_MAP_NEXT), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs new file mode 100644 index 00000000000..ba97ab3900c --- /dev/null +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{sym, Span}; + +use super::CLONED_INSTEAD_OF_COPIED; + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + let inner_ty = match recv_ty.kind() { + // `Option` -> `T` + ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0), + _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) { + // ::Item + Some(ty) => ty, + _ => return, + }, + _ => return, + }; + match inner_ty.kind() { + // &T where T: Copy + ty::Ref(_, ty, _) if is_copy(cx, ty) => {}, + _ => return, + }; + span_lint_and_sugg( + cx, + CLONED_INSTEAD_OF_COPIED, + span, + "used `cloned` where `copied` could be used instead", + "try", + "copied".into(), + Applicability::MachineApplicable, + ) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6c21ff0f6c2..49a9af2a465 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -8,6 +8,7 @@ mod chars_next_cmp; mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; +mod cloned_instead_of_copied; mod expect_fun_call; mod expect_used; mod filetype_is_file; @@ -73,6 +74,29 @@ use rustc_span::symbol::SymbolStr; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; +declare_clippy_lint! { + /// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where + /// `copied()` could be used instead. + /// + /// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned + /// implements `Copy`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// [1, 2, 3].iter().cloned(); + /// ``` + /// Use instead: + /// ```rust + /// [1, 2, 3].iter().copied(); + /// ``` + pub CLONED_INSTEAD_OF_COPIED, + pedantic, + "used `cloned` where `copied` could be used instead" +} + declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// @@ -1638,6 +1662,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, + CLONED_INSTEAD_OF_COPIED, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, @@ -1909,6 +1934,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span), ("collect", []) => match method_call!(recv) { Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), Some(("map", [m_recv, m_arg], _)) => { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 807cfbc4c7f..64a80f2554f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; use rustc_span::sym; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -52,6 +52,25 @@ pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool { }) } +/// Resolves `::Item` for `T` +/// Do not invoke without first verifying that the type implements `Iterator` +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + cx.tcx + .get_diagnostic_item(sym::Iterator) + .and_then(|iter_did| { + cx.tcx.associated_items(iter_did).find_by_name_and_kind( + cx.tcx, + Ident::from_str("Item"), + ty::AssocKind::Type, + iter_did, + ) + }) + .map(|assoc| { + let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); + cx.tcx.normalize_erasing_regions(cx.param_env, proj) + }) +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed new file mode 100644 index 00000000000..4eb999e18e6 --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::cloned_instead_of_copied)] + +fn main() { + // yay + let _ = [1].iter().copied(); + let _ = vec!["hi"].iter().copied(); + let _ = Some(&1).copied(); + let _ = Box::new([1].iter()).copied(); + let _ = Box::new(Some(&1)).copied(); + + // nay + let _ = [String::new()].iter().cloned(); + let _ = Some(&String::new()).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs new file mode 100644 index 00000000000..894496c0ebb --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.rs @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::cloned_instead_of_copied)] + +fn main() { + // yay + let _ = [1].iter().cloned(); + let _ = vec!["hi"].iter().cloned(); + let _ = Some(&1).cloned(); + let _ = Box::new([1].iter()).cloned(); + let _ = Box::new(Some(&1)).cloned(); + + // nay + let _ = [String::new()].iter().cloned(); + let _ = Some(&String::new()).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr new file mode 100644 index 00000000000..e0707d32146 --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -0,0 +1,34 @@ +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:6:24 + | +LL | let _ = [1].iter().cloned(); + | ^^^^^^ help: try: `copied` + | + = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:7:31 + | +LL | let _ = vec!["hi"].iter().cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:8:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:9:34 + | +LL | let _ = Box::new([1].iter()).cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:10:32 + | +LL | let _ = Box::new(Some(&1)).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From b049c88fbe661f2ef1f1f7806788fed59dc1bfa8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Apr 2021 11:07:08 -0500 Subject: Eat dogfood --- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/checked_conversions.rs | 4 ++-- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/matches.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/write.rs | 2 +- clippy_utils/src/hir_utils.rs | 2 +- clippy_utils/src/lib.rs | 6 +++--- 9 files changed, 12 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 58d9aa9c005..67f0e0c7870 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -261,7 +261,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } METHODS_WITH_NEGATION .iter() - .cloned() + .copied() .flat_map(|(a, b)| vec![(a, b), (b, a)]) .find(|&(a, _)| { let path: &str = &path.ident.name.as_str(); diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d7136f84cc3..6a2666bc6c0 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -323,7 +323,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: if let [int] = &*tp.segments; then { let name = &int.ident.name.as_str(); - candidates.iter().find(|c| name == *c).cloned() + candidates.iter().find(|c| name == *c).copied() } else { None } @@ -337,7 +337,7 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if let [ty] = &*path.segments; then { let name = &ty.ident.name.as_str(); - INTS.iter().find(|c| name == *c).cloned() + INTS.iter().find(|c| name == *c).copied() } else { None } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 96720764e16..e97b7c94170 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -100,7 +100,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. absorb_break(&never_loop_block(b, main_loop_id)) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e4d1451b369..f4102709d7e 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1124,7 +1124,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) Applicability::MaybeIncorrect, ), variants => { - let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); + let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); let message = if adt_def.is_variant_list_non_exhaustive() { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 780e2241293..e33a33e2386 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -279,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { spans.extend( deref_span .iter() - .cloned() + .copied() .map(|span| (span, format!("*{}", snippet(cx, span, "")))), ); spans.sort_by_key(|&(span, _)| span); diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index cb2237e5312..4272935bc31 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -195,7 +195,7 @@ fn attempt_to_emit_no_difference_lint( i: usize, expected_loc: IdentLocation, ) { - if let Some(binop) = binops.get(i).cloned() { + if let Some(binop) = binops.get(i).copied() { // We need to try and figure out which identifier we should // suggest using instead. Since there could be multiple // replacement candidates in a given expression, and we're diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 12a47a6b703..7e962472c07 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -573,7 +573,7 @@ impl Write { diag.multipart_suggestion( "try this", iter::once((comma_span.to(token_expr.span), String::new())) - .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement))) + .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement))) .collect(), Applicability::MachineApplicable, ); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f695f1a61e7..3ad1ac75e56 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -424,7 +424,7 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &' TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace ) }) - .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) => + .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) => { kind }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6c8a3302aa4..a5986ed00da 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1053,7 +1053,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// the function once on the given pattern. pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) { if let PatKind::Or(pats) = pat.kind { - pats.iter().cloned().for_each(f) + pats.iter().copied().for_each(f) } else { f(pat) } @@ -1230,14 +1230,14 @@ pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) let search_path = cx.get_def_path(did); paths .iter() - .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().cloned())) + .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied())) } /// Checks if the given `DefId` matches the path. pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { // We should probably move to Symbols in Clippy as well rather than interning every time. let path = cx.get_def_path(did); - syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().cloned()) + syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) } pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { -- cgit 1.4.1-3-g733a5 From 2d050f4b2f6057ea0ac63a3d8d37c05992e4ee3e Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Fri, 16 Apr 2021 19:38:01 +0200 Subject: add type in help of from over Into --- clippy_lints/src/from_over_into.rs | 2 +- tests/ui/from_over_into.stderr | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e5ec245e502..5e2baba8943 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -73,7 +73,7 @@ impl LateLintPass<'_> for FromOverInto { cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, - "consider to implement `From` instead", + &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), ); } } diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index b101d2704fb..fb391759dce 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -5,7 +5,6 @@ LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider to implement `From` instead + = help: consider to implement `From` instead error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From 559deddb3b5e376b79a019491bbdbf75eba2826d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 11:49:45 -0400 Subject: Allow allman style braces in `suspicious_else_formatting` --- clippy_lints/src/formatting.rs | 17 +++++++++++-- tests/ui/suspicious_else_formatting.rs | 26 ++++++++++++++++++++ tests/ui/suspicious_else_formatting.stderr | 39 ++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 48612befc68..3bd6a09d365 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -215,9 +215,22 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { // the snippet should look like " else \n " with maybe comments anywhere // it’s bad when there is a ‘\n’ after the “else” if let Some(else_snippet) = snippet_opt(cx, else_span); - if let Some(else_pos) = else_snippet.find("else"); - if else_snippet[else_pos..].contains('\n'); + if let Some((pre_else, post_else)) = else_snippet.split_once("else"); + if let Some((_, post_else_post_eol)) = post_else.split_once('\n'); + then { + // Allow allman style braces `} \n else \n {` + if_chain! { + if is_block(else_); + if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n'); + // Exactly one eol before and after the else + if !pre_else_post_eol.contains('\n'); + if !post_else_post_eol.contains('\n'); + then { + return; + } + } + let else_desc = if is_if(else_) { "if" } else { "{..}" }; span_lint_and_note( cx, diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index 226010ec6df..547615b10d9 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -40,6 +40,7 @@ fn main() { { } + // This is fine, though weird. Allman style braces on the else. if foo() { } else @@ -76,4 +77,29 @@ fn main() { } if foo() { } + + // Almost Allman style braces. Lint these. + if foo() { + } + + else + { + + } + + if foo() { + } + else + + { + + } + + // #3864 - Allman style braces + if foo() + { + } + else + { + } } diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr index bbc036d376f..d8d67b4138a 100644 --- a/tests/ui/suspicious_else_formatting.stderr +++ b/tests/ui/suspicious_else_formatting.stderr @@ -41,37 +41,50 @@ LL | | { | = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` -error: this is an `else {..}` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:44:6 +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:51:6 | -LL | } +LL | } else | ______^ -LL | | else -LL | | { +LL | | if foo() { // the span of the above error should continue here | |____^ | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` error: this is an `else if` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:50:6 + --> $DIR/suspicious_else_formatting.rs:56:6 | -LL | } else +LL | } | ______^ +LL | | else LL | | if foo() { // the span of the above error should continue here | |____^ | = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` -error: this is an `else if` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:55:6 +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:83:6 | LL | } | ______^ +LL | | LL | | else -LL | | if foo() { // the span of the above error should continue here +LL | | { | |____^ | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:91:6 + | +LL | } + | ______^ +LL | | else +LL | | +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 5af078ac1b705abde41cc2e7c71be38701943cdb Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Apr 2021 15:06:21 -0500 Subject: Add flat_map_option lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/matches.rs | 2 +- clippy_lints/src/methods/flat_map_option.rs | 34 +++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 30 ++++++++++++++++++++++++- clippy_utils/src/attrs.rs | 2 +- tests/ui/flat_map_option.fixed | 13 +++++++++++ tests/ui/flat_map_option.rs | 13 +++++++++++ tests/ui/flat_map_option.stderr | 16 ++++++++++++++ 9 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/methods/flat_map_option.rs create mode 100644 tests/ui/flat_map_option.fixed create mode 100644 tests/ui/flat_map_option.rs create mode 100644 tests/ui/flat_map_option.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 483365b5e91..56cdd356469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2216,6 +2216,7 @@ Released 2018-09-13 [`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next [`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map [`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0f48799d339..a7871b7aba7 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: methods::FILTER_MAP_NEXT, methods::FILTER_NEXT, methods::FLAT_MAP_IDENTITY, + methods::FLAT_MAP_OPTION, methods::FROM_ITER_INSTEAD_OF_COLLECT, methods::GET_UNWRAP, methods::IMPLICIT_CLONE, @@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(matches::SINGLE_MATCH_ELSE), LintId::of(methods::CLONED_INSTEAD_OF_COPIED), LintId::of(methods::FILTER_MAP_NEXT), + LintId::of(methods::FLAT_MAP_OPTION), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::MAP_FLATTEN), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2c8d8a0f132..44b4eb29035 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1509,7 +1509,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<' /// Gets all arms that are unbounded `PatRange`s. fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec> { arms.iter() - .flat_map(|arm| { + .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs = match lhs { diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs new file mode 100644 index 00000000000..12d560653ed --- /dev/null +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{source_map::Span, sym}; + +use super::FLAT_MAP_OPTION; +use clippy_utils::ty::is_type_diagnostic_item; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { + if !is_trait_method(cx, expr, sym::Iterator) { + return; + } + let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); + let sig = match arg_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), + _ => return, + }; + if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) { + return; + } + span_lint_and_sugg( + cx, + FLAT_MAP_OPTION, + span, + "used `flat_map` where `filter_map` could be used instead", + "try", + "filter_map".into(), + Applicability::MachineApplicable, + ) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 49a9af2a465..c2cd3011d14 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -17,6 +17,7 @@ mod filter_map_identity; mod filter_map_next; mod filter_next; mod flat_map_identity; +mod flat_map_option; mod from_iter_instead_of_collect; mod get_unwrap; mod implicit_clone; @@ -97,6 +98,29 @@ declare_clippy_lint! { "used `cloned` where `copied` could be used instead" } +declare_clippy_lint! { + /// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be + /// used instead. + /// + /// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that + /// `Option` is used to produce 0 or 1 items. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let nums: Vec = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect(); + /// ``` + /// Use instead: + /// ```rust + /// let nums: Vec = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect(); + /// ``` + pub FLAT_MAP_OPTION, + pedantic, + "used `flat_map` where `filter_map` could be used instead" +} + declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// @@ -1663,6 +1687,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, CLONED_INSTEAD_OF_COPIED, + FLAT_MAP_OPTION, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, @@ -1958,7 +1983,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); }, - ("flat_map", [flm_arg]) => flat_map_identity::check(cx, expr, flm_arg, span), + ("flat_map", [arg]) => { + flat_map_identity::check(cx, expr, arg, span); + flat_map_option::check(cx, expr, arg, span); + }, ("flatten", []) => { if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { map_flatten::check(cx, expr, recv, map_arg); diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 5e44c7b3ee0..c0584e1e226 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -154,6 +154,6 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { attrs .iter() .filter(|attr| attr.has_name(sym::doc)) - .flat_map(ast::Attribute::meta_item_list) + .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } diff --git a/tests/ui/flat_map_option.fixed b/tests/ui/flat_map_option.fixed new file mode 100644 index 00000000000..6a34f008995 --- /dev/null +++ b/tests/ui/flat_map_option.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::flat_map_option)] +#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)] + +fn main() { + // yay + let c = |x| Some(x); + let _ = [1].iter().filter_map(c); + let _ = [1].iter().filter_map(Some); + + // nay + let _ = [1].iter().flat_map(|_| &Some(1)); +} diff --git a/tests/ui/flat_map_option.rs b/tests/ui/flat_map_option.rs new file mode 100644 index 00000000000..2479abddbf0 --- /dev/null +++ b/tests/ui/flat_map_option.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::flat_map_option)] +#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)] + +fn main() { + // yay + let c = |x| Some(x); + let _ = [1].iter().flat_map(c); + let _ = [1].iter().flat_map(Some); + + // nay + let _ = [1].iter().flat_map(|_| &Some(1)); +} diff --git a/tests/ui/flat_map_option.stderr b/tests/ui/flat_map_option.stderr new file mode 100644 index 00000000000..a9d8056dee9 --- /dev/null +++ b/tests/ui/flat_map_option.stderr @@ -0,0 +1,16 @@ +error: used `flat_map` where `filter_map` could be used instead + --> $DIR/flat_map_option.rs:8:24 + | +LL | let _ = [1].iter().flat_map(c); + | ^^^^^^^^ help: try: `filter_map` + | + = note: `-D clippy::flat-map-option` implied by `-D warnings` + +error: used `flat_map` where `filter_map` could be used instead + --> $DIR/flat_map_option.rs:9:24 + | +LL | let _ = [1].iter().flat_map(Some); + | ^^^^^^^^ help: try: `filter_map` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 8b5faf4ab0cfc54bc9cc23b6f013bc571c62c360 Mon Sep 17 00:00:00 2001 From: JP Sugarbroad Date: Fri, 16 Apr 2021 14:29:23 -0700 Subject: Switch transmute_ptr_to_ptr to "pedantic" class. Per discussion in https://github.com/rust-lang/rust-clippy/issues/6372, this lint has significant false positives. --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/transmute/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0f48799d339..f7255d32c99 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1405,6 +1405,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(strings::STRING_ADD_ASSIGN), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), LintId::of(unicode::NON_ASCII_LITERAL), @@ -1693,7 +1694,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), @@ -1927,7 +1927,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 86ac916df6c..569113910c9 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -274,7 +274,7 @@ declare_clippy_lint! { /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; /// ``` pub TRANSMUTE_PTR_TO_PTR, - complexity, + pedantic, "transmutes from a pointer to a pointer / a reference to a reference" } -- cgit 1.4.1-3-g733a5 From 243dc4625050d791eb8f91b6b4da2f8dc8449a8d Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 18 Apr 2021 23:49:54 +0200 Subject: un-double `return` on try_err --- clippy_lints/src/try_err.rs | 11 ++++++++--- tests/ui/try_err.fixed | 8 ++++++++ tests/ui/try_err.rs | 8 ++++++++ tests/ui/try_err.stderr | 8 +++++++- 4 files changed, 31 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 0b8c03c6865..ebb39ea4877 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, in_macro, is_lang_ctor, match_def_path, paths}; +use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -102,10 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { snippet(cx, err_arg.span, "_") }; + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; let suggestion = if err_ty == expr_err_ty { - format!("return {}{}{}", prefix, origin_snippet, suffix) + format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) } else { - format!("return {}{}.into(){}", prefix, origin_snippet, suffix) + format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) }; span_lint_and_sugg( diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 5b96bb59c5f..264194419c7 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll>> { Poll::Ready(None) } + +// Tests that `return` is not duplicated +pub fn try_return(x: bool) -> Result { + if x { + return Err(42); + } + Ok(0) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index f220d697d2c..bc6979bf457 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll>> { Poll::Ready(None) } + +// Tests that `return` is not duplicated +pub fn try_return(x: bool) -> Result { + if x { + return Err(42)?; + } + Ok(0) +} diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 2c01d37192e..8f332a9b649 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -74,5 +74,11 @@ error: returning an `Err(_)` with the `?` operator LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 10 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:167:16 + | +LL | return Err(42)?; + | ^^^^^^^^ help: try this: `Err(42)` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From c6b381c59bffe8ce62fcf9569fb1a39189234dcc Mon Sep 17 00:00:00 2001 From: Basavesh Shivakumar Date: Mon, 19 Apr 2021 17:20:21 +0200 Subject: useless use of format! should return function directly --- clippy_lints/src/format.rs | 11 ++++++----- tests/ui/format.fixed | 4 ++++ tests/ui/format.rs | 4 ++++ tests/ui/format.stderr | 8 +++++++- 4 files changed, 21 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 4729abbd8e3..c2b055ed648 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; use if_chain::if_chain; @@ -100,15 +101,15 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & return Some(format!("{:?}.to_string()", s.as_str())); } } else { - let snip = snippet(cx, format_args.span, ""); + let sugg = Sugg::hir(cx, format_args, ""); if let ExprKind::MethodCall(path, _, _, _) = format_args.kind { if path.ident.name == sym!(to_string) { - return Some(format!("{}", snip)); + return Some(format!("{}", sugg)); } } else if let ExprKind::Binary(..) = format_args.kind { - return Some(format!("{}", snip)); + return Some(format!("{}", sugg)); } - return Some(format!("{}.to_string()", snip)); + return Some(format!("{}.to_string()", sugg.maybe_par())); } } } @@ -136,7 +137,7 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option = vec!["foo".to_string(), "bar".to_string()]; + let _s: String = (&*v.join("\n")).to_string(); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index b604d79cca3..683957f0ff0 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -67,4 +67,8 @@ fn main() { // False positive let a = "foo".to_string(); let _ = Some(format!("{}", a + "bar")); + + // Wrap it with braces + let v: Vec = vec!["foo".to_string(), "bar".to_string()]; + let _s: String = format!("{}", &*v.join("\n")); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 96df7f37f77..2017eb2b383 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -87,5 +87,11 @@ error: useless use of `format!` LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` -error: aborting due to 13 previous errors +error: useless use of `format!` + --> $DIR/format.rs:73:22 + | +LL | let _s: String = format!("{}", &*v.join("/n")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From e2e104b993277a8096299a2d2eb99d4641f1e318 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 15 Apr 2021 15:26:57 +0200 Subject: Add lint to check for boolean comparison in assert macro calls --- CHANGELOG.md | 1 + clippy_lints/src/bool_assert_comparison.rs | 75 +++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ clippy_utils/src/ast_utils.rs | 32 +++++++++ tests/ui/bool_assert_comparison.rs | 59 +++++++++++++++ tests/ui/bool_assert_comparison.stderr | 112 +++++++++++++++++++++++++++++ 6 files changed, 284 insertions(+) create mode 100644 clippy_lints/src/bool_assert_comparison.rs create mode 100644 tests/ui/bool_assert_comparison.rs create mode 100644 tests/ui/bool_assert_comparison.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7885bdfb12c..830a3efb7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2124,6 +2124,7 @@ Released 2018-09-13 [`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_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`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/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs new file mode 100644 index 00000000000..bee706ed402 --- /dev/null +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -0,0 +1,75 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{ast_utils, is_direct_expn_of}; +use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** This lint warns about boolean comparisons in assert-like macros. + /// + /// **Why is this bad?** It is shorter to use the equivalent. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// assert_eq!("a".is_empty(), false); + /// assert_ne!("a".is_empty(), true); + /// + /// // Good + /// assert!(!"a".is_empty()); + /// ``` + pub BOOL_ASSERT_COMPARISON, + style, + "Using a boolean as comparison value in an assert_* macro when there is no need" +} + +declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); + +fn is_bool_lit(e: &Expr) -> bool { + matches!( + e.kind, + ExprKind::Lit(Lit { + kind: LitKind::Bool(_), + .. + }) + ) && !e.span.from_expansion() +} + +impl EarlyLintPass for BoolAssertComparison { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + let macros = ["assert_eq", "debug_assert_eq"]; + let inverted_macros = ["assert_ne", "debug_assert_ne"]; + + for mac in macros.iter().chain(inverted_macros.iter()) { + if let Some(span) = is_direct_expn_of(e.span, mac) { + if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) { + let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + + if nb_bool_args != 1 { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } + + let non_eq_mac = &mac[..mac.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + span, + &format!("used `{}!` with a literal bool", mac), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); + return; + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..f08388f9037 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -178,6 +178,7 @@ mod await_holding_invalid; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; +mod bool_assert_comparison; mod booleans; mod bytecount; mod cargo_common_metadata; @@ -567,6 +568,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: bit_mask::VERBOSE_BIT_MASK, blacklisted_name::BLACKLISTED_NAME, blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, + bool_assert_comparison::BOOL_ASSERT_COMPARISON, booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, bytecount::NAIVE_BYTECOUNT, @@ -1270,6 +1272,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); + store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1448,6 +1451,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(blacklisted_name::BLACKLISTED_NAME), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(casts::CAST_REF_TO_MUT), @@ -1734,6 +1738,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(blacklisted_name::BLACKLISTED_NAME), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(casts::FN_TO_NUMERIC_CAST), LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index eaea3e636f9..93e10c836cc 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -5,6 +5,7 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; +use if_chain::if_chain; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; @@ -571,3 +572,34 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { _ => false, } } + +/// Extract args from an assert-like macro. +/// +/// Currently working with: +/// - `assert_eq!` and `assert_ne!` +/// - `debug_assert_eq!` and `debug_assert_ne!` +/// +/// For example: +/// +/// `debug_assert_eq!(a, b)` will return Some([a, b]) +pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> { + if_chain! { + if let ExprKind::If(_, ref block, _) = expr.kind; + if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind; + then { + expr = e; + } + } + if_chain! { + if let ExprKind::Block(ref block, _) = expr.kind; + if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind; + if let ExprKind::Match(ref match_expr, _) = expr.kind; + if let ExprKind::Tup(ref tup) = match_expr.kind; + if let [a, b, ..] = tup.as_slice(); + if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind); + then { + return Some([&*a, &*b]); + } + } + None +} diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs new file mode 100644 index 00000000000..2de402fae8c --- /dev/null +++ b/tests/ui/bool_assert_comparison.rs @@ -0,0 +1,59 @@ +#![warn(clippy::bool_assert_comparison)] + +macro_rules! a { + () => { + true + }; +} +macro_rules! b { + () => { + true + }; +} + +fn main() { + assert_eq!("a".len(), 1); + assert_eq!("a".is_empty(), false); + assert_eq!("".is_empty(), true); + assert_eq!(true, "".is_empty()); + assert_eq!(a!(), b!()); + assert_eq!(a!(), "".is_empty()); + assert_eq!("".is_empty(), b!()); + + assert_ne!("a".len(), 1); + assert_ne!("a".is_empty(), false); + assert_ne!("".is_empty(), true); + assert_ne!(true, "".is_empty()); + assert_ne!(a!(), b!()); + assert_ne!(a!(), "".is_empty()); + assert_ne!("".is_empty(), b!()); + + debug_assert_eq!("a".len(), 1); + debug_assert_eq!("a".is_empty(), false); + debug_assert_eq!("".is_empty(), true); + debug_assert_eq!(true, "".is_empty()); + debug_assert_eq!(a!(), b!()); + debug_assert_eq!(a!(), "".is_empty()); + debug_assert_eq!("".is_empty(), b!()); + + debug_assert_ne!("a".len(), 1); + debug_assert_ne!("a".is_empty(), false); + debug_assert_ne!("".is_empty(), true); + debug_assert_ne!(true, "".is_empty()); + debug_assert_ne!(a!(), b!()); + debug_assert_ne!(a!(), "".is_empty()); + debug_assert_ne!("".is_empty(), b!()); + + // assert with error messages + assert_eq!("a".len(), 1, "tadam {}", 1); + assert_eq!("a".len(), 1, "tadam {}", true); + assert_eq!("a".is_empty(), false, "tadam {}", 1); + assert_eq!("a".is_empty(), false, "tadam {}", true); + assert_eq!(false, "a".is_empty(), "tadam {}", true); + + debug_assert_eq!("a".len(), 1, "tadam {}", 1); + debug_assert_eq!("a".len(), 1, "tadam {}", true); + debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); + debug_assert_eq!("a".is_empty(), false, "tadam {}", true); + debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); +} diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr new file mode 100644 index 00000000000..f57acf520d5 --- /dev/null +++ b/tests/ui/bool_assert_comparison.stderr @@ -0,0 +1,112 @@ +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:16:5 + | +LL | assert_eq!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | + = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:17:5 + | +LL | assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:18:5 + | +LL | assert_eq!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:24:5 + | +LL | assert_ne!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:25:5 + | +LL | assert_ne!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:26:5 + | +LL | assert_ne!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:32:5 + | +LL | debug_assert_eq!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:33:5 + | +LL | debug_assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:34:5 + | +LL | debug_assert_eq!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:40:5 + | +LL | debug_assert_ne!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:41:5 + | +LL | debug_assert_ne!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:42:5 + | +LL | debug_assert_ne!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:50:5 + | +LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:51:5 + | +LL | assert_eq!("a".is_empty(), false, "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:52:5 + | +LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:56:5 + | +LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:57:5 + | +LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:58:5 + | +LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: aborting due to 18 previous errors + -- cgit 1.4.1-3-g733a5 From 6eae905808fe830a731ed37e3f4a6264ae98e129 Mon Sep 17 00:00:00 2001 From: Yawara ISHIDA Date: Tue, 20 Apr 2021 13:33:39 +0900 Subject: Add a test for FP in macro expansion --- clippy_lints/src/inconsistent_struct_constructor.rs | 3 ++- tests/ui/inconsistent_struct_constructor.fixed | 13 +++++++++++++ tests/ui/inconsistent_struct_constructor.rs | 13 +++++++++++++ tests/ui/inconsistent_struct_constructor.stderr | 4 ++-- 4 files changed, 30 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 7532f182360..d138c3a8acf 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; @@ -66,7 +67,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl LateLintPass<'_> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { - if !expr.span.from_expansion(); + if !in_macro(expr.span); if let ExprKind::Struct(qpath, fields, base) = expr.kind; let ty = cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 8d9c3110035..d1025743790 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -13,6 +13,15 @@ struct Foo { z: i32, } +macro_rules! new_foo { + () => { + let x = 1; + let y = 1; + let z = 1; + Foo { y, x, z } + }; +} + mod without_base { use super::Foo; @@ -24,6 +33,10 @@ mod without_base { // Should lint. Foo { x, y, z }; + // Should NOT lint. + // issue #7069. + new_foo!(); + // Shoule NOT lint because the order is the same as in the definition. Foo { x, y, z }; diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs index 63fac910501..b095aa64a21 100644 --- a/tests/ui/inconsistent_struct_constructor.rs +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -13,6 +13,15 @@ struct Foo { z: i32, } +macro_rules! new_foo { + () => { + let x = 1; + let y = 1; + let z = 1; + Foo { y, x, z } + }; +} + mod without_base { use super::Foo; @@ -24,6 +33,10 @@ mod without_base { // Should lint. Foo { y, x, z }; + // Should NOT lint. + // issue #7069. + new_foo!(); + // Shoule NOT lint because the order is the same as in the definition. Foo { x, y, z }; diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index d021bb19579..ef308dedb16 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,5 +1,5 @@ error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:25:9 + --> $DIR/inconsistent_struct_constructor.rs:34:9 | LL | Foo { y, x, z }; | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` @@ -7,7 +7,7 @@ LL | Foo { y, x, z }; = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:43:9 + --> $DIR/inconsistent_struct_constructor.rs:56:9 | LL | / Foo { LL | | z, -- cgit 1.4.1-3-g733a5 From 224881b94df5215952c7a955acaefacb388c50c5 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Mon, 12 Apr 2021 18:24:47 -0700 Subject: add unnecessary_self_imports lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/unnecessary_self_imports.rs | 67 ++++++++++++++++++++++++++++ tests/ui/unnecessary_self_imports.fixed | 10 +++++ tests/ui/unnecessary_self_imports.rs | 10 +++++ tests/ui/unnecessary_self_imports.stderr | 23 ++++++++++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/unnecessary_self_imports.rs create mode 100644 tests/ui/unnecessary_self_imports.fixed create mode 100644 tests/ui/unnecessary_self_imports.rs create mode 100644 tests/ui/unnecessary_self_imports.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7885bdfb12c..d9a198f8243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2519,6 +2519,7 @@ Released 2018-09-13 [`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_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..7e12e9be4aa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -356,6 +356,7 @@ mod unicode; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; @@ -963,6 +964,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, unnested_or_patterns::UNNESTED_OR_PATTERNS, @@ -1048,6 +1050,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); + store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1320,6 +1323,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), LintId::of(types::RC_BUFFER), + LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(verbose_file_reads::VERBOSE_FILE_READS), LintId::of(write::PRINT_STDERR), diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 6a52de4f713..64e9dc85466 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -4,7 +4,7 @@ use clippy_utils::sext; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs new file mode 100644 index 00000000000..48c54d79cf1 --- /dev/null +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -0,0 +1,67 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use if_chain::if_chain; +use rustc_ast::{Item, ItemKind, UseTreeKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; + +declare_clippy_lint! { + /// **What it does:** Checks for imports ending in `::{self}`. + /// + /// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`. + /// + /// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported. + /// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt + /// to detect this scenario and that is why it is a restriction lint. + /// + /// **Example:** + /// + /// ```rust + /// use std::io::{self}; + /// ``` + /// Use instead: + /// ```rust + /// use std::io; + /// ``` + pub UNNECESSARY_SELF_IMPORTS, + restriction, + "imports ending in `::{self}`, which can be omitted" +} + +declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]); + +impl EarlyLintPass for UnnecessarySelfImports { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if let ItemKind::Use(use_tree) = &item.kind; + if let UseTreeKind::Nested(nodes) = &use_tree.kind; + if let [(self_tree, _)] = &**nodes; + if let [self_seg] = &*self_tree.prefix.segments; + if self_seg.ident.name == kw::SelfLower; + if let Some(last_segment) = use_tree.prefix.segments.last(); + + then { + span_lint_and_then( + cx, + UNNECESSARY_SELF_IMPORTS, + item.span, + "import ending with `::{self}`", + |diag| { + diag.span_suggestion( + last_segment.span().with_hi(item.span.hi()), + "consider omitting `::{self}`", + format!( + "{}{};", + last_segment.ident, + if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() }, + ), + Applicability::MaybeIncorrect, + ); + diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); + }, + ); + } + } + } +} diff --git a/tests/ui/unnecessary_self_imports.fixed b/tests/ui/unnecessary_self_imports.fixed new file mode 100644 index 00000000000..1185eaa1d55 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::unnecessary_self_imports)] +#![allow(unused_imports, dead_code)] + +use std::collections::hash_map::{self, *}; +use std::fs as alias; +use std::io::{self, Read}; +use std::rc; + +fn main() {} diff --git a/tests/ui/unnecessary_self_imports.rs b/tests/ui/unnecessary_self_imports.rs new file mode 100644 index 00000000000..56bfbc09402 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::unnecessary_self_imports)] +#![allow(unused_imports, dead_code)] + +use std::collections::hash_map::{self, *}; +use std::fs::{self as alias}; +use std::io::{self, Read}; +use std::rc::{self}; + +fn main() {} diff --git a/tests/ui/unnecessary_self_imports.stderr b/tests/ui/unnecessary_self_imports.stderr new file mode 100644 index 00000000000..83a5618c983 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.stderr @@ -0,0 +1,23 @@ +error: import ending with `::{self}` + --> $DIR/unnecessary_self_imports.rs:6:1 + | +LL | use std::fs::{self as alias}; + | ^^^^^^^^^-------------------- + | | + | help: consider omitting `::{self}`: `fs as alias;` + | + = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings` + = note: this will slightly change semantics; any non-module items at the same path will also be imported + +error: import ending with `::{self}` + --> $DIR/unnecessary_self_imports.rs:8:1 + | +LL | use std::rc::{self}; + | ^^^^^^^^^----------- + | | + | help: consider omitting `::{self}`: `rc;` + | + = note: this will slightly change semantics; any non-module items at the same path will also be imported + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 65778fa5e886c641422501e68ec7ffbe690abafc Mon Sep 17 00:00:00 2001 From: Takayuki Date: Thu, 22 Apr 2021 16:52:34 +0900 Subject: fix a false-positive inside const fn in comparison_chain --- clippy_lints/src/comparison_chain.rs | 14 +++++++++++++- tests/ui/comparison_chain.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 42e153909ce..150d8a3d673 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,6 +64,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } + if parent_node_is_if_const_fn(cx, expr) { + return; + } + // Check that there exists at least one explicit else condition let (conds, _) = if_sequence(expr); if conds.len() < 2 { @@ -123,3 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { fn kind_is_cmp(kind: BinOpKind) -> bool { matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } + +fn parent_node_is_if_const_fn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.tcx.hir().find(cx.tcx.hir().get_parent_item(expr.hir_id)) { + Some(Node::Item(item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, item.def_id.to_def_id()), + Some(Node::ImplItem(impl_item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, impl_item.def_id.to_def_id()), + _ => false, + } +} diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 3b03f8c7dfe..c12c6a31027 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -203,4 +203,32 @@ mod issue_5212 { } } +enum Sign { + Negative, + Positive, + Zero, +} + +impl Sign { + const fn sign_i8(n: i8) -> Self { + if n == 0 { + Sign::Zero + } else if n > 0 { + Sign::Positive + } else { + Sign::Negative + } + } +} + +const fn sign_i8(n: i8) -> Sign { + if n == 0 { + Sign::Zero + } else if n > 0 { + Sign::Positive + } else { + Sign::Negative + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From b48699e4cfd8af15669227049f3c6056d4084aba Mon Sep 17 00:00:00 2001 From: cherryblossom <31467609+cherryblossom000@users.noreply.github.com> Date: Thu, 22 Apr 2021 19:34:36 +1000 Subject: `single_component_path_imports`: ignore `pub(crate) use some_macro;` (fixes #7106) --- clippy_lints/src/single_component_path_imports.rs | 36 ++++++++++++++++++---- tests/ui/single_component_path_imports_macro.fixed | 21 +++++++++++++ tests/ui/single_component_path_imports_macro.rs | 21 +++++++++++++ .../ui/single_component_path_imports_macro.stderr | 10 ++++++ 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 tests/ui/single_component_path_imports_macro.fixed create mode 100644 tests/ui/single_component_path_imports_macro.rs create mode 100644 tests/ui/single_component_path_imports_macro.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 6104103580e..a45bb102389 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; -use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -60,8 +60,21 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { // ``` let mut single_use_usages = Vec::new(); + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ```rust,ignore + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + for item in items { - track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); + track_uses( + cx, + &item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); } for single_use in &single_use_usages { @@ -96,6 +109,7 @@ fn track_uses( item: &Item, imports_reused_with_self: &mut Vec, single_use_usages: &mut Vec<(Symbol, Span, bool)>, + macros: &mut Vec, ) { if in_macro(item.span) || item.vis.kind.is_pub() { return; @@ -105,14 +119,22 @@ fn track_uses( ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { check_mod(cx, &items); }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; + let should_report = + |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited); + // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let ident = &segments[0].ident; - single_use_usages.push((ident.name, item.span, true)); + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((name, item.span, true)); + } } return; } @@ -124,8 +146,10 @@ fn track_uses( let segments = &tree.0.prefix.segments; if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let ident = &segments[0].ident; - single_use_usages.push((ident.name, tree.0.span, false)); + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((name, tree.0.span, false)); + } } } } diff --git a/tests/ui/single_component_path_imports_macro.fixed b/tests/ui/single_component_path_imports_macro.fixed new file mode 100644 index 00000000000..05863f9a2bf --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.fixed @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +// #7106: use statements exporting a macro within a crate should not trigger lint + +macro_rules! m1 { + () => {}; +} +pub(crate) use m1; // ok + +macro_rules! m2 { + () => {}; +} + // fail + +fn main() { + m1!(); + m2!(); +} diff --git a/tests/ui/single_component_path_imports_macro.rs b/tests/ui/single_component_path_imports_macro.rs new file mode 100644 index 00000000000..633deea348b --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.rs @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +// #7106: use statements exporting a macro within a crate should not trigger lint + +macro_rules! m1 { + () => {}; +} +pub(crate) use m1; // ok + +macro_rules! m2 { + () => {}; +} +use m2; // fail + +fn main() { + m1!(); + m2!(); +} diff --git a/tests/ui/single_component_path_imports_macro.stderr b/tests/ui/single_component_path_imports_macro.stderr new file mode 100644 index 00000000000..239efb393b1 --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.stderr @@ -0,0 +1,10 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_macro.rs:16:1 + | +LL | use m2; // fail + | ^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 22f8c13cf5650d6c9d6ee7b4f0e88bffba9076ca Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Apr 2021 22:46:10 -0400 Subject: Improve `implicit_return` Better suggestions when returning macro calls. Suggest changeing all the break expressions in a loop, not just the final statement. Don't lint divergent functions. Don't suggest returning the result of any divergent fuction. --- clippy_lints/src/implicit_return.rs | 241 ++++++++++++++++++++++++------------ clippy_utils/src/source.rs | 31 +++-- clippy_utils/src/visitors.rs | 55 +++++++- tests/ui/implicit_return.fixed | 62 +++++++--- tests/ui/implicit_return.rs | 62 +++++++--- tests/ui/implicit_return.stderr | 55 ++++++-- 6 files changed, 366 insertions(+), 140 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 6b379b0d59b..251a7361871 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,13 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_panic_def_id; -use clippy_utils::source::snippet_opt; -use if_chain::if_chain; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, + source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, + visitors::visit_break_exprs, +}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// **What it does:** Checks for missing return statements at the end of a block. @@ -39,89 +41,159 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); -static LINT_BREAK: &str = "change `break` to `return` as shown"; -static LINT_RETURN: &str = "add `return` as shown"; - -fn lint(cx: &LateContext<'_>, outer_span: Span, inner_span: Span, msg: &str) { - let outer_span = outer_span.source_callsite(); - let inner_span = inner_span.source_callsite(); - - span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion( - outer_span, - msg, - format!("return {}", snippet), - Applicability::MachineApplicable, - ); - } - }); +fn lint_return(cx: &LateContext<'_>, span: Span) { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, span, "..", &mut app); + span_lint_and_sugg( + cx, + IMPLICIT_RETURN, + span, + "missing `return` statement", + "add `return` as shown", + format!("return {}", snip), + app, + ); +} + +fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; + span_lint_and_sugg( + cx, + IMPLICIT_RETURN, + break_span, + "missing `return` statement", + "change `break` to `return` as shown", + format!("return {}", snip), + app, + ) +} + +enum LintLocation { + /// The lint was applied to a parent expression. + Parent, + /// The lint was applied to this expression, a child, or not applied. + Inner, } +impl LintLocation { + fn still_parent(self, b: bool) -> Self { + if b { self } else { Self::Inner } + } -fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn is_parent(&self) -> bool { + matches!(*self, Self::Parent) + } +} + +// Gets the call site if the span is in a child context. Otherwise returns `None`. +fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option { + (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span)) +} + +fn lint_implicit_returns( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + // The context of the function body. + ctxt: SyntaxContext, + // Whether the expression is from a macro expansion. + call_site_span: Option, +) -> LintLocation { match expr.kind { - // loops could be using `break` instead of `return` - ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => { - if let Some(expr) = &block.expr { - expr_match(cx, expr); - } - // only needed in the case of `break` with `;` at the end - else if let Some(stmt) = block.stmts.last() { - if_chain! { - if let StmtKind::Semi(expr, ..) = &stmt.kind; - // make sure it's a break, otherwise we want to skip - if let ExprKind::Break(.., Some(break_expr)) = &expr.kind; - then { - lint(cx, expr.span, break_expr.span, LINT_BREAK); - } - } - } - }, - // use `return` instead of `break` - ExprKind::Break(.., break_expr) => { - if let Some(break_expr) = break_expr { - lint(cx, expr.span, break_expr.span, LINT_BREAK); + ExprKind::Block( + Block { + expr: Some(block_expr), .. + }, + _, + ) => lint_implicit_returns( + cx, + block_expr, + ctxt, + call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)), + ) + .still_parent(call_site_span.is_some()), + + ExprKind::If(_, then_expr, Some(else_expr)) => { + // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't + // bother checking. + let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some()); + if res.is_parent() { + // The return was added as a parent of this if expression. + return res; } + lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some()) }, - ExprKind::If(.., if_expr, else_expr) => { - expr_match(cx, if_expr); - if let Some(else_expr) = else_expr { - expr_match(cx, else_expr); + ExprKind::Match(_, arms, _) => { + for arm in arms { + let res = lint_implicit_returns( + cx, + arm.body, + ctxt, + call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)), + ) + .still_parent(call_site_span.is_some()); + if res.is_parent() { + // The return was added as a parent of this match expression. + return res; + } } + LintLocation::Inner }, - ExprKind::Match(.., arms, source) => { - let check_all_arms = match source { - MatchSource::IfLetDesugar { - contains_else_clause: has_else, - } => has_else, - _ => true, - }; - - if check_all_arms { - for arm in arms { - expr_match(cx, arm.body); + + ExprKind::Loop(block, ..) => { + let mut add_return = false; + visit_break_exprs(block, |break_expr, dest, sub_expr| { + if dest.target_id.ok() == Some(expr.hir_id) { + if call_site_span.is_none() && break_expr.span.ctxt() == ctxt { + lint_break(cx, break_expr.span, sub_expr.unwrap().span); + } else { + // the break expression is from a macro call, add a return to the loop + add_return = true; + } + } + }); + if add_return { + #[allow(clippy::option_if_let_else)] + if let Some(span) = call_site_span { + lint_return(cx, span); + LintLocation::Parent + } else { + lint_return(cx, expr.span); + LintLocation::Inner } } else { - expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body); + LintLocation::Inner } }, - // skip if it already has a return statement - ExprKind::Ret(..) => (), - // make sure it's not a call that panics - ExprKind::Call(expr, ..) => { - if_chain! { - if let ExprKind::Path(qpath) = &expr.kind; - if let Some(path_def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - if match_panic_def_id(cx, path_def_id); - then { } - else { - lint(cx, expr.span, expr.span, LINT_RETURN) - } + + // If expressions without an else clause, and blocks without a final expression can only be the final expression + // if they are divergent, or return the unit type. + ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => { + LintLocation::Inner + }, + + // Any divergent expression doesn't need a return statement. + ExprKind::MethodCall(..) + | ExprKind::Call(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Index(..) + if cx.typeck_results().expr_ty(expr).is_never() => + { + LintLocation::Inner + }, + + _ => + { + #[allow(clippy::option_if_let_else)] + if let Some(span) = call_site_span { + lint_return(cx, span); + LintLocation::Parent + } else { + lint_return(cx, expr.span); + LintLocation::Inner } }, - // everything else is missing `return` - _ => lint(cx, expr.span, expr.span, LINT_RETURN), } } @@ -129,19 +201,24 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: FnKind<'tcx>, - _: &'tcx FnDecl<'_>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, span: Span, _: HirId, ) { - if span.from_expansion() { + if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_))) + || span.ctxt() != body.value.span.ctxt() + || in_external_macro(cx.sess(), span) + { return; } - let body = cx.tcx.hir().body(body.id()); - if cx.typeck_results().expr_ty(&body.value).is_unit() { + + let res_ty = cx.typeck_results().expr_ty(&body.value); + if res_ty.is_unit() || res_ty.is_never() { return; } - expr_match(cx, &body.value); + + lint_implicit_returns(cx, &body.value, body.value.span.ctxt(), None); } } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 53180d1f9f5..2e731c182ec 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -280,17 +280,17 @@ pub fn snippet_with_context( default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - let outer_span = hygiene::walk_chain(span, outer); - let (span, is_macro_call) = if outer_span.ctxt() == outer { - (outer_span, span.ctxt() != outer) - } else { - // The span is from a macro argument, and the outer context is the macro using the argument - if *applicability != Applicability::Unspecified { - *applicability = Applicability::MaybeIncorrect; - } - // TODO: get the argument span. - (span, false) - }; + let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( + || { + // The span is from a macro argument, and the outer context is the macro using the argument + if *applicability != Applicability::Unspecified { + *applicability = Applicability::MaybeIncorrect; + } + // TODO: get the argument span. + (span, false) + }, + |outer_span| (outer_span, span.ctxt() != outer), + ); ( snippet_with_applicability(cx, span, default, applicability), @@ -298,6 +298,15 @@ pub fn snippet_with_context( ) } +/// Walks the span up to the target context, thereby returning the macro call site if the span is +/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the +/// case of the span being in a macro expansion, but the target context is from expanding a macro +/// argument. +pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option { + let outer_span = hygiene::walk_chain(span, outer); + (outer_span.ctxt() == outer).then(|| outer_span) +} + /// Removes block comments from the given `Vec` of lines. /// /// # Examples diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 5a8c629e333..d431bdf34ee 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::path_to_local_id; use rustc_hir as hir; -use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Arm, Body, Expr, HirId, Stmt}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -188,3 +188,54 @@ impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { NestedVisitorMap::OnlyBodies(self.hir) } } + +pub trait Visitable<'tcx> { + fn visit>(self, v: &mut V); +} +impl Visitable<'tcx> for &'tcx Expr<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_expr(self) + } +} +impl Visitable<'tcx> for &'tcx Block<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_block(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_stmt(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_body(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_arm(self) + } +} + +pub fn visit_break_exprs<'tcx>( + node: impl Visitable<'tcx>, + f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>), +) { + struct V(F); + impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if let ExprKind::Break(dest, sub_expr) = e.kind { + self.0(e, dest, sub_expr) + } + walk_expr(self, e); + } + } + + node.visit(&mut V(f)); +} diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index 59f7ad9c106..c0fc4b926a0 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::implicit_return)] -#![allow(clippy::needless_return, unused)] +#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)] fn test_end_of_fn() -> bool { if true { @@ -12,7 +12,6 @@ fn test_end_of_fn() -> bool { return true } -#[allow(clippy::needless_bool)] fn test_if_block() -> bool { if true { return true } else { return false } } @@ -25,7 +24,6 @@ fn test_match(x: bool) -> bool { } } -#[allow(clippy::needless_return)] fn test_match_with_unreachable(x: bool) -> bool { match x { true => return false, @@ -33,14 +31,12 @@ fn test_match_with_unreachable(x: bool) -> bool { } } -#[allow(clippy::never_loop)] fn test_loop() -> bool { loop { return true; } } -#[allow(clippy::never_loop)] fn test_loop_with_block() -> bool { loop { { @@ -49,7 +45,6 @@ fn test_loop_with_block() -> bool { } } -#[allow(clippy::never_loop)] fn test_loop_with_nests() -> bool { loop { if true { @@ -83,15 +78,48 @@ fn test_return_macro() -> String { return format!("test {}", "test") } -fn main() { - let _ = test_end_of_fn(); - let _ = test_if_block(); - let _ = test_match(true); - let _ = test_match_with_unreachable(true); - let _ = test_loop(); - let _ = test_loop_with_block(); - let _ = test_loop_with_nests(); - let _ = test_loop_with_if_let(); - test_closure(); - let _ = test_return_macro(); +fn macro_branch_test() -> bool { + macro_rules! m { + ($t:expr, $f:expr) => { + if true { $t } else { $f } + }; + } + return m!(true, false) +} + +fn loop_test() -> bool { + 'outer: loop { + if true { + return true; + } + + let _ = loop { + if false { + return false; + } + if true { + break true; + } + }; + } } + +fn loop_macro_test() -> bool { + macro_rules! m { + ($e:expr) => { + break $e + }; + } + return loop { + m!(true); + } +} + +fn divergent_test() -> bool { + fn diverge() -> ! { + panic!() + } + diverge() +} + +fn main() {} diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index 2c1bc046515..737ffd5ddce 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::implicit_return)] -#![allow(clippy::needless_return, unused)] +#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)] fn test_end_of_fn() -> bool { if true { @@ -12,7 +12,6 @@ fn test_end_of_fn() -> bool { true } -#[allow(clippy::needless_bool)] fn test_if_block() -> bool { if true { true } else { false } } @@ -25,7 +24,6 @@ fn test_match(x: bool) -> bool { } } -#[allow(clippy::needless_return)] fn test_match_with_unreachable(x: bool) -> bool { match x { true => return false, @@ -33,14 +31,12 @@ fn test_match_with_unreachable(x: bool) -> bool { } } -#[allow(clippy::never_loop)] fn test_loop() -> bool { loop { break true; } } -#[allow(clippy::never_loop)] fn test_loop_with_block() -> bool { loop { { @@ -49,7 +45,6 @@ fn test_loop_with_block() -> bool { } } -#[allow(clippy::never_loop)] fn test_loop_with_nests() -> bool { loop { if true { @@ -83,15 +78,48 @@ fn test_return_macro() -> String { format!("test {}", "test") } -fn main() { - let _ = test_end_of_fn(); - let _ = test_if_block(); - let _ = test_match(true); - let _ = test_match_with_unreachable(true); - let _ = test_loop(); - let _ = test_loop_with_block(); - let _ = test_loop_with_nests(); - let _ = test_loop_with_if_let(); - test_closure(); - let _ = test_return_macro(); +fn macro_branch_test() -> bool { + macro_rules! m { + ($t:expr, $f:expr) => { + if true { $t } else { $f } + }; + } + m!(true, false) } + +fn loop_test() -> bool { + 'outer: loop { + if true { + break true; + } + + let _ = loop { + if false { + break 'outer false; + } + if true { + break true; + } + }; + } +} + +fn loop_macro_test() -> bool { + macro_rules! m { + ($e:expr) => { + break $e + }; + } + loop { + m!(true); + } +} + +fn divergent_test() -> bool { + fn diverge() -> ! { + panic!() + } + diverge() +} + +fn main() {} diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index 3608319e5bd..632e30cbdc6 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -7,64 +7,97 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:17:15 + --> $DIR/implicit_return.rs:16:15 | LL | if true { true } else { false } | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:17:29 + --> $DIR/implicit_return.rs:16:29 | LL | if true { true } else { false } | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:23:17 + --> $DIR/implicit_return.rs:22:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:24:20 + --> $DIR/implicit_return.rs:23:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:39:9 + --> $DIR/implicit_return.rs:36:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:47:13 + --> $DIR/implicit_return.rs:43:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:56:13 + --> $DIR/implicit_return.rs:51:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:74:18 + --> $DIR/implicit_return.rs:69:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:75:16 + --> $DIR/implicit_return.rs:70:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:83:5 + --> $DIR/implicit_return.rs:78:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` -error: aborting due to 11 previous errors +error: missing `return` statement + --> $DIR/implicit_return.rs:87:5 + | +LL | m!(true, false) + | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` + +error: missing `return` statement + --> $DIR/implicit_return.rs:93:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:98:17 + | +LL | break 'outer false; + | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` + +error: missing `return` statement + --> $DIR/implicit_return.rs:113:5 + | +LL | / loop { +LL | | m!(true); +LL | | } + | |_____^ + | +help: add `return` as shown + | +LL | return loop { +LL | m!(true); +LL | } + | + +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 74cf5f2fc6c121ae0eaaa709c9a303cd82fe9b30 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 17:53:14 -0400 Subject: Fix `implicit_return` suggestion for async functions --- clippy_lints/src/implicit_return.rs | 11 +++++++++- clippy_utils/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++--- tests/ui/implicit_return.fixed | 6 ++++++ tests/ui/implicit_return.rs | 6 ++++++ tests/ui/implicit_return.stderr | 38 ++++++++++++++++++++--------------- 5 files changed, 81 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 251a7361871..39612dbf050 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,5 +1,6 @@ use clippy_utils::{ diagnostics::span_lint_and_sugg, + get_async_fn_body, is_async_fn, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, visitors::visit_break_exprs, }; @@ -219,6 +220,14 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - lint_implicit_returns(cx, &body.value, body.value.span.ctxt(), None); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(e) => e, + None => return, + } + } else { + &body.value + }; + lint_implicit_returns(cx, expr, expr.span.ctxt(), None); } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b7017411927..6dc96e51a6b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -60,12 +60,12 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, - QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, + PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1300,6 +1300,40 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } +/// Checks if the given function kind is an async function. +pub fn is_async_fn(kind: FnKind) -> bool { + matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async) +} + +/// Peels away all the compiler generated code surrounding the body of an async function, +pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call( + _, + &[Expr { + kind: ExprKind::Closure(_, _, body, _, _), + .. + }], + ) = body.value.kind + { + if let ExprKind::Block( + Block { + stmts: [], + expr: + Some(Expr { + kind: ExprKind::DropTemps(expr), + .. + }), + .. + }, + _, + ) = tcx.hir().body(body).value.kind + { + return Some(expr); + } + }; + None +} + // Finds the `#[must_use]` attribute, if any pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { attrs.iter().find(|a| a.has_name(sym::must_use)) diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index c0fc4b926a0..7698b88a88c 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::implicit_return)] @@ -122,4 +123,9 @@ fn divergent_test() -> bool { diverge() } +// issue #6940 +async fn foo() -> bool { + return true +} + fn main() {} diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index 737ffd5ddce..45bbc2ec670 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::implicit_return)] @@ -122,4 +123,9 @@ fn divergent_test() -> bool { diverge() } +// issue #6940 +async fn foo() -> bool { + true +} + fn main() {} diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index 632e30cbdc6..16fe9ed444f 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -1,5 +1,5 @@ error: missing `return` statement - --> $DIR/implicit_return.rs:12:5 + --> $DIR/implicit_return.rs:13:5 | LL | true | ^^^^ help: add `return` as shown: `return true` @@ -7,85 +7,85 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:16:15 + --> $DIR/implicit_return.rs:17:15 | LL | if true { true } else { false } | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:16:29 + --> $DIR/implicit_return.rs:17:29 | LL | if true { true } else { false } | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:22:17 + --> $DIR/implicit_return.rs:23:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:23:20 + --> $DIR/implicit_return.rs:24:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:36:9 + --> $DIR/implicit_return.rs:37:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:43:13 + --> $DIR/implicit_return.rs:44:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:51:13 + --> $DIR/implicit_return.rs:52:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:69:18 + --> $DIR/implicit_return.rs:70:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:70:16 + --> $DIR/implicit_return.rs:71:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:78:5 + --> $DIR/implicit_return.rs:79:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` error: missing `return` statement - --> $DIR/implicit_return.rs:87:5 + --> $DIR/implicit_return.rs:88:5 | LL | m!(true, false) | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` error: missing `return` statement - --> $DIR/implicit_return.rs:93:13 + --> $DIR/implicit_return.rs:94:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:98:17 + --> $DIR/implicit_return.rs:99:17 | LL | break 'outer false; | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:113:5 + --> $DIR/implicit_return.rs:114:5 | LL | / loop { LL | | m!(true); @@ -99,5 +99,11 @@ LL | m!(true); LL | } | -error: aborting due to 15 previous errors +error: missing `return` statement + --> $DIR/implicit_return.rs:128:5 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 3d793f3111845c8d5836310387b65622ea86b01f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 22 Apr 2021 09:11:36 -0400 Subject: Minor cleanup of `implicit_return` --- clippy_lints/src/implicit_return.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 39612dbf050..30174fa2100 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -70,6 +70,7 @@ fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) { ) } +#[derive(Clone, Copy, PartialEq, Eq)] enum LintLocation { /// The lint was applied to a parent expression. Parent, @@ -81,8 +82,8 @@ impl LintLocation { if b { self } else { Self::Inner } } - fn is_parent(&self) -> bool { - matches!(*self, Self::Parent) + fn is_parent(self) -> bool { + self == Self::Parent } } -- cgit 1.4.1-3-g733a5 From 5625d58f9f3fbff3d81cbab76fbbcfc5ff06c833 Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Fri, 16 Apr 2021 20:12:16 +0200 Subject: add detection unused_io_amount of "or", "or_else" and "ok" --- clippy_lints/src/unused_io_amount.rs | 20 +++++++++++++----- tests/ui/unused_io_amount.rs | 41 +++++++++++++++++++++++++++++++++++- tests/ui/unused_io_amount.stderr | 30 +++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 5e8e530f480..3387f35bac3 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -47,25 +47,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) ) { - check_method_call(cx, &args[0], expr); + check_map_error(cx, &args[0], expr); } } else { - check_method_call(cx, res, expr); + check_map_error(cx, res, expr); } }, - hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { - check_method_call(cx, &args[0], expr); + check_map_error(cx, &args[0], expr); }, _ => (), }, - _ => (), } } } +fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { + let mut call = call; + while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind { + if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") { + call = &args[0]; + } else { + break; + } + } + check_method_call(cx, call, expr); +} + fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = &*path.ident.as_str(); diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index ebaba9629db..8b141e25942 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] #![warn(clippy::unused_io_amount)] -use std::io; +use std::io::{self, Read}; fn question_mark(s: &mut T) -> io::Result<()> { s.write(b"test")?; @@ -22,4 +22,43 @@ fn vectored(s: &mut T) -> io::Result<()> { Ok(()) } +fn ok(file: &str) -> Option<()> { + let mut reader = std::fs::File::open(file).ok()?; + let mut result = [0u8; 0]; + reader.read(&mut result).ok()?; + Some(()) +} + +#[allow(clippy::redundant_closure)] +#[allow(clippy::bind_instead_of_map)] +fn or_else(file: &str) -> io::Result<()> { + let mut reader = std::fs::File::open(file)?; + let mut result = [0u8; 0]; + reader.read(&mut result).or_else(|err| Err(err))?; + Ok(()) +} + +#[derive(Debug)] +enum Error { + Kind, +} + +fn or(file: &str) -> Result<(), Error> { + let mut reader = std::fs::File::open(file).unwrap(); + let mut result = [0u8; 0]; + reader.read(&mut result).or(Err(Error::Kind))?; + Ok(()) +} + +fn combine_or(file: &str) -> Result<(), Error> { + let mut reader = std::fs::File::open(file).unwrap(); + let mut result = [0u8; 0]; + reader + .read(&mut result) + .or(Err(Error::Kind)) + .or(Err(Error::Kind)) + .expect("error"); + Ok(()) +} + fn main() {} diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 5219d63980b..d8dfc0e5a79 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -36,5 +36,33 @@ error: written amount is not handled LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:28:5 + | +LL | reader.read(&mut result).ok()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:37:5 + | +LL | reader.read(&mut result).or_else(|err| Err(err))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:49:5 + | +LL | reader.read(&mut result).or(Err(Error::Kind))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:56:5 + | +LL | / reader +LL | | .read(&mut result) +LL | | .or(Err(Error::Kind)) +LL | | .or(Err(Error::Kind)) +LL | | .expect("error"); + | |________________________^ + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 3f5be5e2357d11ed3273d4e286e2cb9efe46cd28 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 25 Apr 2021 09:51:44 -0500 Subject: Fix cloned_instead_of_copied MSRV --- .../src/methods/cloned_instead_of_copied.rs | 24 +++++++++++++++------- clippy_lints/src/methods/mod.rs | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index ba97ab3900c..9c1b6f55c88 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,23 +1,33 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use clippy_utils::{is_trait_method, meets_msrv}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { +const ITERATOR_COPIED_MSRV: RustcVersion = RustcVersion::new(1, 36, 0); +const OPTION_COPIED_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` - ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0), - _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) { - // ::Item - Some(ty) => ty, - _ => return, + ty::Adt(adt, subst) + if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &OPTION_COPIED_MSRV) => + { + subst.type_at(0) + }, + _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &ITERATOR_COPIED_MSRV) => { + match get_iterator_item_ty(cx, recv_ty) { + // ::Item + Some(ty) => ty, + _ => return, + } }, _ => return, }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c2cd3011d14..e15dbb899b3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1959,7 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), ("collect", []) => match method_call!(recv) { Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), Some(("map", [m_recv, m_arg], _)) => { -- cgit 1.4.1-3-g733a5 From efc4c6c957ea2b0d870f4728fa934042213da5e8 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 25 Apr 2021 18:10:38 +0200 Subject: extend `single_element_loop` to match `.iter()` --- clippy_lints/src/loops/single_element_loop.rs | 8 +++++++- tests/ui/single_element_loop.fixed | 5 +++++ tests/ui/single_element_loop.rs | 4 ++++ tests/ui/single_element_loop.stderr | 18 +++++++++++++++++- 4 files changed, 33 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index fc067e81bca..0fd09ff7197 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -14,8 +14,14 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { + let arg_expr = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, + ExprKind::MethodCall(method, _, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => { + &args[0] + }, + _ => return, + }; if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index 8ca068293a6..c307afffcb8 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -8,4 +8,9 @@ fn main() { let item = &item1; println!("{}", item); } + + { + let item = &item1; + println!("{:?}", item); + } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 57e9336a31f..2c0c03b7211 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -7,4 +7,8 @@ fn main() { for item in &[item1] { println!("{}", item); } + + for item in [item1].iter() { + println!("{:?}", item); + } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index 90be1dc3283..0e35a33ded5 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -15,5 +15,21 @@ LL | println!("{}", item); LL | } | -error: aborting due to previous error +error: for loop over a single element + --> $DIR/single_element_loop.rs:11:5 + | +LL | / for item in [item1].iter() { +LL | | println!("{:?}", item); +LL | | } + | |_____^ + | +help: try + | +LL | { +LL | let item = &item1; +LL | println!("{:?}", item); +LL | } + | + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From dcf4e07458c4136f2107d5ff26590c47f9590b1a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 26 Apr 2021 11:09:24 -0500 Subject: Finish MSRV for cloned_instead_of_copied --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/min_rust_version_attr.rs | 4 ++++ tests/ui/min_rust_version_attr.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 147f823491d..2f8714b1799 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: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports + /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 49ace1ca128..7f9f7ddc535 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -4,6 +4,10 @@ use std::ops::{Deref, RangeFrom}; +fn cloned_instead_of_copied() { + let _ = [1].iter().cloned(); +} + fn option_as_ref_deref() { let mut opt = Some(String::from("123")); diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 8d3575c2da8..ddb1e1f3724 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:156:24 + --> $DIR/min_rust_version_attr.rs:160:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:155:9 + --> $DIR/min_rust_version_attr.rs:159:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:168:24 + --> $DIR/min_rust_version_attr.rs:172:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:167:9 + --> $DIR/min_rust_version_attr.rs:171:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 84003aa7a18f2673b20340dd20344dda8265ba7a Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 26 Apr 2021 12:08:24 -0700 Subject: fix invalid code suggestion in `manual_unwrap_or`, due to macro expansion --- clippy_lints/src/manual_unwrap_or.rs | 11 ++++++++++- tests/ui/manual_unwrap_or.fixed | 12 ++++++++++++ tests/ui/manual_unwrap_or.rs | 15 +++++++++++++++ tests/ui/manual_unwrap_or.stderr | 12 +++++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 65baa2552cc..520162559e5 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -112,6 +112,15 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); + + let suggestion = if scrutinee.span.from_expansion() { + // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)` + sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") + } + else { + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() + }; + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, @@ -119,7 +128,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { "replace with", format!( "{}.unwrap_or({})", - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(), + suggestion, reindented_or_body, ), Applicability::MachineApplicable, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index f1d3252230b..e7a29596b73 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -151,4 +151,16 @@ const fn const_fn_result_unwrap_or() { }; } +mod issue6965 { + macro_rules! some_macro { + () => { + if 1 > 2 { Some(1) } else { None } + }; + } + + fn test() { + let _ = some_macro!().unwrap_or(0); + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index c9eee25a5b1..66006b6c616 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -190,4 +190,19 @@ const fn const_fn_result_unwrap_or() { }; } +mod issue6965 { + macro_rules! some_macro { + () => { + if 1 > 2 { Some(1) } else { None } + }; + } + + fn test() { + let _ = match some_macro!() { + Some(val) => val, + None => 0, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index fc174c4c270..99625b789b6 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -141,5 +141,15 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 13 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:201:17 + | +LL | let _ = match some_macro!() { + | _________________^ +LL | | Some(val) => val, +LL | | None => 0, +LL | | }; + | |_________^ help: replace with: `some_macro!().unwrap_or(0)` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From d7627dcfc8a60aaedccf002738dc44a2576fa8fd Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 26 Apr 2021 13:03:51 -0700 Subject: Fix FN in `iter_cloned_collect` with a large array --- clippy_lints/src/methods/utils.rs | 4 +--- tests/ui/iter_cloned_collect.fixed | 4 ++++ tests/ui/iter_cloned_collect.rs | 4 ++++ tests/ui/iter_cloned_collect.stderr | 8 +++++++- 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index f6bf37e08b9..0daea47816a 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -18,9 +18,7 @@ pub(super) 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).is_some(), ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, } diff --git a/tests/ui/iter_cloned_collect.fixed b/tests/ui/iter_cloned_collect.fixed index 2773227e26b..39cc58cd298 100644 --- a/tests/ui/iter_cloned_collect.fixed +++ b/tests/ui/iter_cloned_collect.fixed @@ -19,4 +19,8 @@ fn main() { let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) .to_bytes().to_vec(); } + + // Issue #6808 + let arr: [u8; 64] = [0; 64]; + let _: Vec<_> = arr.to_vec(); } diff --git a/tests/ui/iter_cloned_collect.rs b/tests/ui/iter_cloned_collect.rs index 60a4eac23c7..c2a036ec09f 100644 --- a/tests/ui/iter_cloned_collect.rs +++ b/tests/ui/iter_cloned_collect.rs @@ -22,4 +22,8 @@ fn main() { .cloned() .collect(); } + + // Issue #6808 + let arr: [u8; 64] = [0; 64]; + let _: Vec<_> = arr.iter().cloned().collect(); } diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index b90a1e6c919..e1df61794ce 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -22,5 +22,11 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 3 previous errors +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:28:24 + | +LL | let _: Vec<_> = arr.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 33ed8b5b24caf1e3d1d4d8020f61ca65102c87f8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 26 Apr 2021 15:49:25 -0500 Subject: Remove needless_question_mark MSRV --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/needless_question_mark.rs | 42 ++++------------- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/needless_question_mark.fixed | 72 ------------------------------ tests/ui/needless_question_mark.rs | 72 ------------------------------ tests/ui/needless_question_mark.stderr | 22 +-------- 6 files changed, 13 insertions(+), 199 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2ff633bcda5..c84890299df 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1076,7 +1076,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); - store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark); store.register_late_pass(move || box casts::Casts::new(msrv)); store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index cfe7ae6630e..7b156a8c49d 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,15 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, meets_msrv}; +use clippy_utils::{differing_macro_contexts, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; declare_clippy_lint! { @@ -63,21 +61,7 @@ declare_clippy_lint! { "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." } -const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); -const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); - -pub struct NeedlessQuestionMark { - msrv: Option, -} - -impl NeedlessQuestionMark { - #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } - } -} - -impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); +declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); #[derive(Debug)] enum SomeOkCall<'a> { @@ -111,7 +95,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { _ => return, }; - if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) { + if let Some(ok_some_call) = is_some_or_ok_call(cx, e) { emit_lint(cx, &ok_some_call); } } @@ -127,14 +111,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark { if_chain! { if let Some(expr) = expr_opt; - if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr); + if let Some(ok_some_call) = is_some_or_ok_call(cx, expr); then { emit_lint(cx, &ok_some_call); } }; } - - extract_msrv_attr!(LateContext); } fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { @@ -153,11 +135,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { ); } -fn is_some_or_ok_call<'a>( - nqml: &NeedlessQuestionMark, - cx: &'a LateContext<'_>, - expr: &'a Expr<'_>, -) -> Option> { +fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option> { if_chain! { // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; @@ -188,8 +166,7 @@ fn is_some_or_ok_call<'a>( let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); // Check for Option MSRV - let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); - if outer_is_some && inner_is_some && meets_option_msrv { + if outer_is_some && inner_is_some { return Some(SomeOkCall::SomeCall(expr, inner_expr)); } @@ -202,8 +179,7 @@ fn is_some_or_ok_call<'a>( let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); // Must meet Result MSRV - let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); - if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + if outer_is_result && inner_is_result && does_not_call_from { return Some(SomeOkCall::OkCall(expr, inner_expr)); } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2f8714b1799..d56855a71c1 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: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports + /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index fd8433870bb..52ddd9d2dc8 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -96,78 +96,6 @@ where fn main() {} -mod question_mark_none { - #![clippy::msrv = "1.12.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should not be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_result { - #![clippy::msrv = "1.21.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - to.magic // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_both { - #![clippy::msrv = "1.22.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - to.magic // should be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - to.magic // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, // the suggestion fails to apply; do not lint macro_rules! some_in_macro { diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 36d45ac7e03..1ea4ba0d83f 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -96,78 +96,6 @@ where fn main() {} -mod question_mark_none { - #![clippy::msrv = "1.12.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should not be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_result { - #![clippy::msrv = "1.21.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_both { - #![clippy::msrv = "1.22.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, // the suggestion fails to apply; do not lint macro_rules! some_in_macro { diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 7cbf1e505ad..afd68d91e51 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -67,25 +67,7 @@ LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:138:9 - | -LL | Ok(to.magic?) // should be triggered - | ^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:154:9 - | -LL | Some(to.magic?) // should be triggered - | ^^^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:162:9 - | -LL | Ok(to.magic?) // should be triggered - | ^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:187:27 + --> $DIR/needless_question_mark.rs:115:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` @@ -95,5 +77,5 @@ LL | let _x = some_and_qmark_in_macro!(x?); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 15 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 340b570ea0479dc3a9f9bbe1031f8686c9f660e1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 26 Apr 2021 14:10:32 -0500 Subject: Refactor MSRV aliases --- clippy_lints/src/casts/ptr_as_ptr.rs | 6 ++--- clippy_lints/src/checked_conversions.rs | 6 ++--- clippy_lints/src/from_over_into.rs | 6 ++--- clippy_lints/src/if_then_some_else_none.rs | 6 ++--- clippy_lints/src/manual_non_exhaustive.rs | 6 ++--- clippy_lints/src/manual_strip.rs | 6 ++--- clippy_lints/src/matches.rs | 9 +++---- clippy_lints/src/mem_replace.rs | 6 ++--- .../src/methods/cloned_instead_of_copied.rs | 9 +++---- clippy_lints/src/methods/filter_map_next.rs | 6 ++--- clippy_lints/src/methods/map_unwrap_or.rs | 11 ++++---- clippy_lints/src/methods/option_as_ref_deref.rs | 6 ++--- clippy_lints/src/missing_const_for_fn.rs | 6 ++--- clippy_lints/src/ranges.rs | 6 ++--- clippy_lints/src/redundant_field_names.rs | 6 ++--- clippy_lints/src/redundant_static_lifetimes.rs | 6 ++--- clippy_lints/src/unnested_or_patterns.rs | 17 +++++-------- clippy_lints/src/use_self.rs | 11 ++++---- clippy_utils/src/lib.rs | 1 + clippy_utils/src/msrvs.rs | 29 ++++++++++++++++++++++ 20 files changed, 80 insertions(+), 85 deletions(-) create mode 100644 clippy_utils/src/msrvs.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 9113e5a0920..3132d3a5cf0 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::meets_msrv; use clippy_utils::sugg::Sugg; +use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; @@ -12,10 +12,8 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); - pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option) { - if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) { + if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 6a2666bc6c0..8d3123e1ec8 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, SpanlessEq}; +use clippy_utils::{meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -12,8 +12,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); - declare_clippy_lint! { /// **What it does:** Checks for explicit bounds checking when casting. /// @@ -58,7 +56,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5e2baba8943..3560672a748 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::paths::INTO; -use clippy_utils::{match_def_path, meets_msrv}; +use clippy_utils::{match_def_path, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); - declare_clippy_lint! { /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. /// @@ -57,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl LateLintPass<'_> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { return; } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 85c95f1151f..eadcd0867a8 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv}; +use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -9,8 +9,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0); - declare_clippy_lint! { /// **What it does:** Checks for if-else that could be written to `bool::then`. /// @@ -59,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl LateLintPass<'_> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { return; } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index dc19805b50a..54f714b54b6 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,7 +1,7 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::meets_msrv; use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; use rustc_errors::Applicability; @@ -10,8 +10,6 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. /// @@ -76,7 +74,7 @@ impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustive { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { return; } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index dfa464ddb81..23428524dee 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -2,7 +2,7 @@ use crate::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths}; +use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -17,8 +17,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; -const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); - declare_clippy_lint! { /// **What it does:** /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using @@ -74,7 +72,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 44b4eb29035..13b2a834b0a 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -7,8 +7,9 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs, + path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, + strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -578,8 +579,6 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); -const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0); - impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { @@ -588,7 +587,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { redundant_pattern_match::check(cx, expr); - if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) { if !check_match_like_matches(cx, expr) { lint_match_arms(cx, expr); } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index ec60bffe955..183daee3617 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths}; +use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -256,8 +256,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } } -const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - pub struct MemReplace { msrv: Option, } @@ -281,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index 9c1b6f55c88..ecec6da3aa0 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; -use clippy_utils::{is_trait_method, meets_msrv}; +use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -10,19 +10,16 @@ use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -const ITERATOR_COPIED_MSRV: RustcVersion = RustcVersion::new(1, 36, 0); -const OPTION_COPIED_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); - pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &OPTION_COPIED_MSRV) => + if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &ITERATOR_COPIED_MSRV) => { + _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 2b19e4ee8c0..f0d69a1f42e 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, meets_msrv}; +use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,8 +9,6 @@ use rustc_span::sym; use super::FILTER_MAP_NEXT; -const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); - pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -19,7 +17,7 @@ pub(super) fn check<'tcx>( msrv: Option<&RustcVersion>, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 4330fea727b..4d8365fcda1 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::meets_msrv; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; +use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -11,8 +11,6 @@ use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; -const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); - /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s /// Return true if lint triggered pub(super) fn check<'tcx>( @@ -23,13 +21,14 @@ pub(super) fn check<'tcx>( unwrap_arg: &'tcx hir::Expr<'_>, msrv: Option<&RustcVersion>, ) -> bool { - if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { - return false; - } // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); + if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) { + return false; + } + if is_option || is_result { // Don't make a suggestion that may fail to compile due to mutably borrowing // the same variable twice. diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 7e9c8fa829d..5a57135038f 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks}; +use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -12,8 +12,6 @@ use rustc_span::sym; use super::OPTION_AS_REF_DEREF; -const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -23,7 +21,7 @@ pub(super) fn check<'tcx>( is_mut: bool, msrv: Option<&RustcVersion>, ) { - if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 93b7a897405..27b5a07c1bc 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; -use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; @@ -12,8 +12,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; -const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); - declare_clippy_lint! { /// **What it does:** /// @@ -97,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) { return; } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 1c3c125e579..7169f96eaf1 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,7 +2,7 @@ use crate::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path}; use clippy_utils::{higher, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; @@ -159,8 +159,6 @@ declare_clippy_lint! { "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" } -const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); - pub struct Ranges { msrv: Option, } @@ -187,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, l, r) => { - if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr); } }, diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index abebd422797..d5ee8d3468d 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::meets_msrv; +use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -7,8 +7,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); - declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands /// could be used. @@ -52,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 32b57698ec5..48107d9c037 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::meets_msrv; use clippy_utils::source::snippet; +use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); - declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. /// @@ -100,7 +98,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9376a2cf66a..3e985fa72b8 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,11 +1,8 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] +use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::over; -use clippy_utils::{ - ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}, - meets_msrv, -}; +use clippy_utils::{meets_msrv, msrvs, over}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; @@ -54,8 +51,6 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0); - #[derive(Clone, Copy)] pub struct UnnestedOrPatterns { msrv: Option, @@ -72,13 +67,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -86,13 +81,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index c6a3c58a9a2..aa4d16633ff 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::{in_macro, meets_msrv}; +use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -62,8 +62,6 @@ pub struct UseSelf { stack: Vec, } -const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); - impl UseSelf { #[must_use] pub fn new(msrv: Option) -> Self { @@ -236,7 +234,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + if in_macro(hir_ty.span) + || in_impl(cx, hir_ty) + || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) + { return; } @@ -288,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) { return; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b7017411927..9b60c92bca1 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -38,6 +38,7 @@ pub mod diagnostics; pub mod eager_or_lazy; pub mod higher; mod hir_utils; +pub mod msrvs; pub mod numeric_literal; pub mod paths; pub mod ptr; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs new file mode 100644 index 00000000000..00df04c0144 --- /dev/null +++ b/clippy_utils/src/msrvs.rs @@ -0,0 +1,29 @@ +use rustc_semver::RustcVersion; + +macro_rules! msrv_aliases { + ($($major:literal,$minor:literal,$patch:literal { + $($name:ident),* $(,)? + })*) => { + $($( + pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch); + )*)* + }; +} + +// names may refer to stabilized feature flags or library items +msrv_aliases! { + 1,53,0 { OR_PATTERNS } + 1,50,0 { BOOL_THEN } + 1,46,0 { CONST_IF_MATCH } + 1,45,0 { STR_STRIP_PREFIX } + 1,42,0 { MATCHES_MACRO } + 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE } + 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } + 1,38,0 { POINTER_CAST } + 1,37,0 { TYPE_ALIAS_ENUM_VARIANTS } + 1,36,0 { ITERATOR_COPIED } + 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } + 1,34,0 { TRY_FROM } + 1,30,0 { ITERATOR_FIND_MAP } + 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } +} -- cgit 1.4.1-3-g733a5 From 572c405da063d2628378b74a8f659ce90e0e7779 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 27 Apr 2021 23:16:19 +0200 Subject: fix ice when checking rustc libstd thread 'rustc' panicked at 'index out of bounds: the len is 0 but the index is 0', src/tools/clippy/clippy_lints/src/matches.rs:1595:53 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace I don't have a minimised testcase because I don't have time to reduce libstd down to a few lines right now. --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 13b2a834b0a..c7a25095bf6 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1590,9 +1590,9 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if_chain! { - if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind; + if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; if is_lang_ctor(cx, qpath, OptionSome); - if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; + if let PatKind::Binding(rb, .., ident, _) = first_pat.kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; -- cgit 1.4.1-3-g733a5 From 32351d6b9f7caa2fd197a23a190fb32392ab9dfd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 28 Apr 2021 14:29:13 -0500 Subject: Remove leftover plugin conf_file code --- clippy_lints/src/lib.rs | 75 +++++++++++++++++------------------------- clippy_lints/src/utils/conf.rs | 23 ------------- src/driver.rs | 2 +- 3 files changed, 31 insertions(+), 69 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c84890299df..57843afaddb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -399,56 +399,41 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { } #[doc(hidden)] -pub fn read_conf(args: &[rustc_ast::NestedMetaItem], sess: &Session) -> Conf { +pub fn read_conf(sess: &Session) -> Conf { use std::path::Path; - match utils::conf::file_from_args(args) { - Ok(file_name) => { - // if the user specified a file, it must exist, otherwise default to `clippy.toml` but - // do not require the file to exist - let file_name = match file_name { - Some(file_name) => file_name, - None => match utils::conf::lookup_conf_file() { - Ok(Some(path)) => path, - Ok(None) => return Conf::default(), - Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) - .emit(); - return Conf::default(); - }, - }, - }; - - let file_name = if file_name.is_relative() { - sess.local_crate_source_file - .as_deref() - .and_then(Path::parent) - .unwrap_or_else(|| Path::new("")) - .join(file_name) - } else { - file_name - }; + let file_name = match utils::conf::lookup_conf_file() { + Ok(Some(path)) => path, + Ok(None) => return Conf::default(), + Err(error) => { + sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) + .emit(); + return Conf::default(); + }, + }; - let (conf, errors) = utils::conf::read(&file_name); + let file_name = if file_name.is_relative() { + sess.local_crate_source_file + .as_deref() + .and_then(Path::parent) + .unwrap_or_else(|| Path::new("")) + .join(file_name) + } else { + file_name + }; - // all conf errors are non-fatal, we just use the default conf in case of error - for error in errors { - sess.struct_err(&format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - error - )) - .emit(); - } + let (conf, errors) = utils::conf::read(&file_name); - conf - }, - Err((err, span)) => { - sess.struct_span_err(span, err) - .span_note(span, "Clippy will use default configuration") - .emit(); - Conf::default() - }, + // all conf errors are non-fatal, we just use the default conf in case of error + for error in errors { + sess.struct_err(&format!( + "error reading Clippy's configuration file `{}`: {}", + file_name.display(), + error + )) + .emit(); } + + conf } /// Register all lints and lint groups with the rustc plugin registry diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index d56855a71c1..e221be63538 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -2,34 +2,11 @@ #![deny(clippy::missing_docs_in_private_items)] -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}; -/// Gets the configuration file from arguments. -pub fn file_from_args(args: &[NestedMetaItem]) -> Result, (&'static str, Span)> { - for arg in args.iter().filter_map(NestedMetaItem::meta_item) { - if arg.has_name(sym!(conf_file)) { - return match arg.kind { - MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)), - MetaItemKind::NameValue(ref value) => { - if let LitKind::Str(ref file, _) = value.kind { - Ok(Some(file.to_string().into())) - } else { - Err(("`conf_file` value must be a string", value.span)) - } - }, - }; - } - } - - Ok(None) -} - /// Error from reading a configuration file. #[derive(Debug)] pub enum Error { diff --git a/src/driver.rs b/src/driver.rs index fa0c5f01430..221474e55d4 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -106,7 +106,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(&[], sess); + let conf = clippy_lints::read_conf(sess); clippy_lints::register_plugins(lint_store, sess, &conf); clippy_lints::register_pre_expansion_lints(lint_store); clippy_lints::register_renamed(lint_store); -- cgit 1.4.1-3-g733a5 From 63425de77db7f4dee55cfceeed7e174bfe986b0d Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 29 Apr 2021 10:10:58 +0200 Subject: while_immutable_cond: check condition for mutation --- clippy_lints/src/loops/while_immutable_condition.rs | 13 ++++++++----- tests/ui/infinite_loop.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index de267cc77d2..55404b87ec9 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -28,11 +28,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' return; } let used_in_condition = &var_visitor.ids; - let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) { - used_in_condition.is_disjoint(&used_mutably) - } else { - return; - }; + let mutated_in_body = mutated_variables(expr, cx); + let mutated_in_condition = mutated_variables(cond, cx); + let no_cond_variable_mutated = + if let (Some(used_mutably_body), Some(used_mutably_cond)) = (mutated_in_body, mutated_in_condition) { + used_in_condition.is_disjoint(&used_mutably_body) && used_in_condition.is_disjoint(&used_mutably_cond) + } else { + return; + }; let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { diff --git a/tests/ui/infinite_loop.rs b/tests/ui/infinite_loop.rs index 72591f12baf..3d8fb8507e5 100644 --- a/tests/ui/infinite_loop.rs +++ b/tests/ui/infinite_loop.rs @@ -192,11 +192,23 @@ fn while_loop_with_break_and_return() { } } +fn immutable_condition_false_positive(mut n: u64) -> u32 { + let mut count = 0; + while { + n >>= 1; + n != 0 + } { + count += 1; + } + count +} + fn main() { immutable_condition(); unused_var(); used_immutable(); internally_mutable(); + immutable_condition_false_positive(5); let mut c = Counter { count: 0 }; c.inc_n(5); -- cgit 1.4.1-3-g733a5 From 0dff377a62b3ae24ceb88ff0a9070f3a4e3d5552 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Fri, 30 Apr 2021 14:40:35 +0900 Subject: use `in_constant` --- clippy_lints/src/comparison_chain.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 150d8a3d673..2a61d58e653 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; -use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; +use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } - if parent_node_is_if_const_fn(cx, expr) { + if in_constant(cx, expr.hir_id) { return; } @@ -127,11 +127,3 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { fn kind_is_cmp(kind: BinOpKind) -> bool { matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } - -fn parent_node_is_if_const_fn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.tcx.hir().find(cx.tcx.hir().get_parent_item(expr.hir_id)) { - Some(Node::Item(item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, item.def_id.to_def_id()), - Some(Node::ImplItem(impl_item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, impl_item.def_id.to_def_id()), - _ => false, - } -} -- cgit 1.4.1-3-g733a5 From b9c8e683d609d204fd1192e236a92099d94cbef2 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 27 Apr 2021 21:03:48 -0500 Subject: Disable default_trait_access in macros --- clippy_lints/src/default.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 710da8fe9e0..7a53d390bb4 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; +use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -75,6 +75,7 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); impl LateLintPass<'_> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { + if !in_macro(expr.span); // Avoid cases already linted by `field_reassign_with_default` if !self.reassigned_linted.contains(&expr.span); if let ExprKind::Call(path, ..) = expr.kind; -- cgit 1.4.1-3-g733a5 From 1e22e564e4d272c296a6acb8596831fbaa05bc7b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 27 Apr 2021 21:04:06 -0500 Subject: Refactor config deserialization --- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/utils/conf.rs | 232 +++++++++------------ tests/ui-toml/bad_toml_type/conf_bad_type.stderr | 2 +- .../conf_deprecated_key/conf_deprecated_key.stderr | 2 +- 4 files changed, 108 insertions(+), 132 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 57843afaddb..40a793e48cf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -382,6 +382,7 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; +use crate::utils::conf::TryConf; /// Register all pre expansion lints /// @@ -421,8 +422,7 @@ pub fn read_conf(sess: &Session) -> Conf { file_name }; - let (conf, errors) = utils::conf::read(&file_name); - + let TryConf { conf, errors } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { sess.struct_err(&format!( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e221be63538..747d92dd742 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -1,98 +1,111 @@ //! Read configurations files. -#![deny(clippy::missing_docs_in_private_items)] +#![allow(clippy::module_name_repetitions)] -use std::lazy::SyncLazy; +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; +use serde::Deserialize; +use std::error::Error; use std::path::{Path, PathBuf}; -use std::sync::Mutex; use std::{env, fmt, fs, io}; -/// Error from reading a configuration file. -#[derive(Debug)] -pub enum Error { - /// An I/O error. - Io(io::Error), - /// Not valid toml or doesn't fit the expected config format - Toml(String), +/// Conf with parse errors +#[derive(Default)] +pub struct TryConf { + pub conf: Conf, + pub errors: Vec, } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(err) => err.fmt(f), - Self::Toml(err) => err.fmt(f), +impl TryConf { + fn from_error(error: impl Error) -> Self { + Self { + conf: Conf::default(), + errors: vec![error.to_string()], } } } -impl From for Error { - fn from(e: io::Error) -> Self { - Self::Io(e) - } -} +macro_rules! define_Conf { + ($( + #[$doc:meta] + $(#[conf_deprecated($dep:literal)])? + ($name:ident: $ty:ty $(= $default:expr)?), + )*) => { + /// Clippy lint configuration + pub struct Conf { + $(#[$doc] pub $name: $ty,)* + } -/// Vec of errors that might be collected during config toml parsing -static ERRORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); + mod defaults { + $(pub fn $name() -> $ty { define_Conf!(@default $($default)?) })* + } -macro_rules! define_Conf { - ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { - mod helpers { - use serde::Deserialize; - /// Type used to store lint configuration. - #[derive(Deserialize)] - #[serde(rename_all = "kebab-case", deny_unknown_fields)] - pub struct Conf { - $( - #[$doc] - #[serde(default = $config_str)] - #[serde(with = $config_str)] - pub $config: $Ty, - )+ - #[allow(dead_code)] - #[serde(default)] - third_party: Option<::toml::Value>, + impl Default for Conf { + fn default() -> Self { + Self { $($name: defaults::$name(),)* } } + } - $( - mod $config { - 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 - .lock() - .expect("no threading here") - .push(Error::Toml(e.to_string())); - super::$config() - }) - ) - } - } + impl<'de> Deserialize<'de> for TryConf { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + deserializer.deserialize_map(ConfVisitor) + } + } + + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "kebab-case")] + #[allow(non_camel_case_types)] + enum Field { $($name,)* third_party, } + + struct ConfVisitor; + + impl<'de> Visitor<'de> for ConfVisitor { + type Value = TryConf; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Conf") + } - #[must_use] - fn $config() -> $Ty { - let x = $default; - x + fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de> { + let mut errors = Vec::new(); + $(let mut $name = None;)* + // could get `Field` here directly, but get `str` first for diagnostics + while let Some(name) = map.next_key::<&str>()? { + match Field::deserialize(name.into_deserializer())? { + $(Field::$name => { + $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)? + match map.next_value() { + Err(e) => errors.push(e.to_string()), + Ok(value) => match $name { + Some(_) => errors.push(format!("duplicate field `{}`", name)), + None => $name = Some(value), + } + } + })* + // white-listed; ignore + Field::third_party => drop(map.next_value::()) + } } - )+ + let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; + Ok(TryConf { conf, errors }) + } } }; + (@default) => (Default::default()); + (@default $default:expr) => ($default); } -pub use self::helpers::Conf; define_Conf! { /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports - (msrv, "msrv": Option, None), + (msrv: Option), /// 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()), + (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), + (cognitive_complexity_threshold: u64 = 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. - (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option, None), + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead.")] + (cyclomatic_complexity_threshold: Option), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks - (doc_valid_idents, "doc_valid_idents": Vec, [ + (doc_valid_idents: Vec = [ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", @@ -113,54 +126,47 @@ define_Conf! { "CamelCase", ].iter().map(ToString::to_string).collect()), /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have - (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7), + (too_many_arguments_threshold: u64 = 7), /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have - (type_complexity_threshold, "type_complexity_threshold": u64, 250), + (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), + (single_char_binding_names_threshold: u64 = 4), /// 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), + (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), + (enum_variant_name_threshold: u64 = 3), /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion - (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200), + (enum_variant_size_threshold: u64 = 200), /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' - (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1), + (verbose_bit_mask_threshold: u64 = 1), /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals - (literal_representation_threshold, "literal_representation_threshold": u64, 16384), + (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), + (trivial_copy_size_limit: Option), /// 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), + (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), + (too_many_lines_threshold: u64 = 100), /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack - (array_size_threshold, "array_size_threshold": u64, 512_000), + (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), + (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), + (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), + (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), + (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), + (warn_on_all_wildcard_imports: bool), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. - (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), + (disallowed_methods: Vec), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. - (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true), + (unreadable_literal_lint_fractions: bool = true), /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other - (upper_case_acronyms_aggressive, "upper_case_acronyms_aggressive": bool, false), + (upper_case_acronyms_aggressive: bool), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish, "cargo_ignore_publish": bool, false), -} - -impl Default for Conf { - #[must_use] - fn default() -> Self { - toml::from_str("").expect("we never error on empty config files") - } + (cargo_ignore_publish: bool), } /// Search for the configuration file. @@ -194,43 +200,13 @@ pub fn lookup_conf_file() -> io::Result> { } } -/// Produces a `Conf` filled with the default values and forwards the errors -/// -/// Used internally for convenience -fn default(errors: Vec) -> (Conf, Vec) { - (Conf::default(), errors) -} - /// Read the `toml` configuration file. /// /// In case of error, the function tries to continue as much as possible. -pub fn read(path: &Path) -> (Conf, Vec) { +pub fn read(path: &Path) -> TryConf { let content = match fs::read_to_string(path) { + Err(e) => return TryConf::from_error(e), Ok(content) => content, - Err(err) => return default(vec![err.into()]), }; - - assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty()); - match toml::from_str(&content) { - Ok(toml) => { - let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); - - let toml_ref: &Conf = &toml; - - let cyc_field: Option = toml_ref.cyclomatic_complexity_threshold; - - if cyc_field.is_some() { - let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string(); - errors.push(Error::Toml(cyc_err)); - } - - (toml, errors) - }, - Err(e) => { - let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); - errors.push(Error::Toml(e.to_string())); - - default(errors) - }, - } + toml::from_str(&content).unwrap_or_else(TryConf::from_error) } diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr index efd02bcbb6e..c7bc261de6c 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence +error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names` error: aborting due to previous error diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 34267c0daf7..8bf9fe64c6d 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. +error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 857d9f15da507adbc3e59560f3c15568cd0ae0e5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 2 May 2021 16:56:46 -0500 Subject: Fix error punctuation --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 747d92dd742..aeca5b9ccce 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -102,7 +102,7 @@ define_Conf! { /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have (cognitive_complexity_threshold: u64 = 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. - #[conf_deprecated("Please use `cognitive-complexity-threshold` instead.")] + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] (cyclomatic_complexity_threshold: Option), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks (doc_valid_idents: Vec = [ diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 8bf9fe64c6d..90021a034a3 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. +error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 499813026f1e84456c9a108b249ed1005c624181 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 2 May 2021 17:50:22 -0500 Subject: Fix config parsing --- clippy_lints/src/utils/conf.rs | 21 ++++++++++----------- util/lintlib.py | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index aeca5b9ccce..dc079663fc9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -28,7 +28,7 @@ macro_rules! define_Conf { ($( #[$doc:meta] $(#[conf_deprecated($dep:literal)])? - ($name:ident: $ty:ty $(= $default:expr)?), + ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { @@ -36,7 +36,7 @@ macro_rules! define_Conf { } mod defaults { - $(pub fn $name() -> $ty { define_Conf!(@default $($default)?) })* + $(pub fn $name() -> $ty { $default })* } impl Default for Conf { @@ -90,20 +90,19 @@ macro_rules! define_Conf { } } }; - (@default) => (Default::default()); - (@default $default:expr) => ($default); } +// N.B., this macro is parsed by util/lintlib.py define_Conf! { /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports - (msrv: Option), + (msrv: Option = None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have (cognitive_complexity_threshold: u64 = 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] - (cyclomatic_complexity_threshold: Option), + (cyclomatic_complexity_threshold: Option = None), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks (doc_valid_idents: Vec = [ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", @@ -142,7 +141,7 @@ define_Conf! { /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals (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: Option), + (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: u64 = 256), /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have @@ -158,15 +157,15 @@ define_Conf! { /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have (max_fn_params_bools: u64 = 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). - (warn_on_all_wildcard_imports: bool), + (warn_on_all_wildcard_imports: bool = false), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec), + (disallowed_methods: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. (unreadable_literal_lint_fractions: bool = true), /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other - (upper_case_acronyms_aggressive: bool), + (upper_case_acronyms_aggressive: bool = false), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish: bool), + (cargo_ignore_publish: bool = false), } /// Search for the configuration file. diff --git a/util/lintlib.py b/util/lintlib.py index d0d9beb9b2d..5707cf0ce0f 100644 --- a/util/lintlib.py +++ b/util/lintlib.py @@ -14,7 +14,7 @@ lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''') group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''') conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE) confvar_re = re.compile( - r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE) + r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE) comment_re = re.compile(r'''\s*/// ?(.*)''') lint_levels = { -- cgit 1.4.1-3-g733a5 From c0808998481402cb1cd502ad6aa54e253840efc6 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 3 May 2021 16:18:41 +0200 Subject: add strip_{prefix,suffix} to PATTERN_METHODS this will warn, if a single_char_pattern is used in one of the above methods --- clippy_lints/src/methods/mod.rs | 4 +++- tests/ui/single_char_pattern.fixed | 2 ++ tests/ui/single_char_pattern.rs | 2 ++ tests/ui/single_char_pattern.stderr | 34 +++++++++++++++++++++++----------- 4 files changed, 30 insertions(+), 12 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e15dbb899b3..5ea989aa684 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2190,7 +2190,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ]; #[rustfmt::skip] -const PATTERN_METHODS: [(&str, usize); 17] = [ +const PATTERN_METHODS: [(&str, usize); 19] = [ ("contains", 1), ("starts_with", 1), ("ends_with", 1), @@ -2206,6 +2206,8 @@ const PATTERN_METHODS: [(&str, usize); 17] = [ ("rmatches", 1), ("match_indices", 1), ("rmatch_indices", 1), + ("strip_prefix", 1), + ("strip_suffix", 1), ("trim_start_matches", 1), ("trim_end_matches", 1), ]; diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index d8b5f19e144..fcbe9af9f56 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -33,6 +33,8 @@ fn main() { x.rmatch_indices('x'); x.trim_start_matches('x'); x.trim_end_matches('x'); + x.strip_prefix('x'); + x.strip_suffix('x'); // Make sure we escape characters correctly. x.split('\n'); x.split('\''); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index a7bc73e3756..b8bc20f4070 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -33,6 +33,8 @@ fn main() { x.rmatch_indices("x"); x.trim_start_matches("x"); x.trim_end_matches("x"); + x.strip_prefix("x"); + x.strip_suffix("x"); // Make sure we escape characters correctly. x.split("\n"); x.split("'"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index ee4e7e50efd..6d94d8a34e3 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -121,64 +121,76 @@ LL | x.trim_end_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:37:13 + --> $DIR/single_char_pattern.rs:36:20 + | +LL | x.strip_prefix("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:37:20 + | +LL | x.strip_suffix("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:39:13 | LL | x.split("/n"); | ^^^^ help: try using a `char` instead: `'/n'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:38:13 + --> $DIR/single_char_pattern.rs:40:13 | LL | x.split("'"); | ^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:39:13 + --> $DIR/single_char_pattern.rs:41:13 | LL | x.split("/'"); | ^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:44:31 + --> $DIR/single_char_pattern.rs:46:31 | LL | x.replace(";", ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:19 + --> $DIR/single_char_pattern.rs:47:19 | LL | x.starts_with("/x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'/x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:52:13 + --> $DIR/single_char_pattern.rs:54:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:53:13 + --> $DIR/single_char_pattern.rs:55:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:54:13 + --> $DIR/single_char_pattern.rs:56:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:55:13 + --> $DIR/single_char_pattern.rs:57:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:56:13 + --> $DIR/single_char_pattern.rs:58:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` -error: aborting due to 30 previous errors +error: aborting due to 32 previous errors -- cgit 1.4.1-3-g733a5 From 19e7448c7f6a4e3a07507c2742bef07d56f1a4a3 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 3 May 2021 16:21:27 +0200 Subject: move PATTERN_METHODS table directly into file also removed rustfmt::skip --- clippy_lints/src/methods/mod.rs | 23 ----------------------- clippy_lints/src/methods/single_char_pattern.rs | 24 +++++++++++++++++++++++- 2 files changed, 23 insertions(+), 24 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5ea989aa684..0b1b6304def 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2189,29 +2189,6 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; -#[rustfmt::skip] -const PATTERN_METHODS: [(&str, usize); 19] = [ - ("contains", 1), - ("starts_with", 1), - ("ends_with", 1), - ("find", 1), - ("rfind", 1), - ("split", 1), - ("rsplit", 1), - ("split_terminator", 1), - ("rsplit_terminator", 1), - ("splitn", 2), - ("rsplitn", 2), - ("matches", 1), - ("rmatches", 1), - ("match_indices", 1), - ("rmatch_indices", 1), - ("strip_prefix", 1), - ("strip_suffix", 1), - ("trim_start_matches", 1), - ("trim_end_matches", 1), -]; - #[derive(Clone, Copy, PartialEq, Debug)] enum SelfKind { Value, diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index f4090c7c617..d313a3db479 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -9,9 +9,31 @@ use rustc_span::symbol::Symbol; use super::SINGLE_CHAR_PATTERN; +const PATTERN_METHODS: [(&str, usize); 19] = [ + ("contains", 1), + ("starts_with", 1), + ("ends_with", 1), + ("find", 1), + ("rfind", 1), + ("split", 1), + ("rsplit", 1), + ("split_terminator", 1), + ("rsplit_terminator", 1), + ("splitn", 2), + ("rsplitn", 2), + ("matches", 1), + ("rmatches", 1), + ("match_indices", 1), + ("rmatch_indices", 1), + ("strip_prefix", 1), + ("strip_suffix", 1), + ("trim_start_matches", 1), + ("trim_end_matches", 1), +]; + /// lint for length-1 `str`s for methods in `PATTERN_METHODS` pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { - for &(method, pos) in &crate::methods::PATTERN_METHODS { + for &(method, pos) in &PATTERN_METHODS { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind(); if *ty.kind() == ty::Str; -- cgit 1.4.1-3-g733a5 From 91a8611b4426c7da8c72317516626affb812d20b Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 16:38:34 +0900 Subject: move misc_early to misc_early/mod.rs --- clippy_lints/src/misc_early.rs | 569 ------------------------------------- clippy_lints/src/misc_early/mod.rs | 569 +++++++++++++++++++++++++++++++++++++ 2 files changed, 569 insertions(+), 569 deletions(-) delete mode 100644 clippy_lints/src/misc_early.rs create mode 100644 clippy_lints/src/misc_early/mod.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs deleted file mode 100644 index 3c6a7071c24..00000000000 --- a/clippy_lints/src/misc_early.rs +++ /dev/null @@ -1,569 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{ - BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, UnOp, -}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; -use rustc_hir::PrimTy; -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; - -declare_clippy_lint! { - /// **What it does:** Checks for structure field patterns bound to wildcards. - /// - /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on - /// the fields that are actually bound. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```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, - "struct fields bound to a wildcard instead of using `..`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for function arguments having the similar names - /// differing by an underscore. - /// - /// **Why is this bad?** It affects code readability. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // Bad - /// fn foo(a: i32, _a: i32) {} - /// - /// // Good - /// fn bar(a: i32, _b: i32) {} - /// ``` - pub DUPLICATE_UNDERSCORE_ARGUMENT, - style, - "function arguments having names which only differ by an underscore" -} - -declare_clippy_lint! { - /// **What it does:** Detects expressions of the form `--x`. - /// - /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was - /// decremented. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let mut x = 3; - /// --x; - /// ``` - pub DOUBLE_NEG, - style, - "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++" -} - -declare_clippy_lint! { - /// **What it does:** Warns on hexadecimal literals with mixed-case letter - /// digits. - /// - /// **Why is this bad?** It looks confusing. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // Bad - /// let y = 0x1a9BAcD; - /// - /// // Good - /// let y = 0x1A9BACD; - /// ``` - pub MIXED_CASE_HEX_LITERALS, - style, - "hex literals whose letter digits are not consistently upper- or lowercased" -} - -declare_clippy_lint! { - /// **What it does:** Warns if literal suffixes are not separated by an - /// underscore. - /// - /// **Why is this bad?** It is much less readable. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // Bad - /// let y = 123832i32; - /// - /// // Good - /// let y = 123832_i32; - /// ``` - pub UNSEPARATED_LITERAL_SUFFIX, - pedantic, - "literals whose suffix is not separated by an underscore" -} - -declare_clippy_lint! { - /// **What it does:** Warns if an integral constant literal starts with `0`. - /// - /// **Why is this bad?** In some languages (including the infamous C language - /// and most of its - /// family), this marks an octal constant. In Rust however, this is a decimal - /// constant. This could - /// be confusing for both the writer and a reader of the constant. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// In Rust: - /// ```rust - /// fn main() { - /// let a = 0123; - /// println!("{}", a); - /// } - /// ``` - /// - /// prints `123`, while in C: - /// - /// ```c - /// #include - /// - /// int main() { - /// int a = 0123; - /// printf("%d\n", a); - /// } - /// ``` - /// - /// prints `83` (as `83 == 0o123` while `123 == 0o173`). - pub ZERO_PREFIXED_LITERAL, - complexity, - "integer literals starting with `0`" -} - -declare_clippy_lint! { - /// **What it does:** Warns if a generic shadows a built-in type. - /// - /// **Why is this bad?** This gives surprising type errors. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```ignore - /// impl Foo { - /// fn impl_func(&self) -> u32 { - /// 42 - /// } - /// } - /// ``` - pub BUILTIN_TYPE_SHADOW, - style, - "shadowing a builtin type" -} - -declare_clippy_lint! { - /// **What it does:** Checks for patterns in the form `name @ _`. - /// - /// **Why is this bad?** It's almost always more readable to just use direct - /// bindings. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # let v = Some("abc"); - /// - /// // Bad - /// match v { - /// Some(x) => (), - /// y @ _ => (), - /// } - /// - /// // Good - /// match v { - /// Some(x) => (), - /// y => (), - /// } - /// ``` - pub REDUNDANT_PATTERN, - style, - "using `name @ _` in a pattern" -} - -declare_clippy_lint! { - /// **What it does:** Checks for tuple patterns with a wildcard - /// pattern (`_`) is next to a rest pattern (`..`). - /// - /// _NOTE_: While `_, ..` means there is at least one element left, `..` - /// means there are 0 or more elements left. This can make a difference - /// when refactoring, but shouldn't result in errors in the refactored code, - /// since the wildcard pattern isn't used anyway. - /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern - /// can match that element as well. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # struct TupleStruct(u32, u32, u32); - /// # let t = TupleStruct(1, 2, 3); - /// // Bad - /// match t { - /// TupleStruct(0, .., _) => (), - /// _ => (), - /// } - /// - /// // Good - /// match t { - /// TupleStruct(0, ..) => (), - /// _ => (), - /// } - /// ``` - pub UNNEEDED_WILDCARD_PATTERN, - complexity, - "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)" -} - -declare_lint_pass!(MiscEarlyLints => [ - UNNEEDED_FIELD_PATTERN, - DUPLICATE_UNDERSCORE_ARGUMENT, - DOUBLE_NEG, - MIXED_CASE_HEX_LITERALS, - UNSEPARATED_LITERAL_SUFFIX, - ZERO_PREFIXED_LITERAL, - BUILTIN_TYPE_SHADOW, - REDUNDANT_PATTERN, - UNNEEDED_WILDCARD_PATTERN, -]); - -impl EarlyLintPass for MiscEarlyLints { - fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { - for param in &gen.params { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - &format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } - } - } - } - - fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { - let mut wilds = 0; - let type_name = npat - .segments - .last() - .expect("A path must have at least one segment") - .ident - .name; - - for field in pfields { - if let PatKind::Wild = field.pat.kind { - wilds += 1; - } - } - if !pfields.is_empty() && wilds == pfields.len() { - span_lint_and_help( - cx, - UNNEEDED_FIELD_PATTERN, - pat.span, - "all the struct fields are matched to a wildcard pattern, consider using `..`", - None, - &format!("try with `{} {{ .. }}` instead", type_name), - ); - return; - } - if wilds > 0 { - for field in pfields { - if let PatKind::Wild = field.pat.kind { - wilds -= 1; - if wilds > 0 { - span_lint( - cx, - UNNEEDED_FIELD_PATTERN, - field.span, - "you matched a field with a wildcard pattern, consider using `..` instead", - ); - } else { - let mut normal = vec![]; - - for field in pfields { - match field.pat.kind { - PatKind::Wild => {}, - _ => { - if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { - normal.push(n); - } - }, - } - } - - span_lint_and_help( - cx, - UNNEEDED_FIELD_PATTERN, - field.span, - "you matched a field with a wildcard pattern, consider using `..` \ - instead", - None, - &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), - ); - } - } - } - } - } - - if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { - let left_binding = match left { - BindingMode::ByRef(Mutability::Mut) => "ref mut ", - BindingMode::ByRef(Mutability::Not) => "ref ", - BindingMode::ByValue(..) => "", - }; - - if let PatKind::Wild = right.kind { - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN, - pat.span, - &format!( - "the `{} @ _` pattern can be written as just `{}`", - ident.name, ident.name, - ), - "try", - format!("{}{}", left_binding, ident.name), - Applicability::MachineApplicable, - ); - } - } - - check_unneeded_wildcard_pattern(cx, pat); - } - - fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - let mut registered_names: FxHashMap = FxHashMap::default(); - - for arg in &fn_kind.decl().inputs { - if let PatKind::Ident(_, ident, None) = arg.pat.kind { - let arg_name = ident.to_string(); - - if let Some(arg_name) = arg_name.strip_prefix('_') { - if let Some(correspondence) = registered_names.get(arg_name) { - span_lint( - cx, - DUPLICATE_UNDERSCORE_ARGUMENT, - *correspondence, - &format!( - "`{}` already exists, having another argument having almost the same \ - name makes code comprehension and documentation more difficult", - arg_name - ), - ); - } - } else { - registered_names.insert(arg_name, arg.pat.span); - } - } - } - } - - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - match expr.kind { - ExprKind::Unary(UnOp::Neg, ref inner) => { - if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { - span_lint( - cx, - DOUBLE_NEG, - expr.span, - "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op", - ); - } - }, - ExprKind::Lit(ref lit) => Self::check_lit(cx, lit), - _ => (), - } - } -} - -impl MiscEarlyLints { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { - // We test if first character in snippet is a number, because the snippet could be an expansion - // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. - // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. - // See for a regression. - // FIXME: Find a better way to detect those cases. - let lit_snip = match snippet_opt(cx, lit.span) { - Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, - _ => return, - }; - - if let LitKind::Int(value, lit_int_type) = lit.kind { - let suffix = match lit_int_type { - LitIntType::Signed(ty) => ty.name_str(), - LitIntType::Unsigned(ty) => ty.name_str(), - LitIntType::Unsuffixed => "", - }; - - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { - return; // It's useless so shouldn't lint. - }; - // Do not lint when literal is unsuffixed. - if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { - span_lint_and_sugg( - cx, - UNSEPARATED_LITERAL_SUFFIX, - lit.span, - "integer type suffix should be separated by an underscore", - "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), - Applicability::MachineApplicable, - ); - } - - if lit_snip.starts_with("0x") { - if maybe_last_sep_idx <= 2 { - // It's meaningless or causes range error. - return; - } - let mut seen = (false, false); - for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() { - match ch { - b'a'..=b'f' => seen.0 = true, - b'A'..=b'F' => seen.1 = true, - _ => {}, - } - if seen.0 && seen.1 { - span_lint( - cx, - MIXED_CASE_HEX_LITERALS, - lit.span, - "inconsistent casing in hexadecimal literal", - ); - break; - } - } - } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { - /* nothing to do */ - } else if value != 0 && lit_snip.starts_with('0') { - span_lint_and_then( - cx, - ZERO_PREFIXED_LITERAL, - lit.span, - "this is a decimal constant", - |diag| { - diag.span_suggestion( - lit.span, - "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), - Applicability::MaybeIncorrect, - ); - }, - ); - } - } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { - let suffix = float_ty.name_str(); - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { - return; // It's useless so shouldn't lint. - }; - if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { - span_lint_and_sugg( - cx, - UNSEPARATED_LITERAL_SUFFIX, - lit.span, - "float type suffix should be separated by an underscore", - "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), - Applicability::MachineApplicable, - ); - } - } - } -} - -fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { - fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { - span_lint_and_sugg( - cx, - UNNEEDED_WILDCARD_PATTERN, - span, - if only_one { - "this pattern is unneeded as the `..` pattern can match that element" - } else { - "these patterns are unneeded as the `..` pattern can match those elements" - }, - if only_one { "remove it" } else { "remove them" }, - "".to_string(), - Applicability::MachineApplicable, - ); - } - - 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(|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(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint( - cx, - patterns[rest_index].span.shrink_to_hi().to(right_pat.span), - right_index == 0, - ); - } - } - } -} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs new file mode 100644 index 00000000000..3c6a7071c24 --- /dev/null +++ b/clippy_lints/src/misc_early/mod.rs @@ -0,0 +1,569 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use rustc_ast::ast::{ + BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, + NodeId, Pat, PatKind, UnOp, +}; +use rustc_ast::visit::FnKind; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::PrimTy; +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; + +declare_clippy_lint! { + /// **What it does:** Checks for structure field patterns bound to wildcards. + /// + /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on + /// the fields that are actually bound. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```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, + "struct fields bound to a wildcard instead of using `..`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for function arguments having the similar names + /// differing by an underscore. + /// + /// **Why is this bad?** It affects code readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// fn foo(a: i32, _a: i32) {} + /// + /// // Good + /// fn bar(a: i32, _b: i32) {} + /// ``` + pub DUPLICATE_UNDERSCORE_ARGUMENT, + style, + "function arguments having names which only differ by an underscore" +} + +declare_clippy_lint! { + /// **What it does:** Detects expressions of the form `--x`. + /// + /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was + /// decremented. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let mut x = 3; + /// --x; + /// ``` + pub DOUBLE_NEG, + style, + "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++" +} + +declare_clippy_lint! { + /// **What it does:** Warns on hexadecimal literals with mixed-case letter + /// digits. + /// + /// **Why is this bad?** It looks confusing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let y = 0x1a9BAcD; + /// + /// // Good + /// let y = 0x1A9BACD; + /// ``` + pub MIXED_CASE_HEX_LITERALS, + style, + "hex literals whose letter digits are not consistently upper- or lowercased" +} + +declare_clippy_lint! { + /// **What it does:** Warns if literal suffixes are not separated by an + /// underscore. + /// + /// **Why is this bad?** It is much less readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let y = 123832i32; + /// + /// // Good + /// let y = 123832_i32; + /// ``` + pub UNSEPARATED_LITERAL_SUFFIX, + pedantic, + "literals whose suffix is not separated by an underscore" +} + +declare_clippy_lint! { + /// **What it does:** Warns if an integral constant literal starts with `0`. + /// + /// **Why is this bad?** In some languages (including the infamous C language + /// and most of its + /// family), this marks an octal constant. In Rust however, this is a decimal + /// constant. This could + /// be confusing for both the writer and a reader of the constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// In Rust: + /// ```rust + /// fn main() { + /// let a = 0123; + /// println!("{}", a); + /// } + /// ``` + /// + /// prints `123`, while in C: + /// + /// ```c + /// #include + /// + /// int main() { + /// int a = 0123; + /// printf("%d\n", a); + /// } + /// ``` + /// + /// prints `83` (as `83 == 0o123` while `123 == 0o173`). + pub ZERO_PREFIXED_LITERAL, + complexity, + "integer literals starting with `0`" +} + +declare_clippy_lint! { + /// **What it does:** Warns if a generic shadows a built-in type. + /// + /// **Why is this bad?** This gives surprising type errors. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```ignore + /// impl Foo { + /// fn impl_func(&self) -> u32 { + /// 42 + /// } + /// } + /// ``` + pub BUILTIN_TYPE_SHADOW, + style, + "shadowing a builtin type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for patterns in the form `name @ _`. + /// + /// **Why is this bad?** It's almost always more readable to just use direct + /// bindings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let v = Some("abc"); + /// + /// // Bad + /// match v { + /// Some(x) => (), + /// y @ _ => (), + /// } + /// + /// // Good + /// match v { + /// Some(x) => (), + /// y => (), + /// } + /// ``` + pub REDUNDANT_PATTERN, + style, + "using `name @ _` in a pattern" +} + +declare_clippy_lint! { + /// **What it does:** Checks for tuple patterns with a wildcard + /// pattern (`_`) is next to a rest pattern (`..`). + /// + /// _NOTE_: While `_, ..` means there is at least one element left, `..` + /// means there are 0 or more elements left. This can make a difference + /// when refactoring, but shouldn't result in errors in the refactored code, + /// since the wildcard pattern isn't used anyway. + /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern + /// can match that element as well. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct TupleStruct(u32, u32, u32); + /// # let t = TupleStruct(1, 2, 3); + /// // Bad + /// match t { + /// TupleStruct(0, .., _) => (), + /// _ => (), + /// } + /// + /// // Good + /// match t { + /// TupleStruct(0, ..) => (), + /// _ => (), + /// } + /// ``` + pub UNNEEDED_WILDCARD_PATTERN, + complexity, + "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)" +} + +declare_lint_pass!(MiscEarlyLints => [ + UNNEEDED_FIELD_PATTERN, + DUPLICATE_UNDERSCORE_ARGUMENT, + DOUBLE_NEG, + MIXED_CASE_HEX_LITERALS, + UNSEPARATED_LITERAL_SUFFIX, + ZERO_PREFIXED_LITERAL, + BUILTIN_TYPE_SHADOW, + REDUNDANT_PATTERN, + UNNEEDED_WILDCARD_PATTERN, +]); + +impl EarlyLintPass for MiscEarlyLints { + fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { + for param in &gen.params { + if let GenericParamKind::Type { .. } = param.kind { + if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + &format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); + } + } + } + } + + fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { + let mut wilds = 0; + let type_name = npat + .segments + .last() + .expect("A path must have at least one segment") + .ident + .name; + + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds += 1; + } + } + if !pfields.is_empty() && wilds == pfields.len() { + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + pat.span, + "all the struct fields are matched to a wildcard pattern, consider using `..`", + None, + &format!("try with `{} {{ .. }}` instead", type_name), + ); + return; + } + if wilds > 0 { + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds -= 1; + if wilds > 0 { + span_lint( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "you matched a field with a wildcard pattern, consider using `..` instead", + ); + } else { + let mut normal = vec![]; + + for field in pfields { + match field.pat.kind { + PatKind::Wild => {}, + _ => { + if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { + normal.push(n); + } + }, + } + } + + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "you matched a field with a wildcard pattern, consider using `..` \ + instead", + None, + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + ); + } + } + } + } + } + + if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { + let left_binding = match left { + BindingMode::ByRef(Mutability::Mut) => "ref mut ", + BindingMode::ByRef(Mutability::Not) => "ref ", + BindingMode::ByValue(..) => "", + }; + + if let PatKind::Wild = right.kind { + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN, + pat.span, + &format!( + "the `{} @ _` pattern can be written as just `{}`", + ident.name, ident.name, + ), + "try", + format!("{}{}", left_binding, ident.name), + Applicability::MachineApplicable, + ); + } + } + + check_unneeded_wildcard_pattern(cx, pat); + } + + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + let mut registered_names: FxHashMap = FxHashMap::default(); + + for arg in &fn_kind.decl().inputs { + if let PatKind::Ident(_, ident, None) = arg.pat.kind { + let arg_name = ident.to_string(); + + if let Some(arg_name) = arg_name.strip_prefix('_') { + if let Some(correspondence) = registered_names.get(arg_name) { + span_lint( + cx, + DUPLICATE_UNDERSCORE_ARGUMENT, + *correspondence, + &format!( + "`{}` already exists, having another argument having almost the same \ + name makes code comprehension and documentation more difficult", + arg_name + ), + ); + } + } else { + registered_names.insert(arg_name, arg.pat.span); + } + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + match expr.kind { + ExprKind::Unary(UnOp::Neg, ref inner) => { + if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { + span_lint( + cx, + DOUBLE_NEG, + expr.span, + "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op", + ); + } + }, + ExprKind::Lit(ref lit) => Self::check_lit(cx, lit), + _ => (), + } + } +} + +impl MiscEarlyLints { + fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + // We test if first character in snippet is a number, because the snippet could be an expansion + // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. + // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. + // See for a regression. + // FIXME: Find a better way to detect those cases. + let lit_snip = match snippet_opt(cx, lit.span) { + Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, + _ => return, + }; + + if let LitKind::Int(value, lit_int_type) = lit.kind { + let suffix = match lit_int_type { + LitIntType::Signed(ty) => ty.name_str(), + LitIntType::Unsigned(ty) => ty.name_str(), + LitIntType::Unsuffixed => "", + }; + + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; + // Do not lint when literal is unsuffixed. + if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + span_lint_and_sugg( + cx, + UNSEPARATED_LITERAL_SUFFIX, + lit.span, + "integer type suffix should be separated by an underscore", + "add an underscore", + format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + Applicability::MachineApplicable, + ); + } + + if lit_snip.starts_with("0x") { + if maybe_last_sep_idx <= 2 { + // It's meaningless or causes range error. + return; + } + let mut seen = (false, false); + for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() { + match ch { + b'a'..=b'f' => seen.0 = true, + b'A'..=b'F' => seen.1 = true, + _ => {}, + } + if seen.0 && seen.1 { + span_lint( + cx, + MIXED_CASE_HEX_LITERALS, + lit.span, + "inconsistent casing in hexadecimal literal", + ); + break; + } + } + } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { + /* nothing to do */ + } else if value != 0 && lit_snip.starts_with('0') { + span_lint_and_then( + cx, + ZERO_PREFIXED_LITERAL, + lit.span, + "this is a decimal constant", + |diag| { + diag.span_suggestion( + lit.span, + "if you mean to use a decimal constant, remove the `0` to avoid confusion", + lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { + let suffix = float_ty.name_str(); + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; + if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + span_lint_and_sugg( + cx, + UNSEPARATED_LITERAL_SUFFIX, + lit.span, + "float type suffix should be separated by an underscore", + "add an underscore", + format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { + fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { + span_lint_and_sugg( + cx, + UNNEEDED_WILDCARD_PATTERN, + span, + if only_one { + "this pattern is unneeded as the `..` pattern can match that element" + } else { + "these patterns are unneeded as the `..` pattern can match those elements" + }, + if only_one { "remove it" } else { "remove them" }, + "".to_string(), + Applicability::MachineApplicable, + ); + } + + 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(|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(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint( + cx, + patterns[rest_index].span.shrink_to_hi().to(right_pat.span), + right_index == 0, + ); + } + } + } +} -- cgit 1.4.1-3-g733a5 From 64eb18e6759f4375d01d7e3352447ff6b4305184 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:04:10 +0900 Subject: move builtin_type_shadow to its own module --- clippy_lints/src/misc_early/builtin_type_shadow.rs | 19 +++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 18 +++++----------- tests/ui/builtin-type-shadow.rs | 9 -------- tests/ui/builtin-type-shadow.stderr | 24 ---------------------- tests/ui/builtin_type_shadow.rs | 9 ++++++++ tests/ui/builtin_type_shadow.stderr | 24 ++++++++++++++++++++++ 6 files changed, 57 insertions(+), 46 deletions(-) create mode 100644 clippy_lints/src/misc_early/builtin_type_shadow.rs delete mode 100644 tests/ui/builtin-type-shadow.rs delete mode 100644 tests/ui/builtin-type-shadow.stderr create mode 100644 tests/ui/builtin_type_shadow.rs create mode 100644 tests/ui/builtin_type_shadow.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/builtin_type_shadow.rs b/clippy_lints/src/misc_early/builtin_type_shadow.rs new file mode 100644 index 00000000000..9f6b0bdc7a4 --- /dev/null +++ b/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -0,0 +1,19 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_hir::PrimTy; +use rustc_lint::EarlyContext; + +use super::BUILTIN_TYPE_SHADOW; + +pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { + if let GenericParamKind::Type { .. } = param.kind { + if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + &format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); + } + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 3c6a7071c24..94740093d3b 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,13 +1,14 @@ +mod builtin_type_shadow; + use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ - BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, UnOp, + BindingMode, Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, + UnOp, }; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::PrimTy; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -265,16 +266,7 @@ declare_lint_pass!(MiscEarlyLints => [ impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - &format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } - } + builtin_type_shadow::check(cx, param); } } diff --git a/tests/ui/builtin-type-shadow.rs b/tests/ui/builtin-type-shadow.rs deleted file mode 100644 index 69b8b6a0e68..00000000000 --- a/tests/ui/builtin-type-shadow.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::builtin_type_shadow)] -#![allow(non_camel_case_types)] - -fn foo(a: u32) -> u32 { - 42 - // ^ rustc's type error -} - -fn main() {} diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr deleted file mode 100644 index f42b246afd2..00000000000 --- a/tests/ui/builtin-type-shadow.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: this generic shadows the built-in type `u32` - --> $DIR/builtin-type-shadow.rs:4:8 - | -LL | fn foo(a: u32) -> u32 { - | ^^^ - | - = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` - -error[E0308]: mismatched types - --> $DIR/builtin-type-shadow.rs:5:5 - | -LL | fn foo(a: u32) -> u32 { - | --- --- expected `u32` because of return type - | | - | this type parameter -LL | 42 - | ^^ expected type parameter `u32`, found integer - | - = note: expected type parameter `u32` - found type `{integer}` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/builtin_type_shadow.rs b/tests/ui/builtin_type_shadow.rs new file mode 100644 index 00000000000..69b8b6a0e68 --- /dev/null +++ b/tests/ui/builtin_type_shadow.rs @@ -0,0 +1,9 @@ +#![warn(clippy::builtin_type_shadow)] +#![allow(non_camel_case_types)] + +fn foo(a: u32) -> u32 { + 42 + // ^ rustc's type error +} + +fn main() {} diff --git a/tests/ui/builtin_type_shadow.stderr b/tests/ui/builtin_type_shadow.stderr new file mode 100644 index 00000000000..47a8a1e623e --- /dev/null +++ b/tests/ui/builtin_type_shadow.stderr @@ -0,0 +1,24 @@ +error: this generic shadows the built-in type `u32` + --> $DIR/builtin_type_shadow.rs:4:8 + | +LL | fn foo(a: u32) -> u32 { + | ^^^ + | + = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` + +error[E0308]: mismatched types + --> $DIR/builtin_type_shadow.rs:5:5 + | +LL | fn foo(a: u32) -> u32 { + | --- --- expected `u32` because of return type + | | + | this type parameter +LL | 42 + | ^^ expected type parameter `u32`, found integer + | + = note: expected type parameter `u32` + found type `{integer}` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 55af0cee15cf5c85cbb50ef41eafb4498bde24e3 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:20:22 +0900 Subject: move double_neg to its own module --- clippy_lints/src/misc_early/double_neg.rs | 23 +++++++++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 19 +++---------------- 2 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 clippy_lints/src/misc_early/double_neg.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/double_neg.rs b/clippy_lints/src/misc_early/double_neg.rs new file mode 100644 index 00000000000..6f65778e119 --- /dev/null +++ b/clippy_lints/src/misc_early/double_neg.rs @@ -0,0 +1,23 @@ +use super::MiscEarlyLints; +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Expr, ExprKind, UnOp}; +use rustc_lint::EarlyContext; + +use super::DOUBLE_NEG; + +pub(super) fn check(cx: &EarlyContext<'_>, expr: &Expr) { + match expr.kind { + ExprKind::Unary(UnOp::Neg, ref inner) => { + if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { + span_lint( + cx, + DOUBLE_NEG, + expr.span, + "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op", + ); + } + }, + ExprKind::Lit(ref lit) => MiscEarlyLints::check_lit(cx, lit), + _ => (), + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 94740093d3b..47cc87dfffb 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,10 +1,10 @@ mod builtin_type_shadow; +mod double_neg; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ - BindingMode, Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, - UnOp, + BindingMode, Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, }; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; @@ -393,20 +393,7 @@ impl EarlyLintPass for MiscEarlyLints { if in_external_macro(cx.sess(), expr.span) { return; } - match expr.kind { - ExprKind::Unary(UnOp::Neg, ref inner) => { - if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { - span_lint( - cx, - DOUBLE_NEG, - expr.span, - "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op", - ); - } - }, - ExprKind::Lit(ref lit) => Self::check_lit(cx, lit), - _ => (), - } + double_neg::check(cx, expr) } } -- cgit 1.4.1-3-g733a5 From 52cfde058668ddce3a191c0c608ac07603175d04 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:28:32 +0900 Subject: move redundant_pattern to its own module --- clippy_lints/src/misc_early/mod.rs | 29 +++------------------- clippy_lints/src/misc_early/redundant_pattern.rs | 31 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 clippy_lints/src/misc_early/redundant_pattern.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 47cc87dfffb..564afdb7f8d 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,11 +1,10 @@ mod builtin_type_shadow; mod double_neg; +mod redundant_pattern; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{ - BindingMode, Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, -}; +use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -336,29 +335,7 @@ impl EarlyLintPass for MiscEarlyLints { } } - if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { - let left_binding = match left { - BindingMode::ByRef(Mutability::Mut) => "ref mut ", - BindingMode::ByRef(Mutability::Not) => "ref ", - BindingMode::ByValue(..) => "", - }; - - if let PatKind::Wild = right.kind { - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN, - pat.span, - &format!( - "the `{} @ _` pattern can be written as just `{}`", - ident.name, ident.name, - ), - "try", - format!("{}{}", left_binding, ident.name), - Applicability::MachineApplicable, - ); - } - } - + redundant_pattern::check(cx, pat); check_unneeded_wildcard_pattern(cx, pat); } diff --git a/clippy_lints/src/misc_early/redundant_pattern.rs b/clippy_lints/src/misc_early/redundant_pattern.rs new file mode 100644 index 00000000000..525dbf7757c --- /dev/null +++ b/clippy_lints/src/misc_early/redundant_pattern.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{BindingMode, Mutability, Pat, PatKind}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; + +use super::REDUNDANT_PATTERN; + +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { + let left_binding = match left { + BindingMode::ByRef(Mutability::Mut) => "ref mut ", + BindingMode::ByRef(Mutability::Not) => "ref ", + BindingMode::ByValue(..) => "", + }; + + if let PatKind::Wild = right.kind { + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN, + pat.span, + &format!( + "the `{} @ _` pattern can be written as just `{}`", + ident.name, ident.name, + ), + "try", + format!("{}{}", left_binding, ident.name), + Applicability::MachineApplicable, + ); + } + } +} -- cgit 1.4.1-3-g733a5 From c0a106e25214a16cb01c87f7f01fd7c95d3534c2 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 19:50:48 +0900 Subject: move unneeded_wildcard_pattern to its own module --- clippy_lints/src/misc_early/mod.rs | 48 +------------------- .../src/misc_early/unneeded_wildcard_pattern.rs | 52 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 46 deletions(-) create mode 100644 clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 564afdb7f8d..7d1cd81f49f 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,6 +1,7 @@ mod builtin_type_shadow; mod double_neg; mod redundant_pattern; +mod unneeded_wildcard_pattern; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; @@ -336,7 +337,7 @@ impl EarlyLintPass for MiscEarlyLints { } redundant_pattern::check(cx, pat); - check_unneeded_wildcard_pattern(cx, pat); + unneeded_wildcard_pattern::check(cx, pat); } fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { @@ -478,48 +479,3 @@ impl MiscEarlyLints { } } } - -fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { - fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { - span_lint_and_sugg( - cx, - UNNEEDED_WILDCARD_PATTERN, - span, - if only_one { - "this pattern is unneeded as the `..` pattern can match that element" - } else { - "these patterns are unneeded as the `..` pattern can match those elements" - }, - if only_one { "remove it" } else { "remove them" }, - "".to_string(), - Applicability::MachineApplicable, - ); - } - - 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(|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(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint( - cx, - patterns[rest_index].span.shrink_to_hi().to(right_pat.span), - right_index == 0, - ); - } - } - } -} diff --git a/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs new file mode 100644 index 00000000000..4dd032d78f1 --- /dev/null +++ b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{Pat, PatKind}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; +use rustc_span::source_map::Span; + +use super::UNNEEDED_WILDCARD_PATTERN; + +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { + 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(|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(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint( + cx, + patterns[rest_index].span.shrink_to_hi().to(right_pat.span), + right_index == 0, + ); + } + } + } +} + +fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { + span_lint_and_sugg( + cx, + UNNEEDED_WILDCARD_PATTERN, + span, + if only_one { + "this pattern is unneeded as the `..` pattern can match that element" + } else { + "these patterns are unneeded as the `..` pattern can match those elements" + }, + if only_one { "remove it" } else { "remove them" }, + "".to_string(), + Applicability::MachineApplicable, + ); +} -- cgit 1.4.1-3-g733a5 From 0854f0caee1d6d16e57eb1d3f5abf539b3bee3f0 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 4 May 2021 16:48:25 +0200 Subject: Don't trigger `field_reassign_with_default` in macros Producing a good suggestion for this lint is already hard when no macros are involved. With macros the lint message and the suggestion are just confusing. Since both, producing a good suggestion and figuring out if this pattern can be re-written inside a macro is nearly impossible, just bail out. --- clippy_lints/src/default.rs | 3 +-- tests/ui/field_reassign_with_default.rs | 20 ++++++++++++++++ tests/ui/field_reassign_with_default.stderr | 36 ++++++++++++++--------------- 3 files changed, 39 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 7a53d390bb4..947479db8f5 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -7,7 +7,6 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; @@ -122,7 +121,7 @@ impl LateLintPass<'_> for Default { if let StmtKind::Local(local) = stmt.kind; if let Some(expr) = local.init; if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if !in_external_macro(cx.tcx.sess, expr.span); + if !in_macro(expr.span); // only take bindings to identifiers if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; // only when assigning `... = Default::default()` diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 1368c5d7984..787053fb000 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -29,6 +29,21 @@ struct C { i: Vec, j: i64, } + +#[derive(Default)] +struct D { + a: Option, + b: Option, +} + +macro_rules! m { + ($key:ident: $value:tt) => {{ + let mut data = $crate::D::default(); + data.$key = Some($value); + data + }}; +} + /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -143,6 +158,11 @@ fn main() { let mut a: WrapperMulti = Default::default(); a.i = 42; + + // Don't lint in macros + m! { + a: 42 + }; } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index dd7c0360bb1..b56db08ec8a 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,108 +1,108 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:48:5 + --> $DIR/field_reassign_with_default.rs:63:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:47:5 + --> $DIR/field_reassign_with_default.rs:62:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:88:5 + --> $DIR/field_reassign_with_default.rs:103:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:87:5 + --> $DIR/field_reassign_with_default.rs:102:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:93:5 + --> $DIR/field_reassign_with_default.rs:108:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:92:5 + --> $DIR/field_reassign_with_default.rs:107:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:99:5 + --> $DIR/field_reassign_with_default.rs:114:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:98:5 + --> $DIR/field_reassign_with_default.rs:113:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:109:5 + --> $DIR/field_reassign_with_default.rs:124:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:108:5 + --> $DIR/field_reassign_with_default.rs:123:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:113:5 + --> $DIR/field_reassign_with_default.rs:128:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:112:5 + --> $DIR/field_reassign_with_default.rs:127:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:135:5 + --> $DIR/field_reassign_with_default.rs:150:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:134:5 + --> $DIR/field_reassign_with_default.rs:149:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:142:5 + --> $DIR/field_reassign_with_default.rs:157:5 | LL | a.i = true; | ^^^^^^^^^^^ | note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:141:5 + --> $DIR/field_reassign_with_default.rs:156:5 | LL | let mut a: Wrapper = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:145:5 + --> $DIR/field_reassign_with_default.rs:160:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:144:5 + --> $DIR/field_reassign_with_default.rs:159:5 | LL | let mut a: WrapperMulti = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 829fde5e1f1163b3218409244863ce64d8c7dd19 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 01:44:38 +0900 Subject: move unneeded_field_pattern to its own module --- clippy_lints/src/misc_early/mod.rs | 69 +-------------------- .../src/misc_early/unneeded_field_pattern.rs | 72 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 66 deletions(-) create mode 100644 clippy_lints/src/misc_early/unneeded_field_pattern.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 7d1cd81f49f..ee1bff8bf8a 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,9 +1,10 @@ mod builtin_type_shadow; mod double_neg; mod redundant_pattern; +mod unneeded_field_pattern; mod unneeded_wildcard_pattern; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; @@ -271,71 +272,7 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { - let mut wilds = 0; - let type_name = npat - .segments - .last() - .expect("A path must have at least one segment") - .ident - .name; - - for field in pfields { - if let PatKind::Wild = field.pat.kind { - wilds += 1; - } - } - if !pfields.is_empty() && wilds == pfields.len() { - span_lint_and_help( - cx, - UNNEEDED_FIELD_PATTERN, - pat.span, - "all the struct fields are matched to a wildcard pattern, consider using `..`", - None, - &format!("try with `{} {{ .. }}` instead", type_name), - ); - return; - } - if wilds > 0 { - for field in pfields { - if let PatKind::Wild = field.pat.kind { - wilds -= 1; - if wilds > 0 { - span_lint( - cx, - UNNEEDED_FIELD_PATTERN, - field.span, - "you matched a field with a wildcard pattern, consider using `..` instead", - ); - } else { - let mut normal = vec![]; - - for field in pfields { - match field.pat.kind { - PatKind::Wild => {}, - _ => { - if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { - normal.push(n); - } - }, - } - } - - span_lint_and_help( - cx, - UNNEEDED_FIELD_PATTERN, - field.span, - "you matched a field with a wildcard pattern, consider using `..` \ - instead", - None, - &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), - ); - } - } - } - } - } - + unneeded_field_pattern::check(cx, pat); redundant_pattern::check(cx, pat); unneeded_wildcard_pattern::check(cx, pat); } diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs new file mode 100644 index 00000000000..329a0009a3e --- /dev/null +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -0,0 +1,72 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use rustc_ast::ast::{Pat, PatKind}; +use rustc_lint::{EarlyContext, LintContext}; + +use super::UNNEEDED_FIELD_PATTERN; + +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { + let mut wilds = 0; + let type_name = npat + .segments + .last() + .expect("A path must have at least one segment") + .ident + .name; + + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds += 1; + } + } + if !pfields.is_empty() && wilds == pfields.len() { + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + pat.span, + "all the struct fields are matched to a wildcard pattern, consider using `..`", + None, + &format!("try with `{} {{ .. }}` instead", type_name), + ); + return; + } + if wilds > 0 { + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds -= 1; + if wilds > 0 { + span_lint( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "you matched a field with a wildcard pattern, consider using `..` instead", + ); + } else { + let mut normal = vec![]; + + for field in pfields { + match field.pat.kind { + PatKind::Wild => {}, + _ => { + if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { + normal.push(n); + } + }, + } + } + + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "you matched a field with a wildcard pattern, consider using `..` \ + instead", + None, + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + ); + } + } + } + } + } +} -- cgit 1.4.1-3-g733a5 From f012e0e3de04ca5b8022a0b8856a12d47d746b5b Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 02:10:24 +0900 Subject: move unseparated_literal_suffix to its own module --- clippy_lints/src/misc_early/mod.rs | 19 ++------------- .../src/misc_early/unseparated_literal_suffix.rs | 27 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 clippy_lints/src/misc_early/unseparated_literal_suffix.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index ee1bff8bf8a..182218e77f6 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -3,6 +3,7 @@ mod double_neg; mod redundant_pattern; mod unneeded_field_pattern; mod unneeded_wildcard_pattern; +mod unseparated_literal_suffix; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; @@ -396,23 +397,7 @@ impl MiscEarlyLints { ); } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { - let suffix = float_ty.name_str(); - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { - return; // It's useless so shouldn't lint. - }; - if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { - span_lint_and_sugg( - cx, - UNSEPARATED_LITERAL_SUFFIX, - lit.span, - "float type suffix should be separated by an underscore", - "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), - Applicability::MachineApplicable, - ); - } + unseparated_literal_suffix::check(cx, lit, float_ty, lit_snip) } } } diff --git a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs new file mode 100644 index 00000000000..ffdd5d93a38 --- /dev/null +++ b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs @@ -0,0 +1,27 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::FloatTy; +use rustc_ast::ast::Lit; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; + +use super::UNSEPARATED_LITERAL_SUFFIX; + +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, float_ty: FloatTy, lit_snip: String) { + let suffix = float_ty.name_str(); + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; + if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + span_lint_and_sugg( + cx, + UNSEPARATED_LITERAL_SUFFIX, + lit.span, + "float type suffix should be separated by an underscore", + "add an underscore", + format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + Applicability::MachineApplicable, + ); + } +} -- cgit 1.4.1-3-g733a5 From 0773d8afdb792a3147de53ad590b620be9eef006 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 02:21:26 +0900 Subject: move mixed_case_hex_literals to its own module --- .../src/misc_early/mixed_case_hex_literals.rs | 29 ++++++++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 23 ++--------------- 2 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 clippy_lints/src/misc_early/mixed_case_hex_literals.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs new file mode 100644 index 00000000000..fbcbfa2556f --- /dev/null +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::Lit; +use rustc_lint::EarlyContext; + +use super::MIXED_CASE_HEX_LITERALS; + +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, maybe_last_sep_idx: usize, lit_snip: String) { + if maybe_last_sep_idx <= 2 { + // It's meaningless or causes range error. + return; + } + let mut seen = (false, false); + for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() { + match ch { + b'a'..=b'f' => seen.0 = true, + b'A'..=b'F' => seen.1 = true, + _ => {}, + } + if seen.0 && seen.1 { + span_lint( + cx, + MIXED_CASE_HEX_LITERALS, + lit.span, + "inconsistent casing in hexadecimal literal", + ); + break; + } + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 182218e77f6..0bfa6833aac 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,5 +1,6 @@ mod builtin_type_shadow; mod double_neg; +mod mixed_case_hex_literals; mod redundant_pattern; mod unneeded_field_pattern; mod unneeded_wildcard_pattern; @@ -351,27 +352,7 @@ impl MiscEarlyLints { } if lit_snip.starts_with("0x") { - if maybe_last_sep_idx <= 2 { - // It's meaningless or causes range error. - return; - } - let mut seen = (false, false); - for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() { - match ch { - b'a'..=b'f' => seen.0 = true, - b'A'..=b'F' => seen.1 = true, - _ => {}, - } - if seen.0 && seen.1 { - span_lint( - cx, - MIXED_CASE_HEX_LITERALS, - lit.span, - "inconsistent casing in hexadecimal literal", - ); - break; - } - } + mixed_case_hex_literals::check(cx, lit, maybe_last_sep_idx, lit_snip) } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { /* nothing to do */ } else if value != 0 && lit_snip.starts_with('0') { -- cgit 1.4.1-3-g733a5 From b1faaaeb0cfb3310c2679f22a33d5ec855f8aaf8 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 3 May 2021 13:36:08 -0700 Subject: needless_collect: Lint cases with type annotations --- clippy_lints/src/loops/needless_collect.rs | 23 ++++++++++++---- tests/ui/needless_collect_indirect.rs | 29 +++++++++++++++++++- tests/ui/needless_collect_indirect.stderr | 44 +++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 4d73aef76e8..293dd8b6b79 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -7,9 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; + use rustc_span::symbol::{sym, Ident}; use rustc_span::{MultiSpan, Span}; @@ -58,18 +59,30 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont } fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option { + if let Some(ty) = ty { + return Some(ty.hir_id); + } + + if let Some(generic_args) = method_args { + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) { + return Some(ty.hir_id); + } + } + + None + } if let ExprKind::Block(block, _) = expr.kind { for stmt in block.stmts { if_chain! { if let StmtKind::Local( Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(init_expr), .. } + init: Some(init_expr), ty, .. } ) = stmt.kind; if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - if let Some(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 let Some(hir_id) = get_hir_id(*ty, method_name.args); + if let ty = cx.typeck_results().node_type(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); diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 0918a6868ab..2647c6401a4 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, VecDeque}; +use std::collections::{HashMap, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -43,3 +43,30 @@ fn main() { .collect::>(); } } + +mod issue7110 { + // #7110 - lint for type annotation cases + use super::*; + + fn lint_vec(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + buffer.len() + } + fn lint_vec_deque() -> usize { + let sample = [1; 5]; + let indirect_len: VecDeque<_> = sample.iter().collect(); + indirect_len.len() + } + fn lint_linked_list() -> usize { + let sample = [1; 5]; + let indirect_len: LinkedList<_> = sample.iter().collect(); + indirect_len.len() + } + fn dont_lint(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + for buff in &buffer { + println!("{}", buff); + } + buffer.len() + } +} diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index c773b841f3b..642beb2865a 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -69,5 +69,47 @@ LL | LL | sample.into_iter().any(|x| x == a); | -error: aborting due to 5 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:52:51 + | +LL | let buffer: Vec<&str> = string.split('/').collect(); + | ^^^^^^^ +LL | buffer.len() + | ------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | string.split('/').count() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:57:55 + | +LL | let indirect_len: VecDeque<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +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:62:57 + | +LL | let indirect_len: LinkedList<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 1835d8a2383c8ae3d26cdcba594fdb933a41f3fb Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Tue, 4 May 2021 12:36:20 -0700 Subject: needless_collect: Add `BinaryHeap` for indirect usage lint --- clippy_lints/src/loops/needless_collect.rs | 1 + tests/ui/needless_collect_indirect.rs | 7 ++++++- tests/ui/needless_collect_indirect.stderr | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 293dd8b6b79..88d586c9346 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -85,6 +85,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ty = cx.typeck_results().node_type(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if let [iter_call] = &*iter_calls; diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 2647c6401a4..2458bf1e490 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, LinkedList, VecDeque}; +use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -62,6 +62,11 @@ mod issue7110 { let indirect_len: LinkedList<_> = sample.iter().collect(); indirect_len.len() } + fn lint_binary_heap() -> usize { + let sample = [1; 5]; + let indirect_len: BinaryHeap<_> = sample.iter().collect(); + indirect_len.len() + } fn dont_lint(string: &str) -> usize { let buffer: Vec<&str> = string.split('/').collect(); for buff in &buffer { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 642beb2865a..f094e182a48 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -111,5 +111,19 @@ LL | LL | sample.iter().count() | -error: aborting due to 8 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:67:57 + | +LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From b1567f4466388df8377512580d72513e1e4e0e48 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 06:18:58 +0900 Subject: move zero_prefixed_literal to its own module --- clippy_lints/src/misc_early/mod.rs | 24 +++--------------- .../src/misc_early/zero_prefixed_literal.rs | 29 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 clippy_lints/src/misc_early/zero_prefixed_literal.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 0bfa6833aac..4b1d55beca8 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -5,8 +5,9 @@ mod redundant_pattern; mod unneeded_field_pattern; mod unneeded_wildcard_pattern; mod unseparated_literal_suffix; +mod zero_prefixed_literal; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; @@ -356,26 +357,7 @@ impl MiscEarlyLints { } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { /* nothing to do */ } else if value != 0 && lit_snip.starts_with('0') { - span_lint_and_then( - cx, - ZERO_PREFIXED_LITERAL, - lit.span, - "this is a decimal constant", - |diag| { - diag.span_suggestion( - lit.span, - "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), - Applicability::MaybeIncorrect, - ); - }, - ); + zero_prefixed_literal::check(cx, lit, lit_snip) } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { unseparated_literal_suffix::check(cx, lit, float_ty, lit_snip) diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs new file mode 100644 index 00000000000..16add0fa1b4 --- /dev/null +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::ast::Lit; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; + +use super::ZERO_PREFIXED_LITERAL; + +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: String) { + span_lint_and_then( + cx, + ZERO_PREFIXED_LITERAL, + lit.span, + "this is a decimal constant", + |diag| { + diag.span_suggestion( + lit.span, + "if you mean to use a decimal constant, remove the `0` to avoid confusion", + lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + Applicability::MaybeIncorrect, + ); + }, + ); +} -- cgit 1.4.1-3-g733a5 From 919ed2ba03f6bf7b02df6eda53d1cbfc28161448 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 May 2021 16:22:43 -0500 Subject: Fix needless_quesiton_mark false positive --- clippy_lints/src/needless_question_mark.rs | 112 +++++------------------------ tests/ui/needless_question_mark.fixed | 5 ++ tests/ui/needless_question_mark.rs | 5 ++ tests/ui/needless_question_mark.stderr | 2 +- 4 files changed, 30 insertions(+), 94 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 7b156a8c49d..5c9cce6aad4 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -63,12 +62,6 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); -#[derive(Debug)] -enum SomeOkCall<'a> { - SomeCall(&'a Expr<'a>, &'a Expr<'a>), - OkCall(&'a Expr<'a>, &'a Expr<'a>), -} - impl LateLintPass<'_> for NeedlessQuestionMark { /* * The question mark operator is compatible with both Result and Option, @@ -90,104 +83,37 @@ impl LateLintPass<'_> for NeedlessQuestionMark { */ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - let e = match &expr.kind { - ExprKind::Ret(Some(e)) => e, - _ => return, - }; - - if let Some(ok_some_call) = is_some_or_ok_call(cx, e) { - emit_lint(cx, &ok_some_call); + if let ExprKind::Ret(Some(e)) = expr.kind { + check(cx, e); } } fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - // Function / Closure block - let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind { - block.expr - } else { - // Single line closure - Some(&body.value) - }; - - if_chain! { - if let Some(expr) = expr_opt; - if let Some(ok_some_call) = is_some_or_ok_call(cx, expr); - then { - emit_lint(cx, &ok_some_call); - } - }; + check(cx, body.value.peel_blocks()); } } -fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { - let (entire_expr, inner_expr) = match expr { - SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), +fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { + let inner_expr = if_chain! { + if let ExprKind::Call(path, [arg]) = &expr.kind; + if let ExprKind::Path(ref qpath) = &path.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind; + if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind; + if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; + if expr.span.ctxt() == inner_expr.span.ctxt(); + let expr_ty = cx.typeck_results().expr_ty(expr); + let inner_ty = cx.typeck_results().expr_ty(inner_expr); + if TyS::same_type(expr_ty, inner_ty); + then { inner_expr } else { return; } }; - span_lint_and_sugg( cx, NEEDLESS_QUESTION_MARK, - entire_expr.span, + expr.span, "question mark operator is useless here", "try", format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); } - -fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option> { - if_chain! { - // Check outer expression matches CALL_IDENT(ARGUMENT) format - if let ExprKind::Call(path, args) = &expr.kind; - if let ExprKind::Path(ref qpath) = &path.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); - - // Extract inner expression from ARGUMENT - if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; - if let ExprKind::Call(called, args) = &inner_expr_with_q.kind; - if args.len() == 1; - - if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; - then { - // Extract inner expr type from match argument generated by - // question mark operator - let inner_expr = &args[0]; - - // if the inner expr is inside macro but the outer one is not, do not lint (#6921) - if differing_macro_contexts(expr.span, inner_expr.span) { - return None; - } - - let inner_ty = cx.typeck_results().expr_ty(inner_expr); - let outer_ty = cx.typeck_results().expr_ty(expr); - - // Check if outer and inner type are Option - let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type); - let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); - - // Check for Option MSRV - if outer_is_some && inner_is_some { - return Some(SomeOkCall::SomeCall(expr, inner_expr)); - } - - // Check if outer and inner type are Result - let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type); - let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type); - - // Additional check: if the error type of the Result can be converted - // via the From trait, then don't match - let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); - - // Must meet Result MSRV - if outer_is_result && inner_is_result && does_not_call_from { - return Some(SomeOkCall::OkCall(expr, inner_expr)); - } - } - } - - None -} - -fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { - return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); -} diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index 52ddd9d2dc8..f1fc81aa12b 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -94,6 +94,11 @@ where Ok(x?) } +// not quite needless +fn deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + fn main() {} // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 1ea4ba0d83f..44a0c5f61b5 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -94,6 +94,11 @@ where Ok(x?) } +// not quite needless +fn deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + fn main() {} // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index afd68d91e51..fa698224530 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -67,7 +67,7 @@ LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:115:27 + --> $DIR/needless_question_mark.rs:120:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` -- cgit 1.4.1-3-g733a5 From 2dfb246d8e8db3280326330b9ca67aee5d3214e9 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 06:39:38 +0900 Subject: refactor unseparated_literal_suffix --- .../src/misc_early/mixed_case_hex_literals.rs | 7 +++++- clippy_lints/src/misc_early/mod.rs | 28 ++++------------------ .../src/misc_early/unseparated_literal_suffix.rs | 9 ++++--- 3 files changed, 15 insertions(+), 29 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index fbcbfa2556f..98b0f7b5664 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -4,7 +4,12 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, maybe_last_sep_idx: usize, lit_snip: String) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: String) { + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; if maybe_last_sep_idx <= 2 { // It's meaningless or causes range error. return; diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 4b1d55beca8..146d0c367a8 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -7,12 +7,11 @@ mod unneeded_wildcard_pattern; mod unseparated_literal_suffix; mod zero_prefixed_literal; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; 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}; @@ -333,34 +332,17 @@ impl MiscEarlyLints { LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { - return; // It's useless so shouldn't lint. - }; - // Do not lint when literal is unsuffixed. - if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { - span_lint_and_sugg( - cx, - UNSEPARATED_LITERAL_SUFFIX, - lit.span, - "integer type suffix should be separated by an underscore", - "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), - Applicability::MachineApplicable, - ); - } - + unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, maybe_last_sep_idx, lit_snip) + mixed_case_hex_literals::check(cx, lit, suffix, lit_snip) } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { /* nothing to do */ } else if value != 0 && lit_snip.starts_with('0') { zero_prefixed_literal::check(cx, lit, lit_snip) } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { - unseparated_literal_suffix::check(cx, lit, float_ty, lit_snip) + let suffix = float_ty.name_str(); + unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float") } } } diff --git a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs index ffdd5d93a38..1ffdd4cf676 100644 --- a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs +++ b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs @@ -1,24 +1,23 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::FloatTy; use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use super::UNSEPARATED_LITERAL_SUFFIX; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, float_ty: FloatTy, lit_snip: String) { - let suffix = float_ty.name_str(); +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &String, suffix: &str, sugg_type: &str) { let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { val } else { return; // It's useless so shouldn't lint. }; - if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + // Do not lint when literal is unsuffixed. + if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { span_lint_and_sugg( cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, - "float type suffix should be separated by an underscore", + &format!("{} type suffix should be separated by an underscore", sugg_type), "add an underscore", format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), Applicability::MachineApplicable, -- cgit 1.4.1-3-g733a5 From 3fbb060379485605fc8649ba5e749b141fbb9335 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 06:53:04 +0900 Subject: replace lit_snip type with &str --- clippy_lints/src/misc_early/mixed_case_hex_literals.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 4 ++-- clippy_lints/src/misc_early/unseparated_literal_suffix.rs | 2 +- clippy_lints/src/misc_early/zero_prefixed_literal.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 98b0f7b5664..80e24213100 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -4,7 +4,7 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: String) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { val } else { diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 146d0c367a8..dd38316fa25 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -334,11 +334,11 @@ impl MiscEarlyLints { }; unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, suffix, lit_snip) + mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip) } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { /* nothing to do */ } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, lit, lit_snip) + zero_prefixed_literal::check(cx, lit, &lit_snip) } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { let suffix = float_ty.name_str(); diff --git a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs index 1ffdd4cf676..2018aa6184a 100644 --- a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs +++ b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs @@ -5,7 +5,7 @@ use rustc_lint::EarlyContext; use super::UNSEPARATED_LITERAL_SUFFIX; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &String, suffix: &str, sugg_type: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { val } else { diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 16add0fa1b4..4963bba82f2 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -5,7 +5,7 @@ use rustc_lint::EarlyContext; use super::ZERO_PREFIXED_LITERAL; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: String) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, -- cgit 1.4.1-3-g733a5 From 83329ec7050da5093b0310ba3140c8c5ec093321 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 May 2021 17:20:15 -0500 Subject: Fix unused_unit macro false positive --- clippy_lints/src/unused_unit.rs | 4 +++- tests/ui/unused_unit.fixed | 7 +++++++ tests/ui/unused_unit.rs | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index ce2d0b3ab2f..e14945651f5 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -47,7 +47,9 @@ impl EarlyLintPass for UnusedUnit { if_chain! { if let Some(stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); + if is_unit_expr(expr); + let ctxt = block.span.ctxt(); + if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt; then { let sp = expr.span; span_lint_and_sugg( diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index a192ebde3eb..7bb43cf7ae8 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -80,3 +80,10 @@ fn test2(){} #[rustfmt::skip] fn test3(){} + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 96041a7dd85..21073fb802a 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -80,3 +80,10 @@ fn test2() ->(){} #[rustfmt::skip] fn test3()-> (){} + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} -- cgit 1.4.1-3-g733a5 From f79a2a3990e88cba2a2d4c55828a97d2293d6992 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 04:52:03 -0700 Subject: needless_collect: use `node_type_opt` instead of `node_type` It may prevent future ICEs. --- clippy_lints/src/loops/needless_collect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 88d586c9346..9662a0b22a3 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -27,7 +27,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - let ty = cx.typeck_results().node_type(ty.hir_id); + if let Some(ty) = cx.typeck_results().node_type_opt(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::BTREEMAP) @@ -82,7 +82,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); if let Some(hir_id) = get_hir_id(*ty, method_name.args); - if let ty = cx.typeck_results().node_type(hir_id); + if let Some(ty) = cx.typeck_results().node_type_opt(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || -- cgit 1.4.1-3-g733a5 From 344f04bea6fd61a10d079ca51d331307f4747288 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 08:57:09 -0500 Subject: Fix stack overflow in `redundant_pattern_matching` --- clippy_lints/src/matches.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c7a25095bf6..a70e8b26087 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1712,6 +1712,7 @@ mod redundant_pattern_match { use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; + use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{ @@ -1739,6 +1740,13 @@ mod redundant_pattern_match { /// deallocate memory. For these types, and composites containing them, changing the drop order /// won't result in any observable side effects. fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) + } + + fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { + if !seen.insert(ty) { + return false; + } if !ty.needs_drop(cx.tcx, cx.param_env) { false } else if !cx @@ -1750,12 +1758,12 @@ mod redundant_pattern_match { // This type doesn't implement drop, so no side effects here. // Check if any component type has any. match ty.kind() { - ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)), - ty::Array(ty, _) => type_needs_ordered_drop(cx, ty), + ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), + ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen), ty::Adt(adt, subs) => adt .all_fields() .map(|f| f.ty(cx.tcx, subs)) - .any(|ty| type_needs_ordered_drop(cx, ty)), + .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), _ => true, } } @@ -1772,7 +1780,7 @@ mod redundant_pattern_match { { // Check all of the generic arguments. if let ty::Adt(_, subs) = ty.kind() { - subs.types().any(|ty| type_needs_ordered_drop(cx, ty)) + subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)) } else { true } -- cgit 1.4.1-3-g733a5 From 637751ff62662247480ae1641042a019d8e6609a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 31 Jan 2021 14:46:09 +0100 Subject: Metadata collection lint: Basic lint collection WIP-2021-02-01 WIP-2021-02-01 WIP-2021-02-13 --- .gitignore | 1 + Cargo.toml | 1 + clippy_lints/Cargo.toml | 2 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 3 + .../src/utils/internal_lints/metadata_collector.rs | 206 +++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 2 +- tests/dogfood.rs | 9 + 8 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/utils/internal_lints/metadata_collector.rs (limited to 'clippy_lints/src') diff --git a/.gitignore b/.gitignore index 376528e3085..523bab18828 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ out # gh pages docs util/gh-pages/lints.json +**/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/Cargo.toml b/Cargo.toml index cade44a0a9a..bdee8e40821 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } deny-warnings = [] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] +metadata-collector-lint = ["clippy_lints/metadata-collector-lint"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 05cdd9d064a..d1d56fc84f9 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -20,6 +20,7 @@ pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", optional = true } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" @@ -32,6 +33,7 @@ url = { version = "2.1.0", features = ["serde"] } deny-warnings = [] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] +metadata-collector-lint = ["serde_json"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 40a793e48cf..7668d4dd0b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1004,6 +1004,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } + #[cfg(feature = "metadata-collector-lint")] + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeApi); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 3d3d0e19d26..ee7be24eae8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -32,6 +32,9 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; +#[cfg(feature = "metadata-collector-lint")] +pub mod metadata_collector; + declare_clippy_lint! { /// **What it does:** Checks for various things we like to keep tidy in clippy. /// diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs new file mode 100644 index 00000000000..dc4267697cb --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -0,0 +1,206 @@ +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) +//! +//! This module and therefor the entire lint is guarded by a feature flag called +//! `internal_metadata_lint` +//! +//! The metadata currently contains: +//! - [ ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) +//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) +//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) + +// # Applicability +// - TODO xFrednet 2021-01-17: Find all methods that take and modify applicability or predefine +// them? +// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability +// # NITs +// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames + +use if_chain::if_chain; +use rustc_hir::{ExprKind, Item, ItemKind, Mutability}; +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{sym, Loc, Span}; +use serde::Serialize; +use std::fs::OpenOptions; +use std::io::prelude::*; + +use crate::utils::internal_lints::is_lint_ref_type; +use crate::utils::span_lint; + +const OUTPUT_FILE: &str = "metadata_collection.json"; +const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; + +declare_clippy_lint! { + /// **What it does:** Collects metadata about clippy lints for the website. + /// + /// This lint will be used to report problems of syntax parsing. You should hopefully never + /// see this but never say never I guess ^^ + /// + /// **Why is this bad?** This is not a bad thing but definitely a hacky way to do it. See + /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion + /// about the implementation. + /// + /// **Known problems:** Hopefully none. It would be pretty uncool to have a problem here :) + /// + /// **Example output:** + /// ```json,ignore + /// { + /// "id": "internal_metadata_collector", + /// "id_span": { + /// "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs", + /// "line": 1 + /// }, + /// "group": "clippy::internal", + /// "docs": " **What it does:** Collects metadata about clippy lints for the website. [...] " + /// } + /// ``` + pub INTERNAL_METADATA_COLLECTOR, + internal, + "A busy bee collection metadata about lints" +} + +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Clone, Default)] +pub struct MetadataCollector { + lints: Vec, +} + +impl Drop for MetadataCollector { + fn drop(&mut self) { + // You might ask: How hacky is this? + // My answer: YES + let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); + writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); + } +} + +#[derive(Debug, Clone, Serialize)] +struct LintMetadata { + id: String, + id_span: SerializableSpan, + group: String, + docs: String, +} + +#[derive(Debug, Clone, Serialize)] +struct SerializableSpan { + path: String, + line: usize, +} + +impl SerializableSpan { + fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self { + Self::from_span(cx, item.ident.span) + } + + fn from_span(cx: &LateContext<'_>, span: Span) -> Self { + let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo()); + + Self { + path: format!("{}", loc.file.name), + line: 1, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for MetadataCollector { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; + if is_lint_ref_type(cx, ty); + let expr = &cx.tcx.hir().body(body_id).value; + if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; + if let ExprKind::Struct(_, _, _) = inner_exp.kind; + then { + let lint_name = item.ident.name.as_str().to_string().to_ascii_lowercase(); + if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { + return; + } + + let group: String; + let result = cx.lint_store.check_lint_name(lint_name.as_str(), Some(sym::clippy)); + if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { + group = group_some; + } else { + lint_collection_error(cx, item, "Unable to determine lint group"); + return; + } + } else { + lint_collection_error(cx, item, "Unable to find lint in lint_store"); + return; + } + + let docs: String; + if let Some(docs_some) = extract_attr_docs(item) { + docs = docs_some; + } else { + lint_collection_error(cx, item, "could not collect the lint documentation"); + return; + }; + + self.lints.push(LintMetadata { + id: lint_name, + id_span: SerializableSpan::from_item(cx, item), + group, + docs, + }); + } + } + } +} + +/// This function collects all documentation that has been added to an item using +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks +/// +/// ```ignore +/// #[doc = r"Hello world!"] +/// #[doc = r"=^.^="] +/// struct SomeItem {} +/// ``` +/// +/// Would result in `Hello world!\n=^.^=\n` +fn extract_attr_docs(item: &Item<'_>) -> Option { + item.attrs + .iter() + .filter_map(|ref x| x.doc_str()) + .fold(None, |acc, sym| { + let mut doc_str = sym.as_str().to_string(); + doc_str.push('\n'); + + #[allow(clippy::option_if_let_else)] // See clippy#6737 + if let Some(mut x) = acc { + x.push_str(&doc_str); + Some(x) + } else { + Some(doc_str) + } + + // acc.map_or(Some(doc_str), |mut x| { + // x.push_str(&doc_str); + // Some(x) + // }) + }) +} + +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { + for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { + if lints.iter().any(|x| *x == lint_id) { + return Some((*group_name).to_string()); + } + } + + None +} + +fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { + span_lint( + cx, + INTERNAL_METADATA_COLLECTOR, + item.ident.span, + &format!("Metadata collection error for `{}`: {}", item.ident.name, message), + ); +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d8b31344e6d..b67448e3a57 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(feature = "internal-lints")] +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] pub mod internal_lints; diff --git a/tests/dogfood.rs b/tests/dogfood.rs index d92530f073f..b0da0bfcc35 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,3 +1,8 @@ +//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and +//! long error messages +//! +//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context + // Dogfood cannot run on Windows #![cfg(not(windows))] #![feature(once_cell)] @@ -36,6 +41,10 @@ fn dogfood_clippy() { command.args(&["-D", "clippy::internal"]); } + if cfg!(feature = "metadata-collector-lint") { + command.args(&["-D", "clippy::internal"]); + } + let output = command.output().unwrap(); println!("status: {}", output.status); -- cgit 1.4.1-3-g733a5 From 060e0e9f9382cb7975165cede8d04b7bbe26bcb2 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 14 Feb 2021 16:17:07 +0100 Subject: Metadata collection lint: Basic applicability collection --- .../src/utils/internal_lints/metadata_collector.rs | 178 +++++++++++++++++---- clippy_utils/src/paths.rs | 2 + 2 files changed, 147 insertions(+), 33 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index dc4267697cb..677f9fcc83f 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -2,10 +2,14 @@ //! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) //! //! This module and therefor the entire lint is guarded by a feature flag called -//! `internal_metadata_lint` +//! `metadata-collector-lint` +//! +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such +//! a simple mistake) //! //! The metadata currently contains: -//! - [ ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) +//! - [x] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) //! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) //! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) @@ -17,20 +21,34 @@ // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; -use rustc_hir::{ExprKind, Item, ItemKind, Mutability}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Loc, Span}; +use rustc_span::{sym, Loc, Span, Symbol}; use serde::Serialize; use std::fs::OpenOptions; use std::io::prelude::*; use crate::utils::internal_lints::is_lint_ref_type; -use crate::utils::span_lint; +use crate::utils::{last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth}; +/// This is the output file of the lint collector. const OUTPUT_FILE: &str = "metadata_collection.json"; +/// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +// TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special +// handling +#[rustfmt::skip] +const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ + &["clippy_lints", "utils", "diagnostics", "span_lint"], + &["clippy_lints", "utils", "diagnostics", "span_lint_and_help"], + &["clippy_lints", "utils", "diagnostics", "span_lint_and_note"], + &["clippy_lints", "utils", "diagnostics", "span_lint_hir"], + &["clippy_lints", "utils", "diagnostics", "span_lint_and_sugg"], +]; + declare_clippy_lint! { /// **What it does:** Collects metadata about clippy lints for the website. /// @@ -66,12 +84,21 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[derive(Debug, Clone, Default)] pub struct MetadataCollector { lints: Vec, + applicability_into: FxHashMap, } impl Drop for MetadataCollector { + /// You might ask: How hacky is this? + /// My answer: YES fn drop(&mut self) { - // You might ask: How hacky is this? - // My answer: YES + let mut applicability_info = std::mem::take(&mut self.applicability_into); + + // Mapping the final data + self.lints + .iter_mut() + .for_each(|x| x.applicability = applicability_info.remove(&x.id)); + + // Outputting let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); } @@ -83,6 +110,21 @@ struct LintMetadata { id_span: SerializableSpan, group: String, docs: String, + /// This field is only used in the output and will only be + /// mapped shortly before the actual output. + applicability: Option, +} + +impl LintMetadata { + fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { + Self { + id, + id_span, + group, + docs, + applicability: None, + } + } } #[derive(Debug, Clone, Serialize)] @@ -101,12 +143,31 @@ impl SerializableSpan { Self { path: format!("{}", loc.file.name), - line: 1, + line: loc.line, } } } +#[derive(Debug, Clone, Default, Serialize)] +struct ApplicabilityInfo { + /// Indicates if any of the lint emissions uses multiple spans. This is related to + /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can + /// currently not be applied automatically. + has_multi_suggestion: bool, + /// These are all the available applicability values for the lint suggestions + applicabilities: FxHashSet, +} + impl<'tcx> LateLintPass<'tcx> for MetadataCollector { + /// Collecting lint declarations like: + /// ```rust, ignore + /// declare_clippy_lint! { + /// /// **What it does:** Something IDK. + /// pub SOME_LINT, + /// internal, + /// "Who am I?" + /// } + /// ``` fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; @@ -115,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; if let ExprKind::Struct(_, _, _) = inner_exp.kind; then { - let lint_name = item.ident.name.as_str().to_string().to_ascii_lowercase(); + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { return; } @@ -126,11 +187,11 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { group = group_some; } else { - lint_collection_error(cx, item, "Unable to determine lint group"); + lint_collection_error_item(cx, item, "Unable to determine lint group"); return; } } else { - lint_collection_error(cx, item, "Unable to find lint in lint_store"); + lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); return; } @@ -138,21 +199,38 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { if let Some(docs_some) = extract_attr_docs(item) { docs = docs_some; } else { - lint_collection_error(cx, item, "could not collect the lint documentation"); + lint_collection_error_item(cx, item, "could not collect the lint documentation"); return; }; - self.lints.push(LintMetadata { - id: lint_name, - id_span: SerializableSpan::from_item(cx, item), + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), group, docs, + )); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if let Some(args) = match_simple_lint_emission(cx, expr) { + if let Some((lint_name, mut applicability)) = extract_emission_info(cx, args) { + let app_info = self.applicability_into.entry(lint_name).or_default(); + applicability.drain(..).for_each(|x| { + app_info.applicabilities.insert(x); }); + } else { + lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); } } } } +fn sym_to_string(sym: Symbol) -> String { + sym.as_str().to_string() +} + /// This function collects all documentation that has been added to an item using /// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks /// @@ -166,23 +244,11 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { fn extract_attr_docs(item: &Item<'_>) -> Option { item.attrs .iter() - .filter_map(|ref x| x.doc_str()) - .fold(None, |acc, sym| { - let mut doc_str = sym.as_str().to_string(); - doc_str.push('\n'); - - #[allow(clippy::option_if_let_else)] // See clippy#6737 - if let Some(mut x) = acc { - x.push_str(&doc_str); - Some(x) - } else { - Some(doc_str) - } - - // acc.map_or(Some(doc_str), |mut x| { - // x.push_str(&doc_str); - // Some(x) - // }) + .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string())) + .reduce(|mut acc, sym| { + acc.push_str(&sym); + acc.push('\n'); + acc }) } @@ -196,7 +262,7 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { None } -fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { span_lint( cx, INTERNAL_METADATA_COLLECTOR, @@ -204,3 +270,49 @@ fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { &format!("Metadata collection error for `{}`: {}", item.ident.name, message), ); } + +fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { + span_lint( + cx, + INTERNAL_METADATA_COLLECTOR, + span, + &format!("Metadata collection error: {}", message), + ); +} + +fn match_simple_lint_emission<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, +) -> Option<&'tcx [hir::Expr<'tcx>]> { + LINT_EMISSION_FUNCTIONS + .iter() + .find_map(|emission_fn| match_function_call(cx, expr, emission_fn).map(|args| args)) +} + +/// This returns the lint name and the possible applicability of this emission +fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) -> Option<(String, Vec)> { + let mut lint_name = None; + let mut applicability = None; + + for arg in args { + let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg)); + + if match_type(cx, arg_ty, &paths::LINT) { + // If we found the lint arg, extract the lint name + if let ExprKind::Path(ref lint_path) = arg.kind { + lint_name = Some(last_path_segment(lint_path).ident.name) + } + } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { + if let ExprKind::Path(ref path) = arg.kind { + applicability = Some(last_path_segment(path).ident.name) + } + } + } + + lint_name.map(|lint_name| { + ( + sym_to_string(lint_name).to_ascii_lowercase(), + applicability.map(sym_to_string).map_or_else(Vec::new, |x| vec![x]), + ) + }) +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5e6733a300f..91e5cd6c046 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -5,6 +5,8 @@ //! See for more information. pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; +#[cfg(feature = "metadata-collector-lint")] +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; -- cgit 1.4.1-3-g733a5 From 68d702f88ded863b9cde999f1850b32c6531fe11 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 18 Feb 2021 00:20:19 +0100 Subject: Metadata collection lint: Start Applicability value tracking --- .../src/utils/internal_lints/metadata_collector.rs | 158 ++++++++++++++++++++- 1 file changed, 155 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 677f9fcc83f..2bdb92376ca 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -24,14 +24,21 @@ use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; +use rustc_middle::ty::BorrowKind; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId}; +use rustc_typeck::hir_ty_to_ty; use serde::Serialize; -use std::fs::OpenOptions; +use std::fs::{self, OpenOptions}; use std::io::prelude::*; +use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; -use crate::utils::{last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth}; +use crate::utils::{ + last_path_segment, match_function_call, match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, +}; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "metadata_collection.json"; @@ -91,6 +98,10 @@ impl Drop for MetadataCollector { /// You might ask: How hacky is this? /// My answer: YES fn drop(&mut self) { + if self.lints.is_empty() { + return; + } + let mut applicability_info = std::mem::take(&mut self.applicability_into); // Mapping the final data @@ -99,6 +110,9 @@ impl Drop for MetadataCollector { .for_each(|x| x.applicability = applicability_info.remove(&x.id)); // Outputting + if Path::new(OUTPUT_FILE).exists() { + fs::remove_file(OUTPUT_FILE).unwrap(); + } let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); } @@ -158,6 +172,17 @@ struct ApplicabilityInfo { applicabilities: FxHashSet, } +fn log_to_file(msg: &str) { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open("metadata-lint.log") + .unwrap(); + + write!(file, "{}", msg).unwrap(); +} + impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// Collecting lint declarations like: /// ```rust, ignore @@ -213,6 +238,20 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { } } + /// Collecting constant applicability from the actual lint emissions + /// + /// Example: + /// ```rust, ignore + /// span_lint_and_sugg( + /// cx, + /// SOME_LINT, + /// item.span, + /// "Le lint message", + /// "Here comes help:", + /// "#![allow(clippy::all)]", + /// Applicability::MachineApplicable, // <-- Extracts this constant value + /// ); + /// ``` fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(args) = match_simple_lint_emission(cx, expr) { if let Some((lint_name, mut applicability)) = extract_emission_info(cx, args) { @@ -225,6 +264,73 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { } } } + + /// Tracking and hopefully collecting dynamic applicability values + /// + /// Example: + /// ```rust, ignore + /// // vvv Applicability value to track + /// let mut applicability = Applicability::MachineApplicable; + /// // vvv Value Mutation + /// let suggestion = snippet_with_applicability(cx, expr.span, "_", &mut applicability); + /// // vvv Emission to link the value to the lint + /// span_lint_and_sugg( + /// cx, + /// SOME_LINT, + /// expr.span, + /// "This can be improved", + /// "try", + /// suggestion, + /// applicability, + /// ); + /// ``` + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + if let Some(tc) = cx.maybe_typeck_results() { + // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) + let local_ty = if let Some(ty) = local.ty { + hir_ty_to_ty(cx.tcx, ty) + } else if let Some(init) = local.init { + tc.expr_ty(init) + } else { + return; + }; + + if_chain! { + if match_type(cx, local_ty, &paths::APPLICABILITY); + if let Some(body) = get_parent_body(cx, local.hir_id); + then { + let span = SerializableSpan::from_span(cx, local.span); + let local_str = crate::utils::snippet(cx, local.span, "_"); + let value_life = format!("{} -- {}:{}\n", local_str, span.path.rsplit('/').next().unwrap_or_default(), span.line); + let value_hir_id = local.pat.hir_id; + let mut tracker = ValueTracker {cx, value_hir_id, value_life}; + + cx.tcx.infer_ctxt().enter(|infcx| { + let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); + ExprUseVisitor::new( + &mut tracker, + &infcx, + body_owner_id, + cx.param_env, + cx.typeck_results() + ) + .consume_body(body); + }); + + log_to_file(&tracker.value_life); + lint_collection_error_span(cx, local.span, "Applicability value found"); + } + } + } + } +} + +fn get_parent_body<'a, 'tcx>(cx: &'a LateContext<'tcx>, id: hir::HirId) -> Option<&'tcx hir::Body<'tcx>> { + let map = cx.tcx.hir(); + + map.parent_iter(id) + .find_map(|(parent, _)| map.maybe_body_owned_by(parent)) + .map(|body| map.body(body)) } fn sym_to_string(sym: Symbol) -> String { @@ -262,6 +368,9 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { None } +// ================================================================== +// Lint emission +// ================================================================== fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { span_lint( cx, @@ -280,13 +389,16 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { ); } +// ================================================================== +// Applicability +// ================================================================== fn match_simple_lint_emission<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ) -> Option<&'tcx [hir::Expr<'tcx>]> { LINT_EMISSION_FUNCTIONS .iter() - .find_map(|emission_fn| match_function_call(cx, expr, emission_fn).map(|args| args)) + .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } /// This returns the lint name and the possible applicability of this emission @@ -316,3 +428,43 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - ) }) } + +struct ValueTracker<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + value_hir_id: hir::HirId, + value_life: String, +} + +impl<'a, 'tcx> ValueTracker<'a, 'tcx> { + fn is_value_expr(&self, expr_id: hir::HirId) -> bool { + match self.cx.tcx.hir().find(expr_id) { + Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), + _ => false, + } + } +} + +impl<'a, 'tcx> Delegate<'tcx> for ValueTracker<'a, 'tcx> { + fn consume(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, _: ConsumeMode) { + if self.is_value_expr(expr_id) { + // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID + todo!(); + } + } + + fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, bk: BorrowKind) { + if self.is_value_expr(expr_id) { + if let BorrowKind::MutBorrow = bk { + // TODO xFrednet 2021-02-17: Save the function + todo!(); + } + } + } + + fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) { + if self.is_value_expr(expr_id) { + // TODO xFrednet 2021-02-17: Save the new value as a mutation + todo!(); + } + } +} -- cgit 1.4.1-3-g733a5 From 8dca1b8f618fdec3f1b354d9bafa01b32ecc8ab6 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 18 Feb 2021 23:40:33 +0100 Subject: Metadata collection: Collecting Applicability assign values --- .../src/utils/internal_lints/metadata_collector.rs | 249 ++++++++++++++++----- clippy_utils/src/paths.rs | 7 + .../track_applicability_value.rs | 46 ++++ 3 files changed, 248 insertions(+), 54 deletions(-) create mode 100644 tests/ui-internal/metadata-collector/track_applicability_value.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 2bdb92376ca..e22aa285f17 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -24,7 +24,7 @@ use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; -use rustc_middle::ty::BorrowKind; +use rustc_middle::ty::{BorrowKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; use rustc_trait_selection::infer::TyCtxtInferExt; @@ -37,7 +37,8 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - last_path_segment, match_function_call, match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, + path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -147,6 +148,12 @@ struct SerializableSpan { line: usize, } +impl std::fmt::Display for SerializableSpan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line) + } +} + impl SerializableSpan { fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self { Self::from_span(cx, item.ident.span) @@ -285,52 +292,54 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// ); /// ``` fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { - if let Some(tc) = cx.maybe_typeck_results() { - // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) - let local_ty = if let Some(ty) = local.ty { - hir_ty_to_ty(cx.tcx, ty) - } else if let Some(init) = local.init { - tc.expr_ty(init) - } else { - return; - }; - - if_chain! { - if match_type(cx, local_ty, &paths::APPLICABILITY); - if let Some(body) = get_parent_body(cx, local.hir_id); - then { - let span = SerializableSpan::from_span(cx, local.span); - let local_str = crate::utils::snippet(cx, local.span, "_"); - let value_life = format!("{} -- {}:{}\n", local_str, span.path.rsplit('/').next().unwrap_or_default(), span.line); - let value_hir_id = local.pat.hir_id; - let mut tracker = ValueTracker {cx, value_hir_id, value_life}; - - cx.tcx.infer_ctxt().enter(|infcx| { - let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); - ExprUseVisitor::new( - &mut tracker, - &infcx, - body_owner_id, - cx.param_env, - cx.typeck_results() - ) - .consume_body(body); - }); - - log_to_file(&tracker.value_life); - lint_collection_error_span(cx, local.span, "Applicability value found"); + if_chain! { + if let Some(local_ty) = get_local_type(cx, local); + if match_type(cx, local_ty, &paths::APPLICABILITY); + if let Some(body) = get_enclosing_body(cx, local.hir_id); + then { + // TODO xFrednet: 2021-02-19: Remove debug code + let span = SerializableSpan::from_span(cx, local.span); + let local_str = crate::utils::snippet(cx, local.span, "_"); + log_to_file(&format!("{} -- {}\n", local_str, span)); + + let value_hir_id = local.pat.hir_id; + let mut tracker = ValueTracker::new(cx, value_hir_id); + if let Some(init_expr) = local.init { + tracker.process_assign_expr(init_expr) } + + // TODO xFrednet 2021-02-18: Support nested bodies + // Note: The `ExprUseVisitor` only searches though one body, this means that values + // references in nested bodies like closures are not found by this simple visitor. + cx.tcx.infer_ctxt().enter(|infcx| { + let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); + ExprUseVisitor::new( + &mut tracker, + &infcx, + body_owner_id, + cx.param_env, + cx.typeck_results() + ) + .consume_body(body); + }); + + log_to_file(&format!("{:?}\n", tracker.value_mutations)); } } } } -fn get_parent_body<'a, 'tcx>(cx: &'a LateContext<'tcx>, id: hir::HirId) -> Option<&'tcx hir::Body<'tcx>> { - let map = cx.tcx.hir(); +fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Option> { + // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) + if let Some(tc) = cx.maybe_typeck_results() { + if let Some(ty) = local.ty { + return Some(hir_ty_to_ty(cx.tcx, ty)); + } else if let Some(init) = local.init { + return Some(tc.expr_ty(init)); + } + } - map.parent_iter(id) - .find_map(|(parent, _)| map.maybe_body_owned_by(parent)) - .map(|body| map.body(body)) + None } fn sym_to_string(sym: Symbol) -> String { @@ -429,42 +438,174 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - }) } -struct ValueTracker<'a, 'tcx> { - cx: &'a LateContext<'tcx>, +#[allow(dead_code)] +struct ValueTracker<'a, 'hir> { + cx: &'a LateContext<'hir>, value_hir_id: hir::HirId, - value_life: String, + value_mutations: Vec>, } -impl<'a, 'tcx> ValueTracker<'a, 'tcx> { +impl<'a, 'hir> ValueTracker<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>, value_hir_id: hir::HirId) -> Self { + Self { + cx, + value_hir_id, + value_mutations: Vec::new(), + } + } + fn is_value_expr(&self, expr_id: hir::HirId) -> bool { match self.cx.tcx.hir().find(expr_id) { Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), _ => false, } } + + /// This function extracts possible `ApplicabilityModifier` from an assign statement like this: + /// + /// ```rust, ignore + /// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv The expression to process + /// let value = Applicability::MachineApplicable; + /// ``` + fn process_assign_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + // This is a bit more complicated. I'll therefor settle on the simple solution of + // simplifying the cases we support. + match &expr.kind { + hir::ExprKind::Call(func_expr, ..) => { + // We only deal with resolved paths as this is the usual case. Other expression kinds like closures + // etc. are hard to track but might be a worthy improvement in the future + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { + self.value_mutations.push(ApplicabilityModifier::Producer(path)); + } else { + let msg = format!( + "Unsupported Call expression at: {}", + SerializableSpan::from_span(self.cx, func_expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + } + }, + hir::ExprKind::MethodCall(..) => { + let msg = format!( + "Unsupported MethodCall expression at: {}", + SerializableSpan::from_span(self.cx, expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + // We can ignore ifs without an else block because those can't be used as an assignment + hir::ExprKind::If(_con, if_block, Some(else_block)) => { + self.process_assign_expr(if_block); + self.process_assign_expr(else_block); + }, + hir::ExprKind::Match(_expr, arms, _) => { + for arm in *arms { + self.process_assign_expr(arm.body); + } + }, + hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + if let Some(block_expr) = block.expr { + self.process_assign_expr(block_expr); + } + }, + hir::ExprKind::Path(path) => { + for enum_value in &paths::APPLICABILITY_VALUES { + if match_qpath(path, enum_value) { + self.value_mutations + .push(ApplicabilityModifier::ConstValue(enum_value[2].to_string())); + } + } + }, + // hir::ExprKind::Field(expr, ident) => not supported + // hir::ExprKind::Index(expr, expr) => not supported + _ => { + let msg = format!( + "Unexpected expression at: {}", + SerializableSpan::from_span(self.cx, expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + } + } } -impl<'a, 'tcx> Delegate<'tcx> for ValueTracker<'a, 'tcx> { - fn consume(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, _: ConsumeMode) { +impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { + fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { if self.is_value_expr(expr_id) { // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - todo!(); + if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { + let span = SerializableSpan::from_span(self.cx, expr.span); + log_to_file(&format!("- consume {}\n", span)); + } } } - fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, bk: BorrowKind) { + fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { if self.is_value_expr(expr_id) { if let BorrowKind::MutBorrow = bk { // TODO xFrednet 2021-02-17: Save the function - todo!(); + if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { + let span = SerializableSpan::from_span(self.cx, expr.span); + log_to_file(&format!("- &mut {}\n", span)); + } } } } - fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) { - if self.is_value_expr(expr_id) { - // TODO xFrednet 2021-02-17: Save the new value as a mutation - todo!(); + fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'hir>, expr_id: hir::HirId) { + if_chain! { + if self.is_value_expr(expr_id); + if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id); + if let hir::ExprKind::Assign(_value_expr, assign_expr, ..) = expr.kind; + then { + self.process_assign_expr(assign_expr); + } } } } + +/// The life of a value in Rust is a true adventure. These are the corner stones of such a +/// fairy tale. Let me introduce you to the possible stepping stones a value might have in +/// in our crazy word: +#[derive(Debug)] +#[allow(dead_code)] +enum ApplicabilityModifier<'hir> { + Unknown(String), + /// A simple constant value. + /// + /// This is the actual character of a value. It's baseline. This only defines where the value + /// started. As in real life it can still change and fully decide who it wants to be. + ConstValue(String), + /// A producer is a function that returns an applicability value. + /// + /// This is the heritage of this value. This value comes from a long family tree and is not + /// just a black piece of paper. The evaluation of this stepping stone needs additional + /// context. We therefore only add a reference. This reference will later be used to ask + /// the librarian about the possible initial character that this value might have. + Producer(&'hir hir::Path<'hir>), + /// A modifier that takes the given applicability and might modify it + /// + /// What would an RPG be without it's NPCs. The special thing about modifiers is that they can + /// be actively interested in the story of the value and might make decisions based on the + /// character of this hero. This means that a modifier doesn't just force its way into the life + /// of our hero but it actually asks him how he's been. The possible modification is a result + /// of the situation. + /// + /// Take this part of our heroes life very seriously! + Modifier(&'hir hir::Path<'hir>), + /// The actual emission of a lint + /// + /// All good things must come to an end. Even the life of your awesome applicability hero. He + /// was the bravest soul that has ever wondered this earth. Songs will be written about his + /// heroic deeds. Castles will be named after him and the world how we know it will never be + /// the same! + /// + /// Is this a happy ending? Did he archive what he wanted in his life? Yes, YES, he has lived a + /// life and he will continue to live in all the lint suggestions that can be applied or just + /// displayed by Clippy. He might be no more, but his legacy will serve generations to come. + LintEmit(LintEmission), +} + +#[derive(Debug)] +struct LintEmission { + lint: String, + is_multi_line_sugg: bool, +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 91e5cd6c046..46f58b788e6 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -7,6 +7,13 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; #[cfg(feature = "metadata-collector-lint")] pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; +#[cfg(feature = "metadata-collector-lint")] +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ + ["rustc_lint_defs", "Applicability", "MachineApplicable"], + ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], + ["rustc_lint_defs", "Applicability", "HasPlaceholders"], + ["rustc_lint_defs", "Applicability", "Unspecified"], +]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs new file mode 100644 index 00000000000..75fae623e61 --- /dev/null +++ b/tests/ui-internal/metadata-collector/track_applicability_value.rs @@ -0,0 +1,46 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +fn producer_fn() -> Applicability { + Applicability::MachineApplicable +} + +fn modifier_fn(applicability: &mut Applicability) { + if let Applicability::MaybeIncorrect = applicability { + *applicability = Applicability::HasPlaceholders; + } +} + +struct Muh; + +impl Muh { + fn producer_method() -> Applicability { + Applicability::MachineApplicable + } +} + +fn main() { + let mut applicability = producer_fn(); + applicability = Applicability::MachineApplicable; + applicability = Muh::producer_method(); + + applicability = if true { + Applicability::HasPlaceholders + } else { + Applicability::MaybeIncorrect + }; + + modifier_fn(&mut applicability); +} -- cgit 1.4.1-3-g733a5 From ee130d066d362118cd0bf756de42ee08a2bfc301 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 00:35:28 +0100 Subject: Metadata collection: Tracking Applicability mut borrows --- .../src/utils/internal_lints/metadata_collector.rs | 57 ++++++++++++++++++---- 1 file changed, 48 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index e22aa285f17..c8637531d94 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -38,7 +38,7 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, - path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, get_parent_expr }; /// This is the output file of the lint collector. @@ -478,7 +478,7 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { self.value_mutations.push(ApplicabilityModifier::Producer(path)); } else { let msg = format!( - "Unsupported Call expression at: {}", + "Unsupported assign Call expression at: {}", SerializableSpan::from_span(self.cx, func_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); @@ -486,7 +486,7 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { }, hir::ExprKind::MethodCall(..) => { let msg = format!( - "Unsupported MethodCall expression at: {}", + "Unsupported assign MethodCall expression at: {}", SerializableSpan::from_span(self.cx, expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); @@ -518,13 +518,56 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { // hir::ExprKind::Index(expr, expr) => not supported _ => { let msg = format!( - "Unexpected expression at: {}", + "Unexpected assign expression at: {}", SerializableSpan::from_span(self.cx, expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, } } + + fn process_borrow_expr(&mut self, access_hir_id: hir::HirId) { + let borrower: &rustc_hir::Expr<'_>; + if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, access_hir_id) { + if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr) { + borrower = borrower_expr + } else { + return; + } + } else { + return; + } + + match &borrower.kind { + hir::ExprKind::Call(func_expr, ..) => { + // We only deal with resolved paths as this is the usual case. Other expression kinds like closures + // etc. are hard to track but might be a worthy improvement in the future + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { + self.value_mutations.push(ApplicabilityModifier::Modifier(path)); + } else { + let msg = format!( + "Unsupported borrow in Call at: {}", + SerializableSpan::from_span(self.cx, func_expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + } + }, + hir::ExprKind::MethodCall(..) => { + let msg = format!( + "Unsupported borrow in MethodCall at: {}", + SerializableSpan::from_span(self.cx, borrower.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + _ => { + let msg = format!( + "Unexpected borrow at: {}", + SerializableSpan::from_span(self.cx, borrower.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + } + } } impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { @@ -541,11 +584,7 @@ impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { if self.is_value_expr(expr_id) { if let BorrowKind::MutBorrow = bk { - // TODO xFrednet 2021-02-17: Save the function - if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { - let span = SerializableSpan::from_span(self.cx, expr.span); - log_to_file(&format!("- &mut {}\n", span)); - } + self.process_borrow_expr(expr_id); } } } -- cgit 1.4.1-3-g733a5 From a39912cfbbd682270b5f3d534bc69eedfaa73a7b Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 13:12:30 +0100 Subject: Metadata collection: Some refactoring for readability --- .../src/utils/internal_lints/metadata_collector.rs | 104 +++++++++++---------- .../track_applicability_value.rs | 4 + 2 files changed, 57 insertions(+), 51 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c8637531d94..de94e47047b 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -37,8 +37,8 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, - path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, get_parent_expr + get_enclosing_body, get_parent_expr, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, + match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -202,39 +202,19 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// ``` fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { + // item validation if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; if is_lint_ref_type(cx, ty); let expr = &cx.tcx.hir().body(body_id).value; if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; if let ExprKind::Struct(_, _, _) = inner_exp.kind; + // blacklist check + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); + // metadata extraction + if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); + if let Some(docs) = extract_attr_docs_or_lint(cx, item); then { - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); - if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { - return; - } - - let group: String; - let result = cx.lint_store.check_lint_name(lint_name.as_str(), Some(sym::clippy)); - if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { - group = group_some; - } else { - lint_collection_error_item(cx, item, "Unable to determine lint group"); - return; - } - } else { - lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); - return; - } - - let docs: String; - if let Some(docs_some) = extract_attr_docs(item) { - docs = docs_some; - } else { - lint_collection_error_item(cx, item, "could not collect the lint documentation"); - return; - }; - self.lints.push(LintMetadata::new( lint_name, SerializableSpan::from_item(cx, item), @@ -301,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { let span = SerializableSpan::from_span(cx, local.span); let local_str = crate::utils::snippet(cx, local.span, "_"); log_to_file(&format!("{} -- {}\n", local_str, span)); - + let value_hir_id = local.pat.hir_id; let mut tracker = ValueTracker::new(cx, value_hir_id); if let Some(init_expr) = local.init { @@ -342,10 +322,21 @@ fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Opt None } +// ================================================================== +// Lint definition extraction +// ================================================================== + fn sym_to_string(sym: Symbol) -> String { sym.as_str().to_string() } +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option { + extract_attr_docs(item).or_else(|| { + lint_collection_error_item(cx, item, "could not collect the lint documentation"); + None + }) +} + /// This function collects all documentation that has been added to an item using /// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks /// @@ -367,6 +358,19 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { }) } +fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'tcx Item<'_>) -> Option { + let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); + if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + get_lint_group(cx, lint_lst[0]).or_else(|| { + lint_collection_error_item(cx, item, "Unable to determine lint group"); + None + }) + } else { + lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); + None + } +} + fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { if lints.iter().any(|x| *x == lint_id) { @@ -526,19 +530,8 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { } } - fn process_borrow_expr(&mut self, access_hir_id: hir::HirId) { - let borrower: &rustc_hir::Expr<'_>; - if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, access_hir_id) { - if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr) { - borrower = borrower_expr - } else { - return; - } - } else { - return; - } - - match &borrower.kind { + fn process_borrow_expr(&mut self, borrower_expr: &'hir rustc_hir::Expr<'hir>) { + match &borrower_expr.kind { hir::ExprKind::Call(func_expr, ..) => { // We only deal with resolved paths as this is the usual case. Other expression kinds like closures // etc. are hard to track but might be a worthy improvement in the future @@ -555,36 +548,45 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { hir::ExprKind::MethodCall(..) => { let msg = format!( "Unsupported borrow in MethodCall at: {}", - SerializableSpan::from_span(self.cx, borrower.span) + SerializableSpan::from_span(self.cx, borrower_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, _ => { let msg = format!( "Unexpected borrow at: {}", - SerializableSpan::from_span(self.cx, borrower.span) + SerializableSpan::from_span(self.cx, borrower_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, } } + + fn process_consume_expr(&mut self, consume_expr: &'hir rustc_hir::Expr<'hir>) { + // We are only interested in lint emissions. Other types like assignments might be + // interesting for further use or improvement but are to complex for now. + if let hir::ExprKind::Call(func_expr, ..) = &consume_expr.kind {} + } } impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { if self.is_value_expr(expr_id) { // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { - let span = SerializableSpan::from_span(self.cx, expr.span); - log_to_file(&format!("- consume {}\n", span)); + if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id) { + log_to_file(&format!("- consume {:?}\n", expr)); } } } fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { - if self.is_value_expr(expr_id) { - if let BorrowKind::MutBorrow = bk { - self.process_borrow_expr(expr_id); + if_chain! { + if self.is_value_expr(expr_id); + if let BorrowKind::MutBorrow = bk; + if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, expr_id); + if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr); + then { + self.process_borrow_expr(borrower_expr); } } } diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs index 75fae623e61..b0f59e5cf8a 100644 --- a/tests/ui-internal/metadata-collector/track_applicability_value.rs +++ b/tests/ui-internal/metadata-collector/track_applicability_value.rs @@ -23,6 +23,8 @@ fn modifier_fn(applicability: &mut Applicability) { } } +fn consumer_fn(_applicability: Applicability) {} + struct Muh; impl Muh { @@ -43,4 +45,6 @@ fn main() { }; modifier_fn(&mut applicability); + + consumer_fn(applicability); } -- cgit 1.4.1-3-g733a5 From ee8a99a1140880e10677fa0b03167cd364fece3f Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 13:50:09 +0100 Subject: Metadata collection: Collecting Applicability assign values --- clippy_lints/Cargo.toml | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 361 ++++----------------- clippy_utils/Cargo.toml | 1 + clippy_utils/src/lib.rs | 8 +- 4 files changed, 73 insertions(+), 299 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d1d56fc84f9..42218c2c00a 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -33,7 +33,7 @@ url = { version = "2.1.0", features = ["serde"] } deny-warnings = [] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] -metadata-collector-lint = ["serde_json"] +metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index de94e47047b..a16f7c2ea5e 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -21,15 +21,11 @@ // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; -use rustc_middle::ty::{BorrowKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; -use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId}; -use rustc_typeck::hir_ty_to_ty; use serde::Serialize; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -37,8 +33,7 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - get_enclosing_body, get_parent_expr, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, - match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth, match_path, }; /// This is the output file of the lint collector. @@ -50,11 +45,11 @@ const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; // handling #[rustfmt::skip] const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ - &["clippy_lints", "utils", "diagnostics", "span_lint"], - &["clippy_lints", "utils", "diagnostics", "span_lint_and_help"], - &["clippy_lints", "utils", "diagnostics", "span_lint_and_note"], - &["clippy_lints", "utils", "diagnostics", "span_lint_hir"], - &["clippy_lints", "utils", "diagnostics", "span_lint_and_sugg"], + &["clippy_utils", "diagnostics", "span_lint"], + &["clippy_utils", "diagnostics", "span_lint_and_help"], + &["clippy_utils", "diagnostics", "span_lint_and_note"], + &["clippy_utils", "diagnostics", "span_lint_hir"], + &["clippy_utils", "diagnostics", "span_lint_and_sugg"], ]; declare_clippy_lint! { @@ -175,10 +170,10 @@ struct ApplicabilityInfo { /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can /// currently not be applied automatically. has_multi_suggestion: bool, - /// These are all the available applicability values for the lint suggestions - applicabilities: FxHashSet, + applicability: Option, } +#[allow(dead_code)] fn log_to_file(msg: &str) { let mut file = OpenOptions::new() .write(true) @@ -190,7 +185,7 @@ fn log_to_file(msg: &str) { write!(file, "{}", msg).unwrap(); } -impl<'tcx> LateLintPass<'tcx> for MetadataCollector { +impl<'hir> LateLintPass<'hir> for MetadataCollector { /// Collecting lint declarations like: /// ```rust, ignore /// declare_clippy_lint! { @@ -200,7 +195,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// "Who am I?" /// } /// ``` - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { if_chain! { // item validation if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; @@ -239,87 +234,16 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// Applicability::MachineApplicable, // <-- Extracts this constant value /// ); /// ``` - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { if let Some(args) = match_simple_lint_emission(cx, expr) { - if let Some((lint_name, mut applicability)) = extract_emission_info(cx, args) { + if let Some((lint_name, applicability)) = extract_emission_info(cx, args) { let app_info = self.applicability_into.entry(lint_name).or_default(); - applicability.drain(..).for_each(|x| { - app_info.applicabilities.insert(x); - }); + app_info.applicability = applicability; } else { lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); } } } - - /// Tracking and hopefully collecting dynamic applicability values - /// - /// Example: - /// ```rust, ignore - /// // vvv Applicability value to track - /// let mut applicability = Applicability::MachineApplicable; - /// // vvv Value Mutation - /// let suggestion = snippet_with_applicability(cx, expr.span, "_", &mut applicability); - /// // vvv Emission to link the value to the lint - /// span_lint_and_sugg( - /// cx, - /// SOME_LINT, - /// expr.span, - /// "This can be improved", - /// "try", - /// suggestion, - /// applicability, - /// ); - /// ``` - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { - if_chain! { - if let Some(local_ty) = get_local_type(cx, local); - if match_type(cx, local_ty, &paths::APPLICABILITY); - if let Some(body) = get_enclosing_body(cx, local.hir_id); - then { - // TODO xFrednet: 2021-02-19: Remove debug code - let span = SerializableSpan::from_span(cx, local.span); - let local_str = crate::utils::snippet(cx, local.span, "_"); - log_to_file(&format!("{} -- {}\n", local_str, span)); - - let value_hir_id = local.pat.hir_id; - let mut tracker = ValueTracker::new(cx, value_hir_id); - if let Some(init_expr) = local.init { - tracker.process_assign_expr(init_expr) - } - - // TODO xFrednet 2021-02-18: Support nested bodies - // Note: The `ExprUseVisitor` only searches though one body, this means that values - // references in nested bodies like closures are not found by this simple visitor. - cx.tcx.infer_ctxt().enter(|infcx| { - let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); - ExprUseVisitor::new( - &mut tracker, - &infcx, - body_owner_id, - cx.param_env, - cx.typeck_results() - ) - .consume_body(body); - }); - - log_to_file(&format!("{:?}\n", tracker.value_mutations)); - } - } - } -} - -fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Option> { - // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) - if let Some(tc) = cx.maybe_typeck_results() { - if let Some(ty) = local.ty { - return Some(hir_ty_to_ty(cx.tcx, ty)); - } else if let Some(init) = local.init { - return Some(tc.expr_ty(init)); - } - } - - None } // ================================================================== @@ -358,7 +282,7 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { }) } -fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'tcx Item<'_>) -> Option { +fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option { let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { get_lint_group(cx, lint_lst[0]).or_else(|| { @@ -405,17 +329,19 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { // ================================================================== // Applicability // ================================================================== -fn match_simple_lint_emission<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, -) -> Option<&'tcx [hir::Expr<'tcx>]> { +/// This function checks if a given expression is equal to a simple lint emission function call. +/// It will return the function arguments if the emission matched any function. +fn match_simple_lint_emission<'hir>( + cx: &LateContext<'hir>, + expr: &'hir hir::Expr<'_>, +) -> Option<&'hir [hir::Expr<'hir>]> { LINT_EMISSION_FUNCTIONS .iter() .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } /// This returns the lint name and the possible applicability of this emission -fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) -> Option<(String, Vec)> { +fn extract_emission_info<'hir>(cx: &LateContext<'hir>, args: &[hir::Expr<'_>]) -> Option<(String, Option)> { let mut lint_name = None; let mut applicability = None; @@ -425,228 +351,69 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - if match_type(cx, arg_ty, &paths::LINT) { // If we found the lint arg, extract the lint name if let ExprKind::Path(ref lint_path) = arg.kind { - lint_name = Some(last_path_segment(lint_path).ident.name) + lint_name = Some(last_path_segment(lint_path).ident.name); } } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { - if let ExprKind::Path(ref path) = arg.kind { - applicability = Some(last_path_segment(path).ident.name) - } + applicability = resolve_applicability(cx, arg); } } lint_name.map(|lint_name| { ( sym_to_string(lint_name).to_ascii_lowercase(), - applicability.map(sym_to_string).map_or_else(Vec::new, |x| vec![x]), + applicability, ) }) } -#[allow(dead_code)] -struct ValueTracker<'a, 'hir> { - cx: &'a LateContext<'hir>, - value_hir_id: hir::HirId, - value_mutations: Vec>, -} - -impl<'a, 'hir> ValueTracker<'a, 'hir> { - fn new(cx: &'a LateContext<'hir>, value_hir_id: hir::HirId) -> Self { - Self { - cx, - value_hir_id, - value_mutations: Vec::new(), - } - } - - fn is_value_expr(&self, expr_id: hir::HirId) -> bool { - match self.cx.tcx.hir().find(expr_id) { - Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), - _ => false, - } - } - - /// This function extracts possible `ApplicabilityModifier` from an assign statement like this: - /// - /// ```rust, ignore - /// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv The expression to process - /// let value = Applicability::MachineApplicable; - /// ``` - fn process_assign_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - // This is a bit more complicated. I'll therefor settle on the simple solution of - // simplifying the cases we support. - match &expr.kind { - hir::ExprKind::Call(func_expr, ..) => { - // We only deal with resolved paths as this is the usual case. Other expression kinds like closures - // etc. are hard to track but might be a worthy improvement in the future - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { - self.value_mutations.push(ApplicabilityModifier::Producer(path)); - } else { - let msg = format!( - "Unsupported assign Call expression at: {}", - SerializableSpan::from_span(self.cx, func_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - } - }, - hir::ExprKind::MethodCall(..) => { - let msg = format!( - "Unsupported assign MethodCall expression at: {}", - SerializableSpan::from_span(self.cx, expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - // We can ignore ifs without an else block because those can't be used as an assignment - hir::ExprKind::If(_con, if_block, Some(else_block)) => { - self.process_assign_expr(if_block); - self.process_assign_expr(else_block); - }, - hir::ExprKind::Match(_expr, arms, _) => { - for arm in *arms { - self.process_assign_expr(arm.body); - } - }, - hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { - if let Some(block_expr) = block.expr { - self.process_assign_expr(block_expr); +fn resolve_applicability(cx: &LateContext<'hir>, expr: &hir::Expr<'_>) -> Option { + match expr.kind { + // We can ignore ifs without an else block because those can't be used as an assignment + hir::ExprKind::If(_con, _if_block, Some(_else_block)) => { + // self.process_assign_expr(if_block); + // self.process_assign_expr(else_block); + return Some("TODO IF EXPR".to_string()); + }, + hir::ExprKind::Match(_expr, _arms, _) => { + // for arm in *arms { + // self.process_assign_expr(arm.body); + // } + return Some("TODO MATCH EXPR".to_string()); + }, + hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + if let Some(block_expr) = block.expr { + return resolve_applicability(cx, block_expr); + } + }, + ExprKind::Path(hir::QPath::Resolved(_, path)) => { + // direct applicabilities are simple: + for enum_value in &paths::APPLICABILITY_VALUES { + if match_path(path, enum_value) { + return Some(enum_value[2].to_string()); } - }, - hir::ExprKind::Path(path) => { - for enum_value in &paths::APPLICABILITY_VALUES { - if match_qpath(path, enum_value) { - self.value_mutations - .push(ApplicabilityModifier::ConstValue(enum_value[2].to_string())); + } + + // Values yay + if let hir::def::Res::Local(local_hir) = path.res { + if let Some(local) = get_parent_local(cx, local_hir) { + if let Some(local_init) = local.init { + return resolve_applicability(cx, local_init); } } - }, - // hir::ExprKind::Field(expr, ident) => not supported - // hir::ExprKind::Index(expr, expr) => not supported - _ => { - let msg = format!( - "Unexpected assign expression at: {}", - SerializableSpan::from_span(self.cx, expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - } - } - - fn process_borrow_expr(&mut self, borrower_expr: &'hir rustc_hir::Expr<'hir>) { - match &borrower_expr.kind { - hir::ExprKind::Call(func_expr, ..) => { - // We only deal with resolved paths as this is the usual case. Other expression kinds like closures - // etc. are hard to track but might be a worthy improvement in the future - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { - self.value_mutations.push(ApplicabilityModifier::Modifier(path)); - } else { - let msg = format!( - "Unsupported borrow in Call at: {}", - SerializableSpan::from_span(self.cx, func_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - } - }, - hir::ExprKind::MethodCall(..) => { - let msg = format!( - "Unsupported borrow in MethodCall at: {}", - SerializableSpan::from_span(self.cx, borrower_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - _ => { - let msg = format!( - "Unexpected borrow at: {}", - SerializableSpan::from_span(self.cx, borrower_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - } - } - - fn process_consume_expr(&mut self, consume_expr: &'hir rustc_hir::Expr<'hir>) { - // We are only interested in lint emissions. Other types like assignments might be - // interesting for further use or improvement but are to complex for now. - if let hir::ExprKind::Call(func_expr, ..) = &consume_expr.kind {} - } -} - -impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { - fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { - if self.is_value_expr(expr_id) { - // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id) { - log_to_file(&format!("- consume {:?}\n", expr)); } } + _ => {} } - fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { - if_chain! { - if self.is_value_expr(expr_id); - if let BorrowKind::MutBorrow = bk; - if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, expr_id); - if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr); - then { - self.process_borrow_expr(borrower_expr); - } - } - } - fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'hir>, expr_id: hir::HirId) { - if_chain! { - if self.is_value_expr(expr_id); - if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id); - if let hir::ExprKind::Assign(_value_expr, assign_expr, ..) = expr.kind; - then { - self.process_assign_expr(assign_expr); - } - } - } + Some("TODO".to_string()) } -/// The life of a value in Rust is a true adventure. These are the corner stones of such a -/// fairy tale. Let me introduce you to the possible stepping stones a value might have in -/// in our crazy word: -#[derive(Debug)] -#[allow(dead_code)] -enum ApplicabilityModifier<'hir> { - Unknown(String), - /// A simple constant value. - /// - /// This is the actual character of a value. It's baseline. This only defines where the value - /// started. As in real life it can still change and fully decide who it wants to be. - ConstValue(String), - /// A producer is a function that returns an applicability value. - /// - /// This is the heritage of this value. This value comes from a long family tree and is not - /// just a black piece of paper. The evaluation of this stepping stone needs additional - /// context. We therefore only add a reference. This reference will later be used to ask - /// the librarian about the possible initial character that this value might have. - Producer(&'hir hir::Path<'hir>), - /// A modifier that takes the given applicability and might modify it - /// - /// What would an RPG be without it's NPCs. The special thing about modifiers is that they can - /// be actively interested in the story of the value and might make decisions based on the - /// character of this hero. This means that a modifier doesn't just force its way into the life - /// of our hero but it actually asks him how he's been. The possible modification is a result - /// of the situation. - /// - /// Take this part of our heroes life very seriously! - Modifier(&'hir hir::Path<'hir>), - /// The actual emission of a lint - /// - /// All good things must come to an end. Even the life of your awesome applicability hero. He - /// was the bravest soul that has ever wondered this earth. Songs will be written about his - /// heroic deeds. Castles will be named after him and the world how we know it will never be - /// the same! - /// - /// Is this a happy ending? Did he archive what he wanted in his life? Yes, YES, he has lived a - /// life and he will continue to live in all the lint suggestions that can be applied or just - /// displayed by Clippy. He might be no more, but his legacy will serve generations to come. - LintEmit(LintEmission), -} +fn get_parent_local(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { + let map = cx.tcx.hir(); + if let Some(hir::Node::Local(local)) = map.find(map.get_parent_node(hir_id)) { + return Some(local); + } -#[derive(Debug)] -struct LintEmission { - lint: String, - is_multi_line_sugg: bool, + None } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index d04c5f889dd..6e158c8ce72 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -15,6 +15,7 @@ rustc-semver="1.1.0" [features] internal-lints = [] +metadata-collector-lint = [] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 94eade0c932..d4bc42657f4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -821,7 +821,13 @@ pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { /// Gets the parent expression, if any –- this is useful to constrain a lint. pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match get_parent_node(cx.tcx, e.hir_id) { + get_parent_expr_for_hir(cx, e.hir_id) +} + +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for +/// constraint lints +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> { + match get_parent_node(cx.tcx, hir_id) { Some(Node::Expr(parent)) => Some(parent), _ => None, } -- cgit 1.4.1-3-g733a5 From 5830fa7c6064480863d1f90513c0c9b05630a9d3 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 27 Feb 2021 18:39:02 +0100 Subject: Metadata Collection: Collecting direct emission applicabilities (324/455) --- .../src/utils/internal_lints/metadata_collector.rs | 126 +++++++++++++++------ 1 file changed, 93 insertions(+), 33 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a16f7c2ea5e..964ddb36e26 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -14,16 +14,19 @@ //! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) // # Applicability -// - TODO xFrednet 2021-01-17: Find all methods that take and modify applicability or predefine -// them? // - TODO xFrednet 2021-01-17: Find lint emit and collect applicability +// - TODO xFrednet 2021-02-27: Link applicability from function parameters +// - (Examples: suspicious_operation_groupings:267, needless_bool.rs:311) +// - TODO xFrednet 2021-02-27: Tuple if let thingy +// - (Examples: unused_unit.rs:140, misc.rs:694) // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; +use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; +use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; use serde::Serialize; @@ -33,13 +36,15 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth, match_path, + last_path_segment, match_function_call, match_path, match_type, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +/// These groups will be ignored by the lint group matcher +const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all"]; // TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special // handling @@ -52,6 +57,9 @@ const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ &["clippy_utils", "diagnostics", "span_lint_and_sugg"], ]; +/// The index of the applicability name of `paths::APPLICABILITY_VALUES` +const APPLICABILITY_NAME_INDEX: usize = 2; + declare_clippy_lint! { /// **What it does:** Collects metadata about clippy lints for the website. /// @@ -297,6 +305,10 @@ fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Ite fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { + if BLACK_LISTED_LINT_GROUP.contains(group_name) { + continue; + } + if lints.iter().any(|x| *x == lint_id) { return Some((*group_name).to_string()); } @@ -341,7 +353,10 @@ fn match_simple_lint_emission<'hir>( } /// This returns the lint name and the possible applicability of this emission -fn extract_emission_info<'hir>(cx: &LateContext<'hir>, args: &[hir::Expr<'_>]) -> Option<(String, Option)> { +fn extract_emission_info<'hir>( + cx: &LateContext<'hir>, + args: &'hir [hir::Expr<'hir>], +) -> Option<(String, Option)> { let mut lint_name = None; let mut applicability = None; @@ -358,41 +373,38 @@ fn extract_emission_info<'hir>(cx: &LateContext<'hir>, args: &[hir::Expr<'_>]) - } } - lint_name.map(|lint_name| { - ( - sym_to_string(lint_name).to_ascii_lowercase(), - applicability, - ) - }) + lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability)) } -fn resolve_applicability(cx: &LateContext<'hir>, expr: &hir::Expr<'_>) -> Option { +/// This function tries to resolve the linked applicability to the given expression. +fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { match expr.kind { // We can ignore ifs without an else block because those can't be used as an assignment - hir::ExprKind::If(_con, _if_block, Some(_else_block)) => { - // self.process_assign_expr(if_block); - // self.process_assign_expr(else_block); - return Some("TODO IF EXPR".to_string()); + hir::ExprKind::If(_con, if_block, Some(else_block)) => { + let mut visitor = ApplicabilityVisitor::new(cx); + intravisit::walk_expr(&mut visitor, if_block); + intravisit::walk_expr(&mut visitor, else_block); + visitor.complete() }, - hir::ExprKind::Match(_expr, _arms, _) => { - // for arm in *arms { - // self.process_assign_expr(arm.body); - // } - return Some("TODO MATCH EXPR".to_string()); + hir::ExprKind::Match(_expr, arms, _) => { + let mut visitor = ApplicabilityVisitor::new(cx); + arms.iter() + .for_each(|arm| intravisit::walk_expr(&mut visitor, arm.body)); + visitor.complete() }, hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { - if let Some(block_expr) = block.expr { - return resolve_applicability(cx, block_expr); - } + let mut visitor = ApplicabilityVisitor::new(cx); + intravisit::walk_block(&mut visitor, block); + visitor.complete() }, ExprKind::Path(hir::QPath::Resolved(_, path)) => { // direct applicabilities are simple: for enum_value in &paths::APPLICABILITY_VALUES { if match_path(path, enum_value) { - return Some(enum_value[2].to_string()); + return Some(enum_value[APPLICABILITY_NAME_INDEX].to_string()); } } - + // Values yay if let hir::def::Res::Local(local_hir) = path.res { if let Some(local) = get_parent_local(cx, local_hir) { @@ -401,19 +413,67 @@ fn resolve_applicability(cx: &LateContext<'hir>, expr: &hir::Expr<'_>) -> Option } } } + + // This is true for paths that are linked to function parameters. They might be a bit more work so + // not today :) + None + }, + _ => None, + } +} + +fn get_parent_local(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { + let map = cx.tcx.hir(); + + match map.find(map.get_parent_node(hir_id)) { + Some(hir::Node::Local(local)) => Some(local), + Some(hir::Node::Pat(pattern)) => get_parent_local(cx, pattern.hir_id), + _ => None, + } +} + +/// This visitor finds the highest applicability value in the visited expressions +struct ApplicabilityVisitor<'a, 'hir> { + cx: &'a LateContext<'hir>, + /// This is the index of hightest `Applicability` for + /// `clippy_utils::paths::APPLICABILITY_VALUES` + applicability_index: Option, +} + +impl<'a, 'hir> ApplicabilityVisitor<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>) -> Self { + Self { + cx, + applicability_index: None, } - _ => {} } + fn add_new_index(&mut self, new_index: usize) { + self.applicability_index = self + .applicability_index + .map_or(new_index, |old_index| old_index.min(new_index)) + .into(); + } - Some("TODO".to_string()) + fn complete(self) -> Option { + self.applicability_index + .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) + } } -fn get_parent_local(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { - let map = cx.tcx.hir(); - if let Some(hir::Node::Local(local)) = map.find(map.get_parent_node(hir_id)) { - return Some(local); +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityVisitor<'a, 'hir> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) } - None + fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { + for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { + if match_path(path, enum_value) { + self.add_new_index(index); + break; + } + } + } } -- cgit 1.4.1-3-g733a5 From 6658db1044bbd19e70bfbf1b74b39bd16d3c4a2c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 28 Feb 2021 16:11:45 +0100 Subject: Metadata collection: processing emission closures (417/455) --- clippy_lints/src/slow_vector_initialization.rs | 20 +-- .../src/utils/internal_lints/metadata_collector.rs | 197 +++++++++++++++++++-- clippy_utils/src/paths.rs | 2 + 3 files changed, 190 insertions(+), 29 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 191781be000..5a51e1141fb 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -157,26 +157,16 @@ impl SlowVectorInit { vec_alloc: &VecAllocation<'_>, ) { match initialization { - InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint( - cx, - e, - vec_alloc, - "slow zero-filling initialization", - SLOW_VECTOR_INITIALIZATION, - ), + InitializationType::Extend(e) | InitializationType::Resize(e) => { + Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization") + }, }; } - fn emit_lint<'tcx>( - cx: &LateContext<'tcx>, - slow_fill: &Expr<'_>, - vec_alloc: &VecAllocation<'_>, - msg: &str, - lint: &'static Lint, - ) { + fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); - span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| { + span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diag.span_suggestion( vec_alloc.allocation_expr.span, "consider replace allocation with", diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 964ddb36e26..20bd2a669aa 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -15,16 +15,25 @@ // # Applicability // - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-27: Link applicability from function parameters -// - (Examples: suspicious_operation_groupings:267, needless_bool.rs:311) -// - TODO xFrednet 2021-02-27: Tuple if let thingy -// - (Examples: unused_unit.rs:140, misc.rs:694) +// - TODO xFrednet 2021-02-28: 1x reference to closure +// - See clippy_lints/src/needless_pass_by_value.rs@NeedlessPassByValue::check_fn +// - TODO xFrednet 2021-02-28: 4x weird emission forwarding +// - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name +// - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from +// function. +// - See clippy_lints/src/methods/mod.rs@lint_binary_expr_with_method_call +// - TODO xFrednet 2021-02-28: 2x lint from local from function call +// - See clippy_lints/src/misc.rs@check_binary +// - TODO xFrednet 2021-02-28: 2x lint from local from method call +// - See clippy_lints/src/non_copy_const.rs@lint +// - TODO xFrednet 2021-02-28: 20x lint from local +// - See clippy_lints/src/map_unit_fn.rs@lint_map_unit_fn // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability}; +use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability, QPath}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -44,18 +53,36 @@ const OUTPUT_FILE: &str = "metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; /// These groups will be ignored by the lint group matcher -const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all"]; +const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all", "clippy::internal"]; // TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special // handling -#[rustfmt::skip] -const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ +const SIMPLE_LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], &["clippy_utils", "diagnostics", "span_lint_and_note"], &["clippy_utils", "diagnostics", "span_lint_hir"], &["clippy_utils", "diagnostics", "span_lint_and_sugg"], ]; +const COMPLEX_LINT_EMISSION_FUNCTIONS: [&[&str]; 2] = [ + &["clippy_utils", "diagnostics", "span_lint_and_then"], + &["clippy_utils", "diagnostics", "span_lint_hir_and_then"], +]; +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [ + ("span_suggestion", false), + ("span_suggestion_short", false), + ("span_suggestion_verbose", false), + ("span_suggestion_hidden", false), + ("tool_only_span_suggestion", false), + ("multipart_suggestion", true), + ("multipart_suggestions", true), + ("tool_only_multipart_suggestion", true), + ("span_suggestions", true), +]; +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ + &["clippy_utils", "diagnostics", "mutispan_sugg"], + &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], +]; /// The index of the applicability name of `paths::APPLICABILITY_VALUES` const APPLICABILITY_NAME_INDEX: usize = 2; @@ -177,7 +204,7 @@ struct ApplicabilityInfo { /// Indicates if any of the lint emissions uses multiple spans. This is related to /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can /// currently not be applied automatically. - has_multi_suggestion: bool, + is_multi_suggestion: bool, applicability: Option, } @@ -250,6 +277,14 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { } else { lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); } + } else if let Some(args) = match_complex_lint_emission(cx, expr) { + if let Some((lint_name, applicability, is_multi_span)) = extract_complex_emission_info(cx, args) { + let app_info = self.applicability_into.entry(lint_name).or_default(); + app_info.applicability = applicability; + app_info.is_multi_suggestion = is_multi_span; + } else { + lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + } } } } @@ -347,7 +382,16 @@ fn match_simple_lint_emission<'hir>( cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>, ) -> Option<&'hir [hir::Expr<'hir>]> { - LINT_EMISSION_FUNCTIONS + SIMPLE_LINT_EMISSION_FUNCTIONS + .iter() + .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) +} + +fn match_complex_lint_emission<'hir>( + cx: &LateContext<'hir>, + expr: &'hir hir::Expr<'_>, +) -> Option<&'hir [hir::Expr<'hir>]> { + COMPLEX_LINT_EMISSION_FUNCTIONS .iter() .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } @@ -376,28 +420,60 @@ fn extract_emission_info<'hir>( lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability)) } +fn extract_complex_emission_info<'hir>( + cx: &LateContext<'hir>, + args: &'hir [hir::Expr<'hir>], +) -> Option<(String, Option, bool)> { + let mut lint_name = None; + let mut applicability = None; + let mut multi_span = false; + + for arg in args { + let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg)); + + if match_type(cx, arg_ty, &paths::LINT) { + // If we found the lint arg, extract the lint name + if let ExprKind::Path(ref lint_path) = arg.kind { + lint_name = Some(last_path_segment(lint_path).ident.name); + } + } else if arg_ty.is_closure() { + if let ExprKind::Closure(_, _, body_id, _, _) = arg.kind { + let mut visitor = EmissionClosureVisitor::new(cx); + intravisit::walk_body(&mut visitor, cx.tcx.hir().body(body_id)); + multi_span = visitor.found_multi_span(); + applicability = visitor.complete(); + } else { + // TODO xfrednet 2021-02-28: linked closures, see: needless_pass_by_value.rs:292 + return None; + } + } + } + + lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_span)) +} + /// This function tries to resolve the linked applicability to the given expression. fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { match expr.kind { // We can ignore ifs without an else block because those can't be used as an assignment - hir::ExprKind::If(_con, if_block, Some(else_block)) => { + ExprKind::If(_con, if_block, Some(else_block)) => { let mut visitor = ApplicabilityVisitor::new(cx); intravisit::walk_expr(&mut visitor, if_block); intravisit::walk_expr(&mut visitor, else_block); visitor.complete() }, - hir::ExprKind::Match(_expr, arms, _) => { + ExprKind::Match(_expr, arms, _) => { let mut visitor = ApplicabilityVisitor::new(cx); arms.iter() .for_each(|arm| intravisit::walk_expr(&mut visitor, arm.body)); visitor.complete() }, - hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + ExprKind::Loop(block, ..) | ExprKind::Block(block, ..) => { let mut visitor = ApplicabilityVisitor::new(cx); intravisit::walk_block(&mut visitor, block); visitor.complete() }, - ExprKind::Path(hir::QPath::Resolved(_, path)) => { + ExprKind::Path(QPath::Resolved(_, path)) => { // direct applicabilities are simple: for enum_value in &paths::APPLICABILITY_VALUES { if match_path(path, enum_value) { @@ -477,3 +553,96 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityVisitor<'a, 'hir> { } } } + +/// This visitor finds the highest applicability value in the visited expressions +struct EmissionClosureVisitor<'a, 'hir> { + cx: &'a LateContext<'hir>, + /// This is the index of hightest `Applicability` for + /// `clippy_utils::paths::APPLICABILITY_VALUES` + applicability_index: Option, + suggestion_count: usize, +} + +impl<'a, 'hir> EmissionClosureVisitor<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>) -> Self { + Self { + cx, + applicability_index: None, + suggestion_count: 0, + } + } + + fn add_new_index(&mut self, new_index: usize) { + self.applicability_index = self + .applicability_index + .map_or(new_index, |old_index| old_index.min(new_index)) + .into(); + } + + fn found_multi_span(&self) -> bool { + self.suggestion_count > 1 + } + + fn complete(self) -> Option { + self.applicability_index + .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) + } +} + +impl<'a, 'hir> intravisit::Visitor<'hir> for EmissionClosureVisitor<'a, 'hir> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { + for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { + if match_path(path, enum_value) { + self.add_new_index(index); + break; + } + } + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + match &expr.kind { + ExprKind::Call(fn_expr, _args) => { + let found_function = SUGGESTION_FUNCTIONS + .iter() + .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some()); + if found_function { + // These functions are all multi part suggestions + self.suggestion_count += 2; + } + }, + ExprKind::MethodCall(path, _path_span, arg, _arg_span) => { + let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0])); + if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) { + let called_method = path.ident.name.as_str().to_string(); + let found_suggestion = + SUGGESTION_DIAGNOSTIC_BUILDER_METHODS + .iter() + .find_map(|(method_name, is_multi_part)| { + if *method_name == called_method { + Some(*is_multi_part) + } else { + None + } + }); + if let Some(multi_part) = found_suggestion { + if multi_part { + // two is enough to have it marked as a multipart suggestion + self.suggestion_count += 2; + } else { + self.suggestion_count += 1; + } + } + } + }, + _ => {}, + } + + intravisit::walk_expr(self, expr); + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 46f58b788e6..a6292b87768 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -14,6 +14,8 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "Unspecified"], ]; +#[cfg(feature = "metadata-collector-lint")] +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; -- cgit 1.4.1-3-g733a5 From 2ce5e368d84887a5a2cee669b467160d4ad7b933 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 1 Mar 2021 12:25:14 +0100 Subject: Metadata collection: Refining the implementation --- .gitignore | 2 +- clippy_lints/src/slow_vector_initialization.rs | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 280 +- metadata_collection.json | 5698 ++++++++++++++++++++ 4 files changed, 5809 insertions(+), 173 deletions(-) create mode 100644 metadata_collection.json (limited to 'clippy_lints/src') diff --git a/.gitignore b/.gitignore index 523bab18828..565327a7f83 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ out # gh pages docs util/gh-pages/lints.json -**/metadata_collection.json +# **/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 5a51e1141fb..a9ae2b77119 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -7,7 +7,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 20bd2a669aa..b2cc6372277 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -33,7 +33,7 @@ use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability, QPath}; +use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -52,19 +52,18 @@ use crate::utils::{ const OUTPUT_FILE: &str = "metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; -/// These groups will be ignored by the lint group matcher -const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all", "clippy::internal"]; +/// These groups will be ignored by the lint group matcher. This is useful for collections like +/// `clippy::all` +const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; +/// Lints within this group will be excluded from the collection +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"]; -// TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special -// handling -const SIMPLE_LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ +const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], &["clippy_utils", "diagnostics", "span_lint_and_note"], &["clippy_utils", "diagnostics", "span_lint_hir"], &["clippy_utils", "diagnostics", "span_lint_and_sugg"], -]; -const COMPLEX_LINT_EMISSION_FUNCTIONS: [&[&str]; 2] = [ &["clippy_utils", "diagnostics", "span_lint_and_then"], &["clippy_utils", "diagnostics", "span_lint_hir_and_then"], ]; @@ -270,18 +269,11 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ); /// ``` fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { - if let Some(args) = match_simple_lint_emission(cx, expr) { - if let Some((lint_name, applicability)) = extract_emission_info(cx, args) { + if let Some(args) = match_lint_emission(cx, expr) { + if let Some((lint_name, applicability, is_multi_part)) = extract_complex_emission_info(cx, args) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; - } else { - lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); - } - } else if let Some(args) = match_complex_lint_emission(cx, expr) { - if let Some((lint_name, applicability, is_multi_span)) = extract_complex_emission_info(cx, args) { - let app_info = self.applicability_into.entry(lint_name).or_default(); - app_info.applicability = applicability; - app_info.is_multi_suggestion = is_multi_span; + app_info.is_multi_suggestion = is_multi_part; } else { lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); } @@ -292,7 +284,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { // ================================================================== // Lint definition extraction // ================================================================== - fn sym_to_string(sym: Symbol) -> String { sym.as_str().to_string() } @@ -328,10 +319,12 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option { let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - get_lint_group(cx, lint_lst[0]).or_else(|| { - lint_collection_error_item(cx, item, "Unable to determine lint group"); - None - }) + get_lint_group(cx, lint_lst[0]) + .or_else(|| { + lint_collection_error_item(cx, item, "Unable to determine lint group"); + None + }) + .filter(|group| !EXCLUDED_LINT_GROUPS.contains(&group.as_str())) } else { lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); None @@ -340,7 +333,7 @@ fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Ite fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { - if BLACK_LISTED_LINT_GROUP.contains(group_name) { + if IGNORED_LINT_GROUPS.contains(group_name) { continue; } @@ -378,55 +371,19 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { // ================================================================== /// This function checks if a given expression is equal to a simple lint emission function call. /// It will return the function arguments if the emission matched any function. -fn match_simple_lint_emission<'hir>( - cx: &LateContext<'hir>, - expr: &'hir hir::Expr<'_>, -) -> Option<&'hir [hir::Expr<'hir>]> { - SIMPLE_LINT_EMISSION_FUNCTIONS - .iter() - .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) -} - -fn match_complex_lint_emission<'hir>( - cx: &LateContext<'hir>, - expr: &'hir hir::Expr<'_>, -) -> Option<&'hir [hir::Expr<'hir>]> { - COMPLEX_LINT_EMISSION_FUNCTIONS +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> { + LINT_EMISSION_FUNCTIONS .iter() .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } -/// This returns the lint name and the possible applicability of this emission -fn extract_emission_info<'hir>( - cx: &LateContext<'hir>, - args: &'hir [hir::Expr<'hir>], -) -> Option<(String, Option)> { - let mut lint_name = None; - let mut applicability = None; - - for arg in args { - let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg)); - - if match_type(cx, arg_ty, &paths::LINT) { - // If we found the lint arg, extract the lint name - if let ExprKind::Path(ref lint_path) = arg.kind { - lint_name = Some(last_path_segment(lint_path).ident.name); - } - } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { - applicability = resolve_applicability(cx, arg); - } - } - - lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability)) -} - fn extract_complex_emission_info<'hir>( cx: &LateContext<'hir>, args: &'hir [hir::Expr<'hir>], ) -> Option<(String, Option, bool)> { let mut lint_name = None; let mut applicability = None; - let mut multi_span = false; + let mut multi_part = false; for arg in args { let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg)); @@ -436,87 +393,47 @@ fn extract_complex_emission_info<'hir>( if let ExprKind::Path(ref lint_path) = arg.kind { lint_name = Some(last_path_segment(lint_path).ident.name); } + } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { + applicability = resolve_applicability(cx, arg); } else if arg_ty.is_closure() { - if let ExprKind::Closure(_, _, body_id, _, _) = arg.kind { - let mut visitor = EmissionClosureVisitor::new(cx); - intravisit::walk_body(&mut visitor, cx.tcx.hir().body(body_id)); - multi_span = visitor.found_multi_span(); - applicability = visitor.complete(); - } else { - // TODO xfrednet 2021-02-28: linked closures, see: needless_pass_by_value.rs:292 - return None; - } + multi_part |= check_is_multi_part(cx, arg); + // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison + applicability = applicability.or_else(|| resolve_applicability(cx, arg)); } } - lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_span)) + lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_part)) } /// This function tries to resolve the linked applicability to the given expression. fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { - match expr.kind { - // We can ignore ifs without an else block because those can't be used as an assignment - ExprKind::If(_con, if_block, Some(else_block)) => { - let mut visitor = ApplicabilityVisitor::new(cx); - intravisit::walk_expr(&mut visitor, if_block); - intravisit::walk_expr(&mut visitor, else_block); - visitor.complete() - }, - ExprKind::Match(_expr, arms, _) => { - let mut visitor = ApplicabilityVisitor::new(cx); - arms.iter() - .for_each(|arm| intravisit::walk_expr(&mut visitor, arm.body)); - visitor.complete() - }, - ExprKind::Loop(block, ..) | ExprKind::Block(block, ..) => { - let mut visitor = ApplicabilityVisitor::new(cx); - intravisit::walk_block(&mut visitor, block); - visitor.complete() - }, - ExprKind::Path(QPath::Resolved(_, path)) => { - // direct applicabilities are simple: - for enum_value in &paths::APPLICABILITY_VALUES { - if match_path(path, enum_value) { - return Some(enum_value[APPLICABILITY_NAME_INDEX].to_string()); - } - } - - // Values yay - if let hir::def::Res::Local(local_hir) = path.res { - if let Some(local) = get_parent_local(cx, local_hir) { - if let Some(local_init) = local.init { - return resolve_applicability(cx, local_init); - } - } - } - - // This is true for paths that are linked to function parameters. They might be a bit more work so - // not today :) - None - }, - _ => None, - } + let mut resolver = ApplicabilityResolver::new(cx); + resolver.visit_expr(expr); + resolver.complete() } -fn get_parent_local(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { - let map = cx.tcx.hir(); - - match map.find(map.get_parent_node(hir_id)) { - Some(hir::Node::Local(local)) => Some(local), - Some(hir::Node::Pat(pattern)) => get_parent_local(cx, pattern.hir_id), - _ => None, +fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { + if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind { + let mut scanner = IsMultiSpanScanner::new(cx); + intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id)); + return scanner.is_multi_part(); + } else if let Some(local) = get_parent_local(cx, closure_expr) { + if let Some(local_init) = local.init { + return check_is_multi_part(cx, local_init); + } } + + false } /// This visitor finds the highest applicability value in the visited expressions -struct ApplicabilityVisitor<'a, 'hir> { +struct ApplicabilityResolver<'a, 'hir> { cx: &'a LateContext<'hir>, - /// This is the index of hightest `Applicability` for - /// `clippy_utils::paths::APPLICABILITY_VALUES` + /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES` applicability_index: Option, } -impl<'a, 'hir> ApplicabilityVisitor<'a, 'hir> { +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { fn new(cx: &'a LateContext<'hir>) -> Self { Self { cx, @@ -537,75 +454,104 @@ impl<'a, 'hir> ApplicabilityVisitor<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityVisitor<'a, 'hir> { +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) } - fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { + fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) { for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { if match_path(path, enum_value) { self.add_new_index(index); - break; + return; + } + } + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr)); + + if_chain! { + if match_type(self.cx, expr_ty, &paths::APPLICABILITY); + if let Some(local) = get_parent_local(self.cx, expr); + if let Some(local_init) = local.init; + then { + intravisit::walk_expr(self, local_init); } + }; + + // TODO xFrednet 2021-03-01: support function arguments? + + intravisit::walk_expr(self, expr); + } +} + +/// This returns the parent local node if the expression is a reference to +fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { + if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { + if let hir::def::Res::Local(local_hir) = path.res { + return get_parent_local_hir_id(cx, local_hir); } } + + None +} + +fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { + let map = cx.tcx.hir(); + + match map.find(map.get_parent_node(hir_id)) { + Some(hir::Node::Local(local)) => Some(local), + Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), + _ => None, + } } /// This visitor finds the highest applicability value in the visited expressions -struct EmissionClosureVisitor<'a, 'hir> { +struct IsMultiSpanScanner<'a, 'hir> { cx: &'a LateContext<'hir>, - /// This is the index of hightest `Applicability` for - /// `clippy_utils::paths::APPLICABILITY_VALUES` - applicability_index: Option, suggestion_count: usize, } -impl<'a, 'hir> EmissionClosureVisitor<'a, 'hir> { +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { fn new(cx: &'a LateContext<'hir>) -> Self { Self { cx, - applicability_index: None, suggestion_count: 0, } } - fn add_new_index(&mut self, new_index: usize) { - self.applicability_index = self - .applicability_index - .map_or(new_index, |old_index| old_index.min(new_index)) - .into(); + /// Add a new single expression suggestion to the counter + fn add_singe_span_suggestion(&mut self) { + self.suggestion_count += 1; } - fn found_multi_span(&self) -> bool { - self.suggestion_count > 1 + /// Signals that a suggestion with possible multiple spans was found + fn add_multi_part_suggestion(&mut self) { + self.suggestion_count += 2; } - fn complete(self) -> Option { - self.applicability_index - .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) + /// Checks if the suggestions include multiple spanns + fn is_multi_part(&self) -> bool { + self.suggestion_count > 1 } } -impl<'a, 'hir> intravisit::Visitor<'hir> for EmissionClosureVisitor<'a, 'hir> { +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) } - fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { - for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { - if match_path(path, enum_value) { - self.add_new_index(index); - break; - } + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + // Early return if the lint is already multi span + if self.is_multi_part() { + return; } - } - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { match &expr.kind { ExprKind::Call(fn_expr, _args) => { let found_function = SUGGESTION_FUNCTIONS @@ -613,29 +559,21 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for EmissionClosureVisitor<'a, 'hir> { .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some()); if found_function { // These functions are all multi part suggestions - self.suggestion_count += 2; + self.add_singe_span_suggestion() } }, ExprKind::MethodCall(path, _path_span, arg, _arg_span) => { let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0])); if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) { let called_method = path.ident.name.as_str().to_string(); - let found_suggestion = - SUGGESTION_DIAGNOSTIC_BUILDER_METHODS - .iter() - .find_map(|(method_name, is_multi_part)| { - if *method_name == called_method { - Some(*is_multi_part) - } else { - None - } - }); - if let Some(multi_part) = found_suggestion { - if multi_part { - // two is enough to have it marked as a multipart suggestion - self.suggestion_count += 2; - } else { - self.suggestion_count += 1; + for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS { + if *method_name == called_method { + if *is_multi_part { + self.add_multi_part_suggestion(); + } else { + self.add_singe_span_suggestion(); + } + break; } } } diff --git a/metadata_collection.json b/metadata_collection.json new file mode 100644 index 00000000000..507752a782c --- /dev/null +++ b/metadata_collection.json @@ -0,0 +1,5698 @@ +[ + { + "id": "approx_constant", + "id_span": { + "path": "clippy_lints/src/approx_const.rs", + "line": 34 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for floating point literals that approximate constants which are defined in\n [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\n or\n [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\n respectively, suggesting to use the predefined constant.\n\n **Why is this bad?** Usually, the definition in the standard library is more\n precise than what people come up with. If you find that your definition is\n actually more precise, please [file a Rust\n issue](https://github.com/rust-lang/rust/issues).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 3.14;\n let y = 1_f64 / x;\n ```\n Use predefined constants instead:\n ```rust\n let x = std::f32::consts::PI;\n let y = std::f64::consts::FRAC_1_PI;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "integer_arithmetic", + "id_span": { + "path": "clippy_lints/src/arithmetic.rs", + "line": 28 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for integer arithmetic operations which could overflow or panic.\n Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\n of overflowing according to the [Rust\n Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\n or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\n attempted.\n\n **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in\n release mode. Division by zero will cause a panic in either mode. In some applications one\n wants explicitly checked, wrapping or saturating arithmetic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0;\n a + 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "float_arithmetic", + "id_span": { + "path": "clippy_lints/src/arithmetic.rs", + "line": 46 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for float arithmetic.\n **Why is this bad?** For some embedded systems or kernel development, it\n can be useful to rule out floating-point numbers.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0.0;\n a + 1.0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "as_conversions", + "id_span": { + "path": "clippy_lints/src/as_conversions.rs", + "line": 42 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `as` conversions.\n Note that this lint is specialized in linting *every single* use of `as`\n regardless of whether good alternatives exist or not.\n If you want more precise lints for `as`, please consider using these separate lints:\n `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\n There is a good explanation the reason why this lint should work in this way and how it is useful\n [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).\n\n **Why is this bad?** `as` conversions will perform many kinds of\n conversions, including silently lossy conversions and dangerous coercions.\n There are cases when it makes sense to use `as`, so the lint is\n Allow by default.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a: u32;\n ...\n f(a as u16);\n ```\n\n Usually better represents the semantics you expect:\n ```rust,ignore\n f(a.try_into()?);\n ```\n or\n ```rust,ignore\n f(a.try_into().expect(\"Unexpected u16 overflow in f\"));\n ```\n\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inline_asm_x86_intel_syntax", + "id_span": { + "path": "clippy_lints/src/asm_syntax.rs", + "line": 78 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of Intel x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for AT&T x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n", + "applicability": null + }, + { + "id": "inline_asm_x86_att_syntax", + "id_span": { + "path": "clippy_lints/src/asm_syntax.rs", + "line": 114 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of AT&T x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for Intel x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n", + "applicability": null + }, + { + "id": "assertions_on_constants", + "id_span": { + "path": "clippy_lints/src/assertions_on_constants.rs", + "line": 23 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.\n **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a\n `panic!()` or `unreachable!()`\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n assert!(false)\n assert!(true)\n const B: bool = false;\n assert!(B)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "assign_op_pattern", + "id_span": { + "path": "clippy_lints/src/assign_ops.rs", + "line": 33 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `a = a op b` or `a = b commutative_op a` patterns.\n\n **Why is this bad?** These can be written as the shorter `a op= b`.\n\n **Known problems:** While forbidden by the spec, `OpAssign` traits may have\n implementations that differ from the regular `Op` impl.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 0;\n // ...\n // Bad\n a = a + b;\n\n // Good\n a += b;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "misrefactored_assign_op", + "id_span": { + "path": "clippy_lints/src/assign_ops.rs", + "line": 56 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.\n **Why is this bad?** Most likely these are bugs where one meant to write `a\n op= b`.\n\n **Known problems:** Clippy cannot know for sure if `a op= a op b` should have\n been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.\n If `a op= a op b` is really the correct behaviour it should be\n written as `a = a op a op b` as it's less confusing.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 2;\n // ...\n a += a + b;\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "async_yields_async", + "id_span": { + "path": "clippy_lints/src/async_yields_async.rs", + "line": 36 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for async blocks that yield values of types that can themselves be awaited.\n\n **Why is this bad?** An await is likely missing.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo()\n };\n }\n ```\n Use instead:\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo().await\n };\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "invalid_atomic_ordering", + "id_span": { + "path": "clippy_lints/src/atomic_ordering.rs", + "line": 47 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for usage of invalid atomic ordering in atomic loads/stores/exchanges/updates and\n memory fences.\n\n **Why is this bad?** Using an invalid atomic ordering\n will cause a panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::sync::atomic::{self, AtomicU8, Ordering};\n\n let x = AtomicU8::new(0);\n\n // Bad: `Release` and `AcqRel` cannot be used for `load`.\n let _ = x.load(Ordering::Release);\n let _ = x.load(Ordering::AcqRel);\n\n // Bad: `Acquire` and `AcqRel` cannot be used for `store`.\n x.store(1, Ordering::Acquire);\n x.store(2, Ordering::AcqRel);\n\n // Bad: `Relaxed` cannot be used as a fence's ordering.\n atomic::fence(Ordering::Relaxed);\n atomic::compiler_fence(Ordering::Relaxed);\n\n // Bad: `Release` and `AcqRel` are both always invalid\n // for the failure ordering (the last arg).\n let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);\n let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);\n\n // Bad: The failure ordering is not allowed to be\n // stronger than the success order, and `SeqCst` is\n // stronger than `Relaxed`.\n let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inline_always", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 65 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.\n\n **Why is this bad?** While there are valid uses of this annotation (and once\n you know when to use it, by all means `allow` this lint), it's a common\n newbie-mistake to pepper one's code with it.\n\n As a rule of thumb, before slapping `#[inline(always)]` on a function,\n measure if that additional function call really affects your runtime profile\n sufficiently to make up for the increase in compile time.\n\n **Known problems:** False positives, big time. This lint is meant to be\n deactivated by everyone doing serious performance work. This means having\n done the measurement.\n\n **Example:**\n ```ignore\n #[inline(always)]\n fn not_quite_hot_code(..) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "useless_attribute", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 99 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `extern crate` and `use` items annotated with lint attributes.\n\n This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n `extern crate` items with a `#[macro_use]` attribute.\n\n **Why is this bad?** Lint attributes have no effect on crate imports. Most\n likely a `!` was forgotten.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n #[deny(dead_code)]\n extern crate foo;\n #[forbid(dead_code)]\n use foo::bar;\n\n // Ok\n #[allow(unused_imports)]\n use foo::baz;\n #[allow(unused_imports)]\n #[macro_use]\n extern crate baz;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "deprecated_semver", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 118 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `#[deprecated]` annotations with a `since` field that is not a valid semantic version.\n\n **Why is this bad?** For checking the version of the deprecation, it must be\n a valid semver. Failing that, the contained information is useless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n #[deprecated(since = \"forever\")]\n fn something_else() { /* ... */ }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "empty_line_after_outer_attr", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 153 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for empty lines after outer attributes\n **Why is this bad?**\n Most likely the attribute was meant to be an inner attribute using a '!'.\n If it was meant to be an outer attribute, then the following item\n should not be separated by empty lines.\n\n **Known problems:** Can cause false positives.\n\n From the clippy side it's difficult to detect empty lines between an attributes and the\n following item because empty lines and comments are not part of the AST. The parsing\n currently works for basic cases but is not perfect.\n\n **Example:**\n ```rust\n // Good (as inner attribute)\n #![allow(dead_code)]\n\n fn this_is_fine() { }\n\n // Bad\n #[allow(dead_code)]\n\n fn not_quite_good_code() { }\n\n // Good (as outer attribute)\n #[allow(dead_code)]\n fn this_is_fine_too() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "blanket_clippy_restriction_lints", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 176 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.\n **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.\n These lints should only be enabled on a lint-by-lint basis and with careful consideration.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n #![deny(clippy::restriction)]\n ```\n\n Good:\n ```rust\n #![deny(clippy::as_conversions)]\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "deprecated_cfg_attr", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 205 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it with `#[rustfmt::skip]`.\n\n **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))\n are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.\n\n **Known problems:** This lint doesn't detect crate level inner attributes, because they get\n processed before the PreExpansionPass lints get executed. See\n [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg_attr(rustfmt, rustfmt_skip)]\n fn main() { }\n ```\n\n Good:\n ```rust\n #[rustfmt::skip]\n fn main() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mismatched_target_os", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 238 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for cfg attributes having operating systems used in target family position.\n **Why is this bad?** The configuration option will not be recognised and the related item will not be included\n by the conditional compilation engine.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg(linux)]\n fn conditional() { }\n ```\n\n Good:\n ```rust\n #[cfg(target_os = \"linux\")]\n fn conditional() { }\n ```\n\n Or:\n ```rust\n #[cfg(unix)]\n fn conditional() { }\n ```\n Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "await_holding_lock", + "id_span": { + "path": "clippy_lints/src/await_holding_invalid.rs", + "line": 47 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for calls to await while holding a non-async-aware MutexGuard.\n\n **Why is this bad?** The Mutex types found in std::sync and parking_lot\n are not designed to operate in an async context across await points.\n\n There are two potential solutions. One is to use an asynx-aware Mutex\n type. Many asynchronous foundation crates provide such a Mutex type. The\n other solution is to ensure the mutex is unlocked before calling await,\n either by introducing a scope or an explicit call to Drop::drop.\n\n **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).\n\n **Example:**\n\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n let guard = x.lock().unwrap();\n *guard += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n {\n let guard = x.lock().unwrap();\n *guard += 1;\n }\n bar.await;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "await_holding_refcell_ref", + "id_span": { + "path": "clippy_lints/src/await_holding_invalid.rs", + "line": 86 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.\n\n **Why is this bad?** `RefCell` refs only check for exclusive mutable access\n at runtime. Holding onto a `RefCell` ref across an `await` suspension point\n risks panics from a mutable ref shared while other refs are outstanding.\n\n **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).\n\n **Example:**\n\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n let mut y = x.borrow_mut();\n *y += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n {\n let mut y = x.borrow_mut();\n *y += 1;\n }\n bar.await;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "bad_bit_mask", + "id_span": { + "path": "clippy_lints/src/bit_mask.rs", + "line": 44 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for incompatible bit masks in comparisons.\n The formula for detecting if an expression of the type `_ m\n c` (where `` is one of {`&`, `|`} and `` is one of\n {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\n table:\n\n |Comparison |Bit Op|Example |is always|Formula |\n |------------|------|------------|---------|----------------------|\n |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |\n\n **Why is this bad?** If the bits that the comparison cares about are always\n set to zero or one by the bit mask, the comparison is constant `true` or\n `false` (depending on mask, compared value, and operators).\n\n So the code is actively misleading, and the only reason someone would write\n this intentionally is to win an underhanded Rust contest or create a\n test-case for this lint.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n if (x & 1 == 2) { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ineffective_bit_mask", + "id_span": { + "path": "clippy_lints/src/bit_mask.rs", + "line": 73 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for bit masks in comparisons which can be removed without changing the outcome. The basic structure can be seen in the\n following table:\n\n |Comparison| Bit Op |Example |equals |\n |----------|---------|-----------|-------|\n |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|\n\n **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),\n but still a bit misleading, because the bit mask is ineffective.\n\n **Known problems:** False negatives: This lint will only match instances\n where we have figured out the math (which is for a power-of-two compared\n value). This means things like `x | 1 >= 7` (which would be better written\n as `x >= 6`) will not be reported (but bit masks like this are fairly\n uncommon).\n\n **Example:**\n ```rust\n # let x = 1;\n if (x | 1 > 3) { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "verbose_bit_mask", + "id_span": { + "path": "clippy_lints/src/bit_mask.rs", + "line": 92 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for bit masks that can be replaced by a call to `trailing_zeros`\n\n **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15\n == 0`\n\n **Known problems:** llvm generates better code for `x & 15 == 0` on x86\n\n **Example:**\n ```rust\n # let x = 1;\n if x & 0b1111 == 0 { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "blacklisted_name", + "id_span": { + "path": "clippy_lints/src/blacklisted_name.rs", + "line": 20 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of blacklisted names for variables, such as `foo`.\n\n **Why is this bad?** These names are usually placeholder names and should be\n avoided.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo = 3.14;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "blocks_in_if_conditions", + "id_span": { + "path": "clippy_lints/src/blocks_in_if_conditions.rs", + "line": 42 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `if` conditions that use blocks containing an expression, statements or conditions that use closures with blocks.\n\n **Why is this bad?** Style, using blocks in the condition makes it hard to read.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n // Bad\n if { true } { /* ... */ }\n\n // Good\n if true { /* ... */ }\n ```\n\n // or\n\n ```rust\n # fn somefunc() -> bool { true };\n // Bad\n if { let x = somefunc(); x } { /* ... */ }\n\n // Good\n let res = { let x = somefunc(); x };\n if res { /* ... */ }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "nonminimal_bool", + "id_span": { + "path": "clippy_lints/src/booleans.rs", + "line": 31 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for boolean expressions that can be written more concisely.\n\n **Why is this bad?** Readability of boolean expressions suffers from\n unnecessary duplication.\n\n **Known problems:** Ignores short circuiting behavior of `||` and\n `&&`. Ignores `|`, `&` and `^`.\n\n **Example:**\n ```ignore\n if a && true // should be: if a\n if !(a == b) // should be: if a != b\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "logic_bug", + "id_span": { + "path": "clippy_lints/src/booleans.rs", + "line": 49 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for boolean expressions that contain terminals that can be eliminated.\n\n **Why is this bad?** This is most likely a logic bug.\n\n **Known problems:** Ignores short circuiting behavior.\n\n **Example:**\n ```ignore\n if a && b || a { ... }\n ```\n The `b` is unnecessary, the expression is equivalent to `if a`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "naive_bytecount", + "id_span": { + "path": "clippy_lints/src/bytecount.rs", + "line": 30 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "cargo_common_metadata", + "id_span": { + "path": "clippy_lints/src/cargo_common_metadata.rs", + "line": 49 + }, + "group": "clippy::cargo", + "docs": " **What it does:** Checks to see if all common metadata is defined in `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata\n\n **Why is this bad?** It will be more difficult for users to discover the\n purpose of the crate, and key information related to it.\n\n **Known problems:** None.\n\n **Example:**\n ```toml\n # This `Cargo.toml` is missing an authors field:\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n\n Should include an authors field like:\n\n ```toml\n # This `Cargo.toml` includes all common metadata\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n authors = [\"Someone \"]\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "case_sensitive_file_extension_comparisons", + "id_span": { + "path": "clippy_lints/src/case_sensitive_file_extension_comparisons.rs", + "line": 34 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for calls to `ends_with` with possible file extensions\n and suggests to use a case-insensitive approach instead.\n\n **Why is this bad?**\n `ends_with` is case-sensitive and may not detect files with a valid extension.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.ends_with(\".rs\")\n }\n ```\n Use instead:\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case(\"rs\")) == Some(true)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "checked_conversions", + "id_span": { + "path": "clippy_lints/src/checked_conversions.rs", + "line": 40 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for explicit bounds checking when casting.\n **Why is this bad?** Reduces the readability of statements & is error prone.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let foo: u32 = 5;\n # let _ =\n foo <= i32::MAX as u32\n # ;\n ```\n\n Could be written:\n\n ```rust\n # use std::convert::TryFrom;\n # let foo = 1;\n # let _ =\n i32::try_from(foo).is_ok()\n # ;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "cognitive_complexity", + "id_span": { + "path": "clippy_lints/src/cognitive_complexity.rs", + "line": 24 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for methods with high cognitive complexity.\n **Why is this bad?** Methods of high cognitive complexity tend to be hard to\n both read and maintain. Also LLVM will tend to optimize small methods better.\n\n **Known problems:** Sometimes it's hard to find a way to reduce the\n complexity.\n\n **Example:** No. You'll see it when you get the warning.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "collapsible_if", + "id_span": { + "path": "clippy_lints/src/collapsible_if.rs", + "line": 50 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for nested `if` statements which can be collapsed by `&&`-combining their conditions.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x {\n if y {\n …\n }\n }\n\n ```\n\n Should be written:\n\n ```rust.ignore\n if x && y {\n …\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "collapsible_else_if", + "id_span": { + "path": "clippy_lints/src/collapsible_if.rs", + "line": 85 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for collapsible `else { if ... }` expressions that can be collapsed to `else if ...`.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n\n if x {\n …\n } else {\n if y {\n …\n }\n }\n ```\n\n Should be written:\n\n ```rust.ignore\n if x {\n …\n } else if y {\n …\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "collapsible_match", + "id_span": { + "path": "clippy_lints/src/collapsible_match.rs", + "line": 44 + }, + "group": "clippy::style", + "docs": " **What it does:** Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together without adding any branches.\n\n Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\n cases where merging would most likely make the code more readable.\n\n **Why is this bad?** It is unnecessarily verbose and complex.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(n) => match n {\n Ok(n) => n,\n _ => return,\n }\n None => return,\n };\n }\n ```\n Use instead:\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(Ok(n)) => n,\n _ => return,\n };\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "comparison_chain", + "id_span": { + "path": "clippy_lints/src/comparison_chain.rs", + "line": 49 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks comparison chains written with `if` that can be rewritten with `match` and `cmp`.\n\n **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get\n repetitive\n\n **Known problems:** The match statement may be slower due to the compiler\n not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)\n\n **Example:**\n ```rust,ignore\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n if x > y {\n a()\n } else if x < y {\n b()\n } else {\n c()\n }\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n use std::cmp::Ordering;\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n match x.cmp(&y) {\n Ordering::Greater => a(),\n Ordering::Less => b(),\n Ordering::Equal => c()\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ifs_same_cond", + "id_span": { + "path": "clippy_lints/src/copies.rs", + "line": 33 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for consecutive `if`s with the same condition.\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if a == b {\n …\n } else if a == b {\n …\n }\n ```\n\n Note that this lint ignores all conditions with a function call as it could\n have side effects:\n\n ```ignore\n if foo() {\n …\n } else if foo() { // not linted\n …\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "same_functions_in_if_condition", + "id_span": { + "path": "clippy_lints/src/copies.rs", + "line": 80 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for consecutive `if`s with the same function call.\n **Why is this bad?** This is probably a copy & paste error.\n Despite the fact that function can have side effects and `if` works as\n intended, such an approach is implicit and can be considered a \"code smell\".\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if foo() == bar {\n …\n } else if foo() == bar {\n …\n }\n ```\n\n This probably should be:\n ```ignore\n if foo() == bar {\n …\n } else if foo() == baz {\n …\n }\n ```\n\n or if the original code was not a typo and called function mutates a state,\n consider move the mutation out of the `if` condition to avoid similarity to\n a copy & paste error:\n\n ```ignore\n let first = foo();\n if first == bar {\n …\n } else {\n let second = foo();\n if second == bar {\n …\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "if_same_then_else", + "id_span": { + "path": "clippy_lints/src/copies.rs", + "line": 101 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `if/else` with the same body as the *then* part and the *else* part.\n\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n let foo = if … {\n 42\n } else {\n 42\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "copy_iterator", + "id_span": { + "path": "clippy_lints/src/copy_iterator.rs", + "line": 27 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for types that implement `Copy` as well as `Iterator`.\n\n **Why is this bad?** Implicit copies can be confusing when working with\n iterator combinators.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[derive(Copy, Clone)]\n struct Countdown(u8);\n\n impl Iterator for Countdown {\n // ...\n }\n\n let a: Vec<_> = my_iterator.take(1).collect();\n let b: Vec<_> = my_iterator.collect();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "create_dir", + "id_span": { + "path": "clippy_lints/src/create_dir.rs", + "line": 24 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.\n **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n std::fs::create_dir(\"foo\");\n ```\n Use instead:\n ```rust\n std::fs::create_dir_all(\"foo\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "dbg_macro", + "id_span": { + "path": "clippy_lints/src/dbg_macro.rs", + "line": 25 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of dbg!() macro.\n **Why is this bad?** `dbg!` macro is intended as a debugging tool. It\n should not be in version control.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n dbg!(true)\n\n // Good\n true\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "default_trait_access", + "id_span": { + "path": "clippy_lints/src/default.rs", + "line": 33 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for literal calls to `Default::default()`.\n **Why is this bad?** It's more clear to the reader to use the name of the type whose default is\n being gotten than the generic `Default`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let s: String = Default::default();\n\n // Good\n let s = String::default();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "field_reassign_with_default", + "id_span": { + "path": "clippy_lints/src/default.rs", + "line": 63 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for immediate reassignment of fields initialized with Default::default().\n\n **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).\n\n **Known problems:** Assignments to patterns that are of tuple type are not linted.\n\n **Example:**\n Bad:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let mut a: A = Default::default();\n a.i = 42;\n ```\n Use instead:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let a = A {\n i: 42,\n .. Default::default()\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "default_numeric_fallback", + "id_span": { + "path": "clippy_lints/src/default_numeric_fallback.rs", + "line": 44 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference.\n\n Default numeric fallback means that if numeric types have not yet been bound to concrete\n types at the end of type inference, then integer type is bound to `i32`, and similarly\n floating type is bound to `f64`.\n\n See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.\n\n **Why is this bad?** For those who are very careful about types, default numeric fallback\n can be a pitfall that cause unexpected runtime behavior.\n\n **Known problems:** This lint can only be allowed at the function level or above.\n\n **Example:**\n ```rust\n let i = 10;\n let f = 1.23;\n ```\n\n Use instead:\n ```rust\n let i = 10i32;\n let f = 1.23f64;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "explicit_deref_methods", + "id_span": { + "path": "clippy_lints/src/dereference.rs", + "line": 32 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.\n **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise,\n when not part of a method chain.\n\n **Example:**\n ```rust\n use std::ops::Deref;\n let a: &mut String = &mut String::from(\"foo\");\n let b: &str = a.deref();\n ```\n Could be written as:\n ```rust\n let a: &mut String = &mut String::from(\"foo\");\n let b = &*a;\n ```\n\n This lint excludes\n ```rust,ignore\n let _ = d.unwrap().deref();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "derive_hash_xor_eq", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 42 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for deriving `Hash` but implementing `PartialEq` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `HashMap`) so it’s probably a bad idea to use a\n default-generated `Hash` implementation with an explicitly defined\n `PartialEq`. In particular, the following must hold for any type:\n\n ```text\n k1 == k2 ⇒ hash(k1) == hash(k2)\n ```\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n #[derive(Hash)]\n struct Foo;\n\n impl PartialEq for Foo {\n ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "derive_ord_xor_partial_ord", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 93 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `sort`) so it’s probably a bad idea to use a\n default-generated `Ord` implementation with an explicitly defined\n `PartialOrd`. In particular, the following must hold for any type\n implementing `Ord`:\n\n ```text\n k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()\n ```\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n #[derive(Ord, PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n ...\n }\n ```\n Use instead:\n ```rust,ignore\n #[derive(PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n fn partial_cmp(&self, other: &Foo) -> Option {\n Some(self.cmp(other))\n }\n }\n\n impl Ord for Foo {\n ...\n }\n ```\n or, if you don't need a custom ordering:\n ```rust,ignore\n #[derive(Ord, PartialOrd, PartialEq, Eq)]\n struct Foo;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "expl_impl_clone_on_copy", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 119 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for explicit `Clone` implementations for `Copy` types.\n\n **Why is this bad?** To avoid surprising behaviour, these traits should\n agree and the behaviour of `Copy` cannot be overridden. In almost all\n situations a `Copy` type should have a `Clone` implementation that does\n nothing more than copy the object, which is what `#[derive(Copy, Clone)]`\n gets you.\n\n **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925\n\n **Example:**\n ```rust,ignore\n #[derive(Copy)]\n struct Foo;\n\n impl Clone for Foo {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unsafe_derive_deserialize", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 153 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for deriving `serde::Deserialize` on a type that has methods using `unsafe`.\n\n **Why is this bad?** Deriving `serde::Deserialize` will create a constructor\n that may violate invariants hold by another constructor.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use serde::Deserialize;\n\n #[derive(Deserialize)]\n pub struct Foo {\n // ..\n }\n\n impl Foo {\n pub fn new() -> Self {\n // setup here ..\n }\n\n pub unsafe fn parts() -> (&str, &str) {\n // assumes invariants hold\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "disallowed_method", + "id_span": { + "path": "clippy_lints/src/disallowed_method.rs", + "line": 46 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Denies the configured methods and functions in clippy.toml\n **Why is this bad?** Some methods are undesirable in certain contexts,\n and it's beneficial to lint for them as needed.\n\n **Known problems:** Currently, you must write each function as a\n fully-qualified path. This lint doesn't support aliases or reexported\n names; be aware that many types in `std` are actually reexports.\n\n For example, if you want to disallow `Duration::as_secs`, your clippy.toml\n configuration would look like\n `disallowed-methods = [\"core::time::Duration::as_secs\"]` and not\n `disallowed-methods = [\"std::time::Duration::as_secs\"]` as you might expect.\n\n **Example:**\n\n An example clippy.toml configuration:\n ```toml\n # clippy.toml\n disallowed-methods = [\"alloc::vec::Vec::leak\", \"std::time::Instant::now\"]\n ```\n\n ```rust,ignore\n // Example code where clippy issues a warning\n let xs = vec![1, 2, 3, 4];\n xs.leak(); // Vec::leak is disallowed in the config.\n\n let _now = Instant::now(); // Instant::now is disallowed in the config.\n ```\n\n Use instead:\n ```rust,ignore\n // Example code which does not raise clippy warning\n let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.\n xs.push(123); // Vec::push is _not_ disallowed in the config.\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "doc_markdown", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 63 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for the presence of `_`, `::` or camel-case words outside ticks in documentation.\n\n **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and\n camel-case probably indicates some code which should be included between\n ticks. `_` can also be used for emphasis in markdown, this lint tries to\n consider that.\n\n **Known problems:** Lots of bad docs won’t be fixed, what the lint checks\n for is limited, and there are still false positives.\n\n In addition, when writing documentation comments, including `[]` brackets\n inside a link text would trip the parser. Therfore, documenting link with\n `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec\n would fail.\n\n **Examples:**\n ```rust\n /// Do something with the foo_bar parameter. See also\n /// that::other::module::foo.\n // ^ `foo_bar` and `that::other::module::foo` should be ticked.\n fn doit(foo_bar: usize) {}\n ```\n\n ```rust\n // Link text with `[]` brackets should be written as following:\n /// Consume the array and return the inner\n /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].\n /// [SmallVec]: SmallVec\n fn main() {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_safety_doc", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 97 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the doc comments of publicly visible unsafe functions and warns if there is no `# Safety` section.\n\n **Why is this bad?** Unsafe functions should document their safety\n preconditions, so that users can be sure they are using them safely.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n# type Universe = ();\n /// This function should really be documented\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n\n At least write a line about safety:\n\n ```rust\n# type Universe = ();\n /// # Safety\n ///\n /// This function should not be called before the horsemen are ready.\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_errors_doc", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 126 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks the doc comments of publicly visible functions that return a `Result` type and warns if there is no `# Errors` section.\n\n **Why is this bad?** Documenting the type of errors that can be returned from a\n function can help callers write code to handle the errors appropriately.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function returns a `Result` it has an `# Errors` section in\n its doc comment:\n\n ```rust\n# use std::io;\n /// # Errors\n ///\n /// Will return `Err` if `filename` does not exist or the user does not have\n /// permission to read it.\n pub fn read(filename: String) -> io::Result {\n unimplemented!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_panics_doc", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 157 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks the doc comments of publicly visible functions that may panic and warns if there is no `# Panics` section.\n\n **Why is this bad?** Documenting the scenarios in which panicking occurs\n can help callers who do not want to panic to avoid those situations.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function may panic it has a `# Panics` section in\n its doc comment:\n\n ```rust\n /// # Panics\n ///\n /// Will panic if y is 0\n pub fn divide_by(x: i32, y: i32) -> i32 {\n if y == 0 {\n panic!(\"Cannot divide by 0\")\n } else {\n x / y\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_doctest_main", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 185 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `fn main() { .. }` in doctests\n **Why is this bad?** The test can be shorter (and likely more readable)\n if the `fn main()` is left implicit.\n\n **Known problems:** None.\n\n **Examples:**\n ``````rust\n /// An example of a doctest with a `main()` function\n ///\n /// # Examples\n ///\n /// ```\n /// fn main() {\n /// // this needs not be in an `fn`\n /// }\n /// ```\n fn needless_main() {\n unimplemented!();\n }\n ``````\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "double_comparisons", + "id_span": { + "path": "clippy_lints/src/double_comparison.rs", + "line": 33 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for double comparisons that could be simplified to a single expression.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 2;\n if x == y || x < y {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 2;\n if x <= y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "double_parens", + "id_span": { + "path": "clippy_lints/src/double_parens.rs", + "line": 35 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for unnecessary double parentheses.\n **Why is this bad?** This makes code harder to read and might indicate a\n mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn simple_double_parens() -> i32 {\n ((0))\n }\n\n // Good\n fn simple_no_parens() -> i32 {\n 0\n }\n\n // or\n\n # fn foo(bar: usize) {}\n // Bad\n foo((0));\n\n // Good\n foo(0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "drop_ref", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 26 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::drop` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `drop` on a reference will only drop the\n reference itself, which is a no-op. It will not call the `drop` method (from\n the `Drop` trait implementation) on the underlying referenced value, which\n is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n let mut lock_guard = mutex.lock();\n std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex\n // still locked\n operation_that_requires_mutex_to_be_unlocked();\n ```\n", + "applicability": null + }, + { + "id": "forget_ref", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 47 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::forget` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `forget` on a reference will only forget the\n reference itself, which is a no-op. It will not forget the underlying\n referenced\n value, which is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = Box::new(1);\n std::mem::forget(&x) // Should have been forget(x), x will still be dropped\n ```\n", + "applicability": null + }, + { + "id": "drop_copy", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 68 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::drop` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::drop` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the\n value will be copied and moved into the function on invocation.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::drop(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", + "applicability": null + }, + { + "id": "forget_copy", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 95 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::forget` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::forget` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the\n value will be copied and moved into the function on invocation.\n\n An alternative, but also valid, explanation is that Copy types do not\n implement\n the Drop trait, which means they have no destructors. Without a destructor,\n there\n is nothing for `std::mem::forget` to ignore.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::forget(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", + "applicability": null + }, + { + "id": "duration_subsec", + "id_span": { + "path": "clippy_lints/src/duration_subsec.rs", + "line": 34 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for calculation of subsecond microseconds or milliseconds from other `Duration` methods.\n\n **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or\n `Duration::subsec_millis()` than to calculate them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::time::Duration;\n let dur = Duration::new(5, 0);\n\n // Bad\n let _micros = dur.subsec_nanos() / 1_000;\n let _millis = dur.subsec_nanos() / 1_000_000;\n\n // Good\n let _micros = dur.subsec_micros();\n let _millis = dur.subsec_millis();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "else_if_without_else", + "id_span": { + "path": "clippy_lints/src/else_if_without_else.rs", + "line": 44 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of if expressions with an `else if` branch, but without a final `else` branch.\n\n **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n }\n ```\n\n Could be written:\n\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n } else {\n // We don't care about zero.\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "empty_enum", + "id_span": { + "path": "clippy_lints/src/empty_enum.rs", + "line": 38 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `enum`s with no variants.\n As of this writing, the `never_type` is still a\n nightly-only experimental API. Therefore, this lint is only triggered\n if the `never_type` is enabled.\n\n **Why is this bad?** If you want to introduce a type which\n can't be instantiated, you should use `!` (the primitive type \"never\"),\n or a wrapper around it, because `!` has more extensive\n compiler support (type inference, etc...) and wrappers\n around it are the conventional way to define an uninhabited type.\n For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)\n\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n enum Test {}\n ```\n\n Good:\n ```rust\n #![feature(never_type)]\n\n struct Test(!);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_entry", + "id_span": { + "path": "clippy_lints/src/entry.rs", + "line": 48 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` or `BTreeMap`.\n\n **Why is this bad?** Using `entry` is more efficient.\n\n **Known problems:** Some false negatives, eg.:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let v = 1;\n # let k = 1;\n if !map.contains_key(&k) {\n map.insert(k.clone(), v);\n }\n ```\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n if !map.contains_key(&k) {\n map.insert(k, v);\n }\n ```\n can both be rewritten as:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n map.entry(k).or_insert(v);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "enum_clike_unportable_variant", + "id_span": { + "path": "clippy_lints/src/enum_clike.rs", + "line": 31 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for C-like enumerations that are `repr(isize/usize)` and have values that don't fit into an `i32`.\n\n **Why is this bad?** This will truncate the variant value on 32 bit\n architectures, but works fine on 64 bit.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # #[cfg(target_pointer_width = \"64\")]\n #[repr(usize)]\n enum NonPortable {\n X = 0x1_0000_0000,\n Y = 0,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "enum_variant_names", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 36 + }, + "group": "clippy::style", + "docs": " **What it does:** Detects enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", + "applicability": null + }, + { + "id": "pub_enum_variant_names", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 66 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Detects public enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Public enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n pub enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", + "applicability": null + }, + { + "id": "module_name_repetitions", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 91 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Detects type names that are prefixed or suffixed by the containing module's name.\n\n **Why is this bad?** It requires the user to type the module name twice.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n mod cake {\n struct BlackForestCake;\n }\n ```\n Could be written as:\n ```rust\n mod cake {\n struct BlackForest;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "module_inception", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 121 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for modules that have the same name as their parent module\n\n **Why is this bad?** A typical beginner mistake is to have `mod foo;` and\n again `mod foo { ..\n }` in `foo.rs`.\n The expectation is that items inside the inner `mod foo { .. }` are then\n available\n through `foo::x`, but they are only available through\n `foo::foo::x`.\n If this is done on purpose, it would be better to choose a more\n representative module name.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // lib.rs\n mod foo;\n // foo.rs\n mod foo {\n ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "eq_op", + "id_span": { + "path": "clippy_lints/src/eq_op.rs", + "line": 34 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for equal operands to comparison, logical and bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n `||`, `&`, `|`, `^`, `-` and `/`).\n\n **Why is this bad?** This is usually just a typo or a copy and paste error.\n\n **Known problems:** False negatives: We had some false positives regarding\n calls (notably [racer](https://github.com/phildawes/racer) had one instance\n of `x.pop() && x.pop()`), so we removed matching any function or method\n calls. We may introduce a list of known pure functions in the future.\n\n **Example:**\n ```rust\n # let x = 1;\n if x + 1 == x + 1 {}\n ```\n or\n ```rust\n # let a = 3;\n # let b = 4;\n assert_eq!(a, a);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "op_ref", + "id_span": { + "path": "clippy_lints/src/eq_op.rs", + "line": 56 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound\n and suggests to dereference the other argument instead\n\n **Why is this bad?** It is more idiomatic to dereference the other argument.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n // Bad\n &x == y\n\n // Good\n x == *y\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "erasing_op", + "id_span": { + "path": "clippy_lints/src/erasing_op.rs", + "line": 25 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for erasing operations, e.g., `x * 0`.\n **Why is this bad?** The whole expression can be replaced by zero.\n This is most likely not the intended outcome and should probably be\n corrected\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1;\n 0 / x;\n 0 * x;\n x & 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "boxed_local", + "id_span": { + "path": "clippy_lints/src/escape.rs", + "line": 43 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for usage of `Box` where an unboxed `T` would work fine.\n\n **Why is this bad?** This is an unnecessary allocation, and bad for\n performance. It is only necessary to allocate if you wish to move the box\n into something.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(bar: usize) {}\n // Bad\n let x = Box::new(1);\n foo(*x);\n println!(\"{}\", *x);\n\n // Good\n let x = 1;\n foo(x);\n println!(\"{}\", x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_closure", + "id_span": { + "path": "clippy_lints/src/eta_reduction.rs", + "line": 37 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for closures which just call another function where the function can be called directly. `unsafe` functions or calls where types\n get adjusted are ignored.\n\n **Why is this bad?** Needlessly creating a closure adds code for no benefit\n and gives the optimizer more work.\n\n **Known problems:** If creating the closure inside the closure has a side-\n effect then moving the closure creation out will change when that side-\n effect runs.\n See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.\n\n **Example:**\n ```rust,ignore\n // Bad\n xs.map(|x| foo(x))\n\n // Good\n xs.map(foo)\n ```\n where `foo(_)` is a plain function that takes the exact argument type of\n `x`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_closure_for_method_calls", + "id_span": { + "path": "clippy_lints/src/eta_reduction.rs", + "line": 61 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for closures which only invoke a method on the closure argument and can be replaced by referencing the method directly.\n\n **Why is this bad?** It's unnecessary to create the closure.\n\n **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),\n [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),\n [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)\n\n\n **Example:**\n ```rust,ignore\n Some('a').map(|s| s.to_uppercase());\n ```\n may be rewritten as\n ```rust,ignore\n Some('a').map(char::to_uppercase);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "eval_order_dependence", + "id_span": { + "path": "clippy_lints/src/eval_order_dependence.rs", + "line": 38 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation\n order of sub-expressions.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Code which intentionally depends on the evaluation\n order, or which is correct for any evaluation order.\n\n **Example:**\n ```rust\n let mut x = 0;\n\n // Bad\n let a = {\n x = 1;\n 1\n } + x;\n // Unclear whether a is 1 or 2.\n\n // Good\n let tmp = {\n x = 1;\n 1\n };\n let a = tmp + x;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "diverging_sub_expression", + "id_span": { + "path": "clippy_lints/src/eval_order_dependence.rs", + "line": 62 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for diverging calls that are not match arms or statements.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Someone might want to use `some_bool || panic!()` as a\n shorthand.\n\n **Example:**\n ```rust,no_run\n # fn b() -> bool { true }\n # fn c() -> bool { true }\n let a = b() || panic!() || c();\n // `c()` is dead, `panic!()` is only called if `b()` returns `false`\n let x = (a, b, c, panic!());\n // can simply be replaced by `panic!()`\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "struct_excessive_bools", + "id_span": { + "path": "clippy_lints/src/excessive_bools.rs", + "line": 40 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for excessive use of bools in structs.\n\n **Why is this bad?** Excessive bools in a struct\n is often a sign that it's used as a state machine,\n which is much better implemented as an enum.\n If it's not the case, excessive bools usually benefit\n from refactoring into two-variant enums for better\n readability and API.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n struct S {\n is_pending: bool,\n is_processing: bool,\n is_finished: bool,\n }\n ```\n\n Good:\n ```rust\n enum S {\n Pending,\n Processing,\n Finished,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fn_params_excessive_bools", + "id_span": { + "path": "clippy_lints/src/excessive_bools.rs", + "line": 78 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for excessive use of bools in function definitions.\n\n **Why is this bad?** Calls to such functions\n are confusing and error prone, because it's\n hard to remember argument order and you have\n no type system support to back you up. Using\n two-variant enums instead of bools often makes\n API easier to use.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust,ignore\n fn f(is_round: bool, is_hot: bool) { ... }\n ```\n\n Good:\n ```rust,ignore\n enum Shape {\n Round,\n Spiky,\n }\n\n enum Temperature {\n Hot,\n IceCold,\n }\n\n fn f(shape: Shape, temperature: Temperature) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "exhaustive_enums", + "id_span": { + "path": "clippy_lints/src/exhaustive_items.rs", + "line": 34 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", + "applicability": null + }, + { + "id": "exhaustive_structs", + "id_span": { + "path": "clippy_lints/src/exhaustive_items.rs", + "line": 64 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", + "applicability": null + }, + { + "id": "exit", + "id_span": { + "path": "clippy_lints/src/exit.rs", + "line": 20 + }, + "group": "clippy::restriction", + "docs": " **What it does:** `exit()` terminates the program and doesn't provide a stack trace.\n\n **Why is this bad?** Ideally a program is terminated by finishing\n the main function.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n std::process::exit(0)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "explicit_write", + "id_span": { + "path": "clippy_lints/src/explicit_write.rs", + "line": 25 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `write!()` / `writeln()!` which can be replaced with `(e)print!()` / `(e)println!()`\n\n **Why is this bad?** Using `(e)println! is clearer and more concise\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::io::Write;\n # let bar = \"furchtbar\";\n // this would be clearer as `eprintln!(\"foo: {:?}\", bar);`\n writeln!(&mut std::io::stderr(), \"foo: {:?}\", bar).unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fallible_impl_from", + "id_span": { + "path": "clippy_lints/src/fallible_impl_from.rs", + "line": 45 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`\n **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo(i32);\n\n // Bad\n impl From for Foo {\n fn from(s: String) -> Self {\n Foo(s.parse().unwrap())\n }\n }\n ```\n\n ```rust\n // Good\n struct Foo(i32);\n\n use std::convert::TryFrom;\n impl TryFrom for Foo {\n type Error = ();\n fn try_from(s: String) -> Result {\n if let Ok(parsed) = s.parse() {\n Ok(Foo(parsed))\n } else {\n Err(())\n }\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "float_equality_without_abs", + "id_span": { + "path": "clippy_lints/src/float_equality_without_abs.rs", + "line": 37 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.\n\n **Why is this bad?** The code without `.abs()` is more likely to have a bug.\n\n **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is\n technically unneccessary. However, it will make the code more robust and doesn't have any\n large performance implications. If the abs call was deliberately left out for performance\n reasons, it is probably better to state this explicitly in the code, which then can be done\n with an allow.\n\n **Example:**\n\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b) < f32::EPSILON\n }\n ```\n Use instead:\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b).abs() < f32::EPSILON\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "excessive_precision", + "id_span": { + "path": "clippy_lints/src/float_literal.rs", + "line": 30 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for float literals with a precision greater than that supported by the underlying type.\n\n **Why is this bad?** Rust will truncate the literal silently.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let v: f32 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789\n\n // Good\n let v: f64 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789_9\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "lossy_float_literal", + "id_span": { + "path": "clippy_lints/src/float_literal.rs", + "line": 54 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for whole number float literals that cannot be represented as the underlying type without loss.\n\n **Why is this bad?** Rust will silently lose precision during\n conversion to a float.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _: f32 = 16_777_217.0; // 16_777_216.0\n\n // Good\n let _: f32 = 16_777_216.0;\n let _: f64 = 16_777_217.0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "imprecise_flops", + "id_span": { + "path": "clippy_lints/src/floating_point_arithmetic.rs", + "line": 45 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy\n at the cost of performance.\n\n **Why is this bad?** Negatively impacts accuracy.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let a = 3f32;\n let _ = a.powf(1.0 / 3.0);\n let _ = (1.0 + a).ln();\n let _ = a.exp() - 1.0;\n ```\n\n is better expressed as\n\n ```rust\n let a = 3f32;\n let _ = a.cbrt();\n let _ = a.ln_1p();\n let _ = a.exp_m1();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "suboptimal_flops", + "id_span": { + "path": "clippy_lints/src/floating_point_arithmetic.rs", + "line": 102 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve both\n accuracy and performance.\n\n **Why is this bad?** Negatively impacts accuracy and performance.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = (2f32).powf(a);\n let _ = E.powf(a);\n let _ = a.powf(1.0 / 2.0);\n let _ = a.log(2.0);\n let _ = a.log(10.0);\n let _ = a.log(E);\n let _ = a.powf(2.0);\n let _ = a * 2.0 + 4.0;\n let _ = if a < 0.0 {\n -a\n } else {\n a\n };\n let _ = if a < 0.0 {\n a\n } else {\n -a\n };\n ```\n\n is better expressed as\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = a.exp2();\n let _ = a.exp();\n let _ = a.sqrt();\n let _ = a.log2();\n let _ = a.log10();\n let _ = a.ln();\n let _ = a.powi(2);\n let _ = a.mul_add(2.0, 4.0);\n let _ = a.abs();\n let _ = -a.abs();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_format", + "id_span": { + "path": "clippy_lints/src/format.rs", + "line": 37 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the use of `format!(\"string literal with no argument\")` and `format!(\"{}\", foo)` where `foo` is a string.\n\n **Why is this bad?** There is no point of doing that. `format!(\"foo\")` can\n be replaced by `\"foo\".to_owned()` if you really need a `String`. The even\n worse `&format!(\"foo\")` is often encountered in the wild. `format!(\"{}\",\n foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`\n if `foo: &str`.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n\n // Bad\n # let foo = \"foo\";\n format!(\"{}\", foo);\n\n // Good\n format!(\"foo\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "suspicious_assignment_formatting", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 22 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.\n\n **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or\n confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n a =- 42; // confusing, should it be `a -= 42` or `a = -42`?\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_unary_op_formatting", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 44 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks the formatting of a unary operator on the right hand side of a binary operator. It lints if there is no space between the binary and unary operators,\n but there is a space between the unary and its operand.\n\n **Why is this bad?** This is either a typo in the binary operator or confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo <- 30 { // this should be `foo < -30` but looks like a different operator\n }\n\n if foo &&! bar { // this should be `foo && !bar` but looks like a different operator\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_else_formatting", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 80 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for formatting of `else`. It lints if the `else` is followed immediately by a newline or the `else` seems to be missing.\n\n **Why is this bad?** This is probably some refactoring remnant, even if the\n code is correct, it might look confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo {\n } { // looks like an `else` is missing here\n }\n\n if foo {\n } if bar { // looks like an `else` is missing here\n }\n\n if foo {\n } else\n\n { // this is the `else` block of the previous `if`, but should it be?\n }\n\n if foo {\n } else\n\n if bar { // this is the `else` block of the previous `if`, but should it be?\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "possible_missing_comma", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 100 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines.\n\n **Why is this bad?** This could lead to unexpected results.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = &[\n -1, -2, -3 // <= no comma here\n -4, -5, -6\n ];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "from_over_into", + "id_span": { + "path": "clippy_lints/src/from_over_into.rs", + "line": 39 + }, + "group": "clippy::style", + "docs": " **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.\n **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct StringWrapper(String);\n\n impl Into for String {\n fn into(self) -> StringWrapper {\n StringWrapper(self)\n }\n }\n ```\n Use instead:\n ```rust\n struct StringWrapper(String);\n\n impl From for StringWrapper {\n fn from(s: String) -> StringWrapper {\n StringWrapper(s)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "from_str_radix_10", + "id_span": { + "path": "clippy_lints/src/from_str_radix_10.rs", + "line": 37 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for function invocations of the form `primitive::from_str_radix(s, 10)`\n\n **Why is this bad?**\n This specific common use case can be rewritten as `s.parse::()`\n (and in most cases, the turbofish can be removed), which reduces code length\n and complexity.\n\n **Known problems:**\n This lint may suggest using (&).parse() instead of .parse() directly\n in some cases, which is correct but adds unnecessary complexity to the code.\n\n **Example:**\n\n ```ignore\n let input: &str = get_input();\n let num = u16::from_str_radix(input, 10)?;\n ```\n Use instead:\n ```ignore\n let input: &str = get_input();\n let num: u16 = input.parse()?;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "too_many_arguments", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 39 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for functions with too many parameters.\n **Why is this bad?** Functions with lots of parameters are considered bad\n style and reduce readability (“what does the 5th parameter mean?”). Consider\n grouping some parameters into a new type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Color;\n fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "too_many_lines", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 62 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions with a large amount of lines.\n **Why is this bad?** Functions with a lot of lines are harder to understand\n due to having to look at a larger amount of code to understand what the\n function is doing. Consider splitting the body of the function into\n multiple functions.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn im_too_long() {\n println!(\"\");\n // ... 100 more LoC\n println!(\"\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "not_unsafe_ptr_arg_deref", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 96 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for public functions that dereference raw pointer arguments but are not marked unsafe.\n\n **Why is this bad?** The function should probably be marked `unsafe`, since\n for an arbitrary raw pointer, there is no way of telling for sure if it is\n valid.\n\n **Known problems:**\n\n * It does not check functions recursively so if the pointer is passed to a\n private non-`unsafe` function which does the dereferencing, the lint won't\n trigger.\n * It only checks for arguments whose type are raw pointers, not raw pointers\n got from an argument in some other way (`fn foo(bar: &[*const u8])` or\n `some_argument.get_raw_ptr()`).\n\n **Example:**\n ```rust,ignore\n // Bad\n pub fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n\n // Good\n pub unsafe fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "must_use_unit", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 117 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for a [`#[must_use]`] attribute on unit-returning functions and methods.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Unit values are useless. The attribute is likely\n a remnant of a refactoring that removed the return type.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn useless() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "double_must_use", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 142 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for a [`#[must_use]`] attribute without further information on functions and methods that return a type already\n marked as `#[must_use]`.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** The attribute isn't needed. Not using the result\n will already be reported. Alternatively, one can add some text to the\n attribute to improve the lint message.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn double_must_use() -> Result<(), ()> {\n unimplemented!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "must_use_candidate", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 170 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for public functions that have no [`#[must_use]`] attribute, but return something not already marked\n must-use, have no mutable arg and mutate no statics.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Not bad at all, this lint just shows places where\n you could add the attribute.\n\n **Known problems:** The lint only checks the arguments for mutable\n types without looking if they are actually changed. On the other hand,\n it also ignores a broad range of potentially interesting side effects,\n because we cannot decide whether the programmer intends the function to\n be called for the side effect or the result. Expect many false\n positives. At least we don't lint if the result type is unit or already\n `#[must_use]`.\n\n **Examples:**\n ```rust\n // this could be annotated with `#[must_use]`.\n fn id(t: T) -> T { t }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "result_unit_err", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 216 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for public functions that return a `Result` with an `Err` type of `()`. It suggests using a custom type that\n implements [`std::error::Error`].\n\n **Why is this bad?** Unit does not implement `Error` and carries no\n further information about what went wrong.\n\n **Known problems:** Of course, this lint assumes that `Result` is used\n for a fallible operation (which is after all the intended use). However\n code may opt to (mis)use it as a basic two-variant-enum. In that case,\n the suggestion is misguided, and the code should use a custom enum\n instead.\n\n **Examples:**\n ```rust\n pub fn read_u8() -> Result { Err(()) }\n ```\n should become\n ```rust,should_panic\n use std::fmt;\n\n #[derive(Debug)]\n pub struct EndOfStream;\n\n impl fmt::Display for EndOfStream {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n write!(f, \"End of Stream\")\n }\n }\n\n impl std::error::Error for EndOfStream { }\n\n pub fn read_u8() -> Result { Err(EndOfStream) }\n# fn main() {\n# read_u8().unwrap();\n# }\n ```\n\n Note that there are crates that simplify creating the error type, e.g.\n [`thiserror`](https://docs.rs/thiserror).\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "future_not_send", + "id_span": { + "path": "clippy_lints/src/future_not_send.rs", + "line": 44 + }, + "group": "clippy::nursery", + "docs": " **What it does:** This lint requires Future implementations returned from functions and methods to implement the `Send` marker trait. It is mostly\n used by library authors (public and internal) that target an audience where\n multithreaded executors are likely to be used for running these Futures.\n\n **Why is this bad?** A Future implementation captures some state that it\n needs to eventually produce its final value. When targeting a multithreaded\n executor (which is the norm on non-embedded devices) this means that this\n state may need to be transported to other threads, in other words the\n whole Future needs to implement the `Send` marker trait. If it does not,\n then the resulting Future cannot be submitted to a thread pool in the\n end user’s code.\n\n Especially for generic functions it can be confusing to leave the\n discovery of this problem to the end user: the reported error location\n will be far from its cause and can in many cases not even be fixed without\n modifying the library where the offending Future implementation is\n produced.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn not_send(bytes: std::rc::Rc<[u8]>) {}\n ```\n Use instead:\n ```rust\n async fn is_send(bytes: std::sync::Arc<[u8]>) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "get_last_with_len", + "id_span": { + "path": "clippy_lints/src/get_last_with_len.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.\n\n **Why is this bad?** Using `x.last()` is easier to read and has the same\n result.\n\n Note that using `x[x.len() - 1]` is semantically different from\n `x.last()`. Indexing into the array will panic on out-of-bounds\n accesses, while `x.get()` and `x.last()` will return `None`.\n\n There is another lint (get_unwrap) that covers the case of using\n `x.get(index).unwrap()` instead of `x[index]`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x = vec![2, 3, 5];\n let last_element = x.get(x.len() - 1);\n\n // Good\n let x = vec![2, 3, 5];\n let last_element = x.last();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "identity_op", + "id_span": { + "path": "clippy_lints/src/identity_op.rs", + "line": 24 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for identity operations, e.g., `x + 0`.\n **Why is this bad?** This code can be removed without changing the\n meaning. So it just obscures what's going on. Delete it mercilessly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n x / 1 + 0 * 1 - 0 | 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "if_let_mutex", + "id_span": { + "path": "clippy_lints/src/if_let_mutex.rs", + "line": 36 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `Mutex::lock` calls in `if let` expression with lock calls in any of the else blocks.\n\n **Why is this bad?** The Mutex lock remains held for the whole\n `if let ... else` block and deadlocks.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n if let Ok(thing) = mutex.lock() {\n do_thing();\n } else {\n mutex.lock();\n }\n ```\n Should be written\n ```rust,ignore\n let locked = mutex.lock();\n if let Ok(thing) = locked {\n do_thing(thing);\n } else {\n use_locked(locked);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "if_let_some_result", + "id_span": { + "path": "clippy_lints/src/if_let_some_result.rs", + "line": 34 + }, + "group": "clippy::style", + "docs": " **What it does:*** Checks for unnecessary `ok()` in if let.\n **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match\n on `Ok(pat)`\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for i in iter {\n if let Some(value) = i.parse().ok() {\n vec.push(value)\n }\n }\n ```\n Could be written:\n\n ```ignore\n for i in iter {\n if let Ok(value) = i.parse() {\n vec.push(value)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "if_not_else", + "id_span": { + "path": "clippy_lints/src/if_not_else.rs", + "line": 43 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `!` or `!=` in an if condition with an else branch.\n\n **Why is this bad?** Negations reduce the readability of statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if !v.is_empty() {\n a()\n } else {\n b()\n }\n ```\n\n Could be written:\n\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if v.is_empty() {\n b()\n } else {\n a()\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "implicit_return", + "id_span": { + "path": "clippy_lints/src/implicit_return.rs", + "line": 33 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for missing return statements at the end of a block.\n **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers\n coming from other languages might prefer the expressiveness of `return`. It's possible to miss\n the last returning statement because the only difference is a missing `;`. Especially in bigger\n code with multiple return paths having a `return` keyword makes it easier to find the\n corresponding statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n add return\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "implicit_saturating_sub", + "id_span": { + "path": "clippy_lints/src/implicit_saturating_sub.rs", + "line": 32 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for implicit saturating subtraction.\n **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let end: u32 = 10;\n let start: u32 = 5;\n\n let mut i: u32 = end - start;\n\n // Bad\n if i != 0 {\n i -= 1;\n }\n\n // Good\n i = i.saturating_sub(1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "inconsistent_struct_constructor", + "id_span": { + "path": "clippy_lints/src/inconsistent_struct_constructor.rs", + "line": 58 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for struct constructors where the order of the field init shorthand in the constructor is inconsistent with the order in the struct definition.\n\n **Why is this bad?** Since the order of fields in a constructor doesn't affect the\n resulted instance as the below example indicates,\n\n ```rust\n #[derive(Debug, PartialEq, Eq)]\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n\n // This assertion never fails.\n assert_eq!(Foo { x, y }, Foo { y, x });\n ```\n\n inconsistent order means nothing and just decreases readability and consistency.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n Foo { y, x };\n ```\n\n Use instead:\n ```rust\n # struct Foo {\n # x: i32,\n # y: i32,\n # }\n # let x = 1;\n # let y = 2;\n Foo { x, y };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "out_of_bounds_indexing", + "id_span": { + "path": "clippy_lints/src/indexing_slicing.rs", + "line": 32 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for out of bounds array indexing with a constant index.\n\n **Why is this bad?** This will always panic at runtime.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```no_run\n # #![allow(const_err)]\n let x = [1, 2, 3, 4];\n\n // Bad\n x[9];\n &x[2..9];\n\n // Good\n x[0];\n x[3];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "indexing_slicing", + "id_span": { + "path": "clippy_lints/src/indexing_slicing.rs", + "line": 81 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint does report on arrays if we can tell that slicing operations are in bounds and does not\n lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.\n\n **Why is this bad?** Indexing and slicing can panic at runtime and there are\n safe alternatives.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```rust,no_run\n // Vector\n let x = vec![0; 5];\n\n // Bad\n x[2];\n &x[2..100];\n &x[2..];\n &x[..100];\n\n // Good\n x.get(2);\n x.get(2..100);\n x.get(2..);\n x.get(..100);\n\n // Array\n let y = [0, 1, 2, 3];\n\n // Bad\n &y[10..100];\n &y[10..];\n &y[..100];\n\n // Good\n &y[2..];\n &y[..2];\n &y[0..3];\n y.get(10);\n y.get(10..100);\n y.get(10..);\n y.get(..100);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "infinite_iter", + "id_span": { + "path": "clippy_lints/src/infinite_iter.rs", + "line": 21 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", + "applicability": null + }, + { + "id": "maybe_infinite_iter", + "id_span": { + "path": "clippy_lints/src/infinite_iter.rs", + "line": 40 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", + "applicability": null + }, + { + "id": "multiple_inherent_impl", + "id_span": { + "path": "clippy_lints/src/inherent_impl.rs", + "line": 37 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for multiple inherent implementations of a struct\n **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn one() {}\n }\n impl X {\n fn other() {}\n }\n ```\n\n Could be written:\n\n ```rust\n struct X;\n impl X {\n fn one() {}\n fn other() {}\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inherent_to_string", + "id_span": { + "path": "clippy_lints/src/inherent_to_string.rs", + "line": 44 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inherent_to_string_shadow_display", + "id_span": { + "path": "clippy_lints/src/inherent_to_string.rs", + "line": 89 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n use std::fmt;\n\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A, too\")\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inline_fn_without_body", + "id_span": { + "path": "clippy_lints/src/inline_fn_without_body.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `#[inline]` on trait methods without bodies\n **Why is this bad?** Only implementations of trait methods may be inlined.\n The inline attribute is ignored for trait methods without bodies.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n trait Animal {\n #[inline]\n fn name(&self) -> &'static str;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "int_plus_one", + "id_span": { + "path": "clippy_lints/src/int_plus_one.rs", + "line": 31 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block\n **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 1;\n if x >= y + 1 {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 1;\n if x > y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "integer_division", + "id_span": { + "path": "clippy_lints/src/integer_division.rs", + "line": 26 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for division of integers\n **Why is this bad?** When outside of some very specific algorithms,\n integer division is very often a mistake because it discards the\n remainder.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x = 3 / 2;\n println!(\"{}\", x);\n\n // Good\n let x = 3f32 / 2f32;\n println!(\"{}\", x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "items_after_statements", + "id_span": { + "path": "clippy_lints/src/items_after_statements.rs", + "line": 48 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for items declared after some statement in a block.\n **Why is this bad?** Items live for the entire scope they are declared\n in. But statements are processed in order. This might cause confusion as\n it's hard to figure out which item is meant in a statement.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n foo(); // prints \"foo\"\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n }\n ```\n\n ```rust\n // Good\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n foo(); // prints \"foo\"\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "large_const_arrays", + "id_span": { + "path": "clippy_lints/src/large_const_arrays.rs", + "line": 30 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for large `const` arrays that should be defined as `static` instead.\n\n **Why is this bad?** Performance: const variables are inlined upon use.\n Static items result in only one instance and has a fixed location in memory.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n pub const a = [0u32; 1_000_000];\n\n // Good\n pub static a = [0u32; 1_000_000];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "large_enum_variant", + "id_span": { + "path": "clippy_lints/src/large_enum_variant.rs", + "line": 39 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for large size differences between variants on `enum`s.\n\n **Why is this bad?** Enum size is bounded by the largest variant. Having a\n large variant can penalize the memory layout of that enum.\n\n **Known problems:** This lint obviously cannot take the distribution of\n variants in your running program into account. It is possible that the\n smaller variants make up less than 1% of all instances, in which case\n the overhead is negligible and the boxing is counter-productive. Always\n measure the change this lint suggests.\n\n **Example:**\n\n ```rust\n // Bad\n enum Test {\n A(i32),\n B([i32; 8000]),\n }\n\n // Possibly better\n enum Test2 {\n A(i32),\n B(Box<[i32; 8000]>),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "large_stack_arrays", + "id_span": { + "path": "clippy_lints/src/large_stack_arrays.rs", + "line": 23 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for local arrays that may be too large.\n **Why is this bad?** Large local arrays may cause stack overflow.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = [0u32; 1_000_000];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "len_zero", + "id_span": { + "path": "clippy_lints/src/len_zero.rs", + "line": 41 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than calculating their length. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n if x.len() == 0 {\n ..\n }\n if y.len() != 0 {\n ..\n }\n ```\n instead use\n ```ignore\n if x.is_empty() {\n ..\n }\n if !y.is_empty() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "len_without_is_empty", + "id_span": { + "path": "clippy_lints/src/len_zero.rs", + "line": 66 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for items that implement `.len()` but not `.is_empty()`.\n\n **Why is this bad?** It is good custom to have both methods, because for\n some data structures, asking about the length will be a costly operation,\n whereas `.is_empty()` can usually answer in constant time. Also it used to\n lead to false positives on the [`len_zero`](#len_zero) lint – currently that\n lint will ignore such entities.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl X {\n pub fn len(&self) -> usize {\n ..\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "comparison_to_empty", + "id_span": { + "path": "clippy_lints/src/len_zero.rs", + "line": 103 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for comparing to an empty slice such as `\"\"` or `[]`, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than checking for equality. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n if s == \"\" {\n ..\n }\n\n if arr == [] {\n ..\n }\n ```\n Use instead:\n ```ignore\n if s.is_empty() {\n ..\n }\n\n if arr.is_empty() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_let_if_seq", + "id_span": { + "path": "clippy_lints/src/let_if_seq.rs", + "line": 49 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for variable declarations immediately followed by a conditional affectation.\n\n **Why is this bad?** This is not idiomatic Rust.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let foo;\n\n if bar() {\n foo = 42;\n } else {\n foo = 0;\n }\n\n let mut baz = None;\n\n if bar() {\n baz = Some(42);\n }\n ```\n\n should be written\n\n ```rust,ignore\n let foo = if bar() {\n 42\n } else {\n 0\n };\n\n let baz = if bar() {\n Some(42)\n } else {\n None\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "let_underscore_must_use", + "id_span": { + "path": "clippy_lints/src/let_underscore.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `let _ = ` where expr is #[must_use]\n\n **Why is this bad?** It's better to explicitly\n handle the value of a #[must_use] expr\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn f() -> Result {\n Ok(0)\n }\n\n let _ = f();\n // is_ok() is marked #[must_use]\n let _ = f().is_ok();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "let_underscore_lock", + "id_span": { + "path": "clippy_lints/src/let_underscore.rs", + "line": 56 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `let _ = sync_lock`\n **Why is this bad?** This statement immediately drops the lock instead of\n extending its lifetime to the end of the scope, which is often not intended.\n To extend lock lifetime to the end of the scope, use an underscore-prefixed\n name instead (i.e. _lock). If you want to explicitly drop the lock,\n `std::mem::drop` conveys your intention better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n let _ = mutex.lock();\n ```\n\n Good:\n ```rust,ignore\n let _lock = mutex.lock();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "let_underscore_drop", + "id_span": { + "path": "clippy_lints/src/let_underscore.rs", + "line": 97 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `let _ = ` where expr has a type that implements `Drop`\n\n **Why is this bad?** This statement immediately drops the initializer\n expression instead of extending its lifetime to the end of the scope, which\n is often not intended. To extend the expression's lifetime to the end of the\n scope, use an underscore-prefixed name instead (i.e. _var). If you want to\n explicitly drop the expression, `std::mem::drop` conveys your intention\n better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n struct Droppable;\n impl Drop for Droppable {\n fn drop(&mut self) {}\n }\n {\n let _ = Droppable;\n // ^ dropped here\n /* more code */\n }\n ```\n\n Good:\n ```rust,ignore\n {\n let _droppable = Droppable;\n /* more code */\n // dropped at end of scope\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_lifetimes", + "id_span": { + "path": "clippy_lints/src/lifetimes.rs", + "line": 46 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for lifetime annotations which can be removed by relying on lifetime elision.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:**\n - We bail out if the function has a `where` clause where lifetimes\n are mentioned due to potenial false positives.\n - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the\n placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetime annotations\n fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {\n x\n }\n\n // Good\n fn elided(x: &u8, y: u8) -> &u8 {\n x\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "extra_unused_lifetimes", + "id_span": { + "path": "clippy_lints/src/lifetimes.rs", + "line": 74 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for lifetimes in generics that are never used anywhere else.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetimes\n fn unused_lifetime<'a>(x: u8) {\n // ..\n }\n\n // Good\n fn no_lifetime(x: u8) {\n // ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unreadable_literal", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 33 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Warns if a long integral or floating-point constant does not contain underscores.\n\n **Why is this bad?** Reading long numbers is difficult without separators.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 61864918973511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mistyped_literal_suffixes", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 57 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Warns for mistyped suffix in literals\n **Why is this bad?** This is most probably a typo\n\n **Known problems:**\n - Recommends a signed suffix, even though the number might be too big and an unsigned\n suffix is required\n - Does not match on `_127` since that is a valid grouping for decimal and octal numbers\n\n **Example:**\n\n ```rust\n // Probably mistyped\n 2_32;\n\n // Good\n 2_i32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "inconsistent_digit_grouping", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 80 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns if an integral or floating-point constant is grouped inconsistently with underscores.\n\n **Why is this bad?** Readers may incorrectly interpret inconsistently\n grouped digits.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 618_64_9189_73_511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unusual_byte_groupings", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 99 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns if hexadecimal or binary literals are not grouped by nibble or byte.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u32 = 0xFFF_FFF;\n let y: u8 = 0b01_011_101;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "large_digit_groups", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 118 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Warns if the digits of an integral or floating-point constant are grouped into groups that\n are too large.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u64 = 6186491_8973511;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "decimal_literal_representation", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 136 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns if there is a better representation for a numeric literal.\n **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more\n readable than a decimal representation.\n\n **Known problems:** None.\n\n **Example:**\n\n `255` => `0xFF`\n `65_535` => `0xFFFF`\n `4_042_322_160` => `0xF0F0_F0F0`\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_memcpy", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 57 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "needless_range_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 85 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "explicit_iter_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 114 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "explicit_into_iter_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 142 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_next_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 165 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "for_loops_over_fallibles", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 208 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "while_let_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 237 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "needless_collect", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 259 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "explicit_counter_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 289 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "empty_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 322 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "while_let_on_iterator", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 341 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "for_kv_map", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 369 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "never_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 390 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_range_bound", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 410 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "while_immutable_condition", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 433 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "same_item_push", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 466 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "single_element_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 491 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_flatten", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 522 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "macro_use_imports", + "id_span": { + "path": "clippy_lints/src/macro_use.rs", + "line": 25 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `#[macro_use] use...`.\n **Why is this bad?** Since the Rust 2018 edition you can import\n macro's directly, this is considered idiomatic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[macro_use]\n use some_macro;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "main_recursion", + "id_span": { + "path": "clippy_lints/src/main_recursion.rs", + "line": 22 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for recursion using the entrypoint.\n **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),\n recursing into main() seems like an unintuitive antipattern we should be able to detect.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n fn main() {\n main();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_async_fn", + "id_span": { + "path": "clippy_lints/src/manual_async_fn.rs", + "line": 32 + }, + "group": "clippy::style", + "docs": " **What it does:** It checks for manual implementations of `async` functions.\n **Why is this bad?** It's more idiomatic to use the dedicated syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::future::Future;\n\n fn foo() -> impl Future { async { 42 } }\n ```\n Use instead:\n ```rust\n async fn foo() -> i32 { 42 }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_map", + "id_span": { + "path": "clippy_lints/src/manual_map.rs", + "line": 36 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** `map` is not capable of representing some control flow which works fine in `match`.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_non_exhaustive", + "id_span": { + "path": "clippy_lints/src/manual_non_exhaustive.rs", + "line": 56 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for manual implementations of the non-exhaustive pattern.\n **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent\n and allows possible optimizations when applied to enums.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct S {\n pub a: i32,\n pub b: i32,\n _c: (),\n }\n\n enum E {\n A,\n B,\n #[doc(hidden)]\n _C,\n }\n\n struct T(pub i32, pub i32, ());\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct S {\n pub a: i32,\n pub b: i32,\n }\n\n #[non_exhaustive]\n enum E {\n A,\n B,\n }\n\n #[non_exhaustive]\n struct T(pub i32, pub i32);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "manual_ok_or", + "id_span": { + "path": "clippy_lints/src/manual_ok_or.rs", + "line": 34 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Finds patterns that reimplement `Option::ok_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n let foo: Option = None;\n foo.map_or(Err(\"error\"), |v| Ok(v));\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.ok_or(\"error\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_strip", + "id_span": { + "path": "clippy_lints/src/manual_strip.rs", + "line": 52 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\n the pattern's length.\n\n **Why is this bad?**\n Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no\n slicing which may panic and the compiler does not need to insert this panic code. It is\n also sometimes more readable as it removes the need for duplicating or storing the pattern\n used by `str::{starts,ends}_with` and in the slicing.\n\n **Known problems:**\n None.\n\n **Example:**\n\n ```rust\n let s = \"hello, world!\";\n if s.starts_with(\"hello, \") {\n assert_eq!(s[\"hello, \".len()..].to_uppercase(), \"WORLD!\");\n }\n ```\n Use instead:\n ```rust\n let s = \"hello, world!\";\n if let Some(end) = s.strip_prefix(\"hello, \") {\n assert_eq!(end.to_uppercase(), \"WORLD!\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_unwrap_or", + "id_span": { + "path": "clippy_lints/src/manual_unwrap_or.rs", + "line": 36 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo: Option = None;\n match foo {\n Some(v) => v,\n None => 1,\n };\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.unwrap_or(1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "map_clone", + "id_span": { + "path": "clippy_lints/src/map_clone.rs", + "line": 40 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `map(|x| x.clone())` or dereferencing closures for `Copy` types, on `Iterator` or `Option`,\n and suggests `cloned()` or `copied()` instead\n\n **Why is this bad?** Readability, this can be written more concisely\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.map(|i| *i);\n ```\n\n The correct use would be:\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.cloned();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "map_err_ignore", + "id_span": { + "path": "clippy_lints/src/map_err_ignore.rs", + "line": 101 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for instances of `map_err(|_| Some::Enum)`\n **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\n\n **Known problems:** None.\n\n **Example:**\n Before:\n ```rust\n use std::fmt;\n\n #[derive(Debug)]\n enum Error {\n Indivisible,\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {}\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(|_| Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n\n After:\n ```rust\n use std::{fmt, num::ParseIntError};\n\n #[derive(Debug)]\n enum Error {\n Indivisible(ParseIntError),\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible(_) => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {\n fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n match self {\n Error::Indivisible(source) => Some(source),\n _ => None,\n }\n }\n }\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_identity", + "id_span": { + "path": "clippy_lints/src/map_identity.rs", + "line": 30 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for instances of `map(f)` where `f` is the identity function.\n **Why is this bad?** It can be written more concisely without the call to `map`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();\n ```\n Use instead:\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| 2*x).collect();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "option_map_unit_fn", + "id_span": { + "path": "clippy_lints/src/map_unit_fn.rs", + "line": 48 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", + "applicability": null + }, + { + "id": "result_map_unit_fn", + "id_span": { + "path": "clippy_lints/src/map_unit_fn.rs", + "line": 89 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", + "applicability": null + }, + { + "id": "match_on_vec_items", + "id_span": { + "path": "clippy_lints/src/match_on_vec_items.rs", + "line": 41 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.\n **Why is this bad?** This can panic at runtime.\n\n **Known problems:** None.\n\n **Example:**\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Bad\n match arr[idx] {\n 0 => println!(\"{}\", 0),\n 1 => println!(\"{}\", 3),\n _ => {},\n }\n ```\n Use instead:\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Good\n match arr.get(idx) {\n Some(0) => println!(\"{}\", 0),\n Some(1) => println!(\"{}\", 3),\n _ => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "single_match", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 55 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", + "applicability": null + }, + { + "id": "single_match_else", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 94 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", + "applicability": null + }, + { + "id": "match_ref_pats", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 125 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for matches where all arms match a reference, suggesting to remove the reference and deref the matched expression\n instead. It also checks for `if let &foo = bar` blocks.\n\n **Why is this bad?** It just makes the code less readable. That reference\n destructuring adds nothing to the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n match x {\n &A(ref y) => foo(y),\n &B => bar(),\n _ => frob(&x),\n }\n\n // Good\n match *x {\n A(ref y) => foo(y),\n B => bar(),\n _ => frob(x),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "match_bool", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 159 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for matches where match expression is a `bool`. It suggests to replace the expression with an `if...else` block.\n\n **Why is this bad?** It makes the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n match condition {\n true => foo(),\n false => bar(),\n }\n ```\n Use if/else instead:\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n if condition {\n foo();\n } else {\n bar();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "match_overlapping_arm", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 181 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for overlapping match arms.\n **Why is this bad?** It is likely to be an error and if not, makes the code\n less obvious.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 5;\n match x {\n 1...10 => println!(\"1 ... 10\"),\n 5...15 => println!(\"5 ... 15\"),\n _ => (),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "match_wild_err_arm", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 203 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for arm which matches all errors with `Err(_)` and take drastic actions like `panic!`.\n\n **Why is this bad?** It is generally a bad practice, similar to\n catching all exceptions in java with `catch(Exception)`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Result = Ok(3);\n match x {\n Ok(_) => println!(\"ok\"),\n Err(_) => panic!(\"err\"),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "match_as_ref", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 229 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for match which is used to add a reference to an `Option` value.\n\n **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Option<()> = None;\n\n // Bad\n let r: Option<&()> = match x {\n None => None,\n Some(ref v) => Some(v),\n };\n\n // Good\n let r: Option<&()> = x.as_ref();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "wildcard_enum_match_arm", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 258 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for wildcard enum matches using `_`.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some\n variants, and also may not use correct path to enum if it's not present in the current scope.\n\n **Example:**\n ```rust\n # enum Foo { A(usize), B(usize) }\n # let x = Foo::B(1);\n // Bad\n match x {\n Foo::A(_) => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A(_) => {},\n Foo::B(_) => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "match_wildcard_for_single_variants", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 290 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for wildcard enum matches for a single variant.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may not use correct path to enum\n if it's not present in the current scope.\n\n **Example:**\n\n ```rust\n # enum Foo { A, B, C }\n # let x = Foo::B;\n // Bad\n match x {\n Foo::A => {},\n Foo::B => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A => {},\n Foo::B => {},\n Foo::C => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "wildcard_in_or_patterns", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 317 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for wildcard pattern used with others patterns in same match arm.\n **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.\n It makes the code less readable, especially to spot wildcard pattern use in match arm.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n match \"foo\" {\n \"a\" => {},\n \"bar\" | _ => {},\n }\n\n // Good\n match \"foo\" {\n \"a\" => {},\n _ => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "infallible_destructuring_match", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 352 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for matches being used to destructure a single-variant enum or tuple struct where a `let` will suffice.\n\n **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n\n let data = match wrapper {\n Wrapper::Data(i) => i,\n };\n ```\n\n The correct use would be:\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n let Wrapper::Data(data) = wrapper;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "match_single_binding", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 380 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for useless match that binds to only one value.\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** Suggested replacements may be incorrect when `match`\n is actually binding temporary value, bringing a 'dropped while borrowed' error.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n\n // Bad\n match (a, b) {\n (c, d) => {\n // useless match\n }\n }\n\n // Good\n let (c, d) = (a, b);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "rest_pat_in_fully_bound_structs", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 410 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.\n **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after\n matching all enum variants explicitly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct A { a: i32 }\n let a = A { a: 5 };\n\n // Bad\n match a {\n A { a: 5, .. } => {},\n _ => {},\n }\n\n // Good\n match a {\n A { a: 5 } => {},\n _ => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_pattern_matching", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 458 + }, + "group": "clippy::style", + "docs": " **What it does:** Lint for redundant pattern matching over `Result`, `Option`, `std::task::Poll` or `std::net::IpAddr`\n\n **Why is this bad?** It's more concise and clear to just use the proper\n utility function\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if let Ok(_) = Ok::(42) {}\n if let Err(_) = Err::(42) {}\n if let None = None::<()> {}\n if let Some(_) = Some(42) {}\n if let Poll::Pending = Poll::Pending::<()> {}\n if let Poll::Ready(_) = Poll::Ready(42) {}\n if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}\n if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}\n match Ok::(42) {\n Ok(_) => true,\n Err(_) => false,\n };\n ```\n\n The more idiomatic use would be:\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if Ok::(42).is_ok() {}\n if Err::(42).is_err() {}\n if None::<()>.is_none() {}\n if Some(42).is_some() {}\n if Poll::Pending::<()>.is_pending() {}\n if Poll::Ready(42).is_ready() {}\n if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}\n if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}\n Ok::(42).is_ok();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "match_like_matches_macro", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 491 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `match` or `if let` expressions producing a `bool` that could be written using `matches!`\n\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** This lint falsely triggers, if there are arms with\n `cfg` attributes that remove an arm evaluating to `false`.\n\n **Example:**\n ```rust\n let x = Some(5);\n\n // Bad\n let a = match x {\n Some(0) => true,\n _ => false,\n };\n\n let a = if let Some(0) = x {\n true\n } else {\n false\n };\n\n // Good\n let a = matches!(x, Some(0));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "match_same_arms", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 532 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `match` with identical arm bodies.\n **Why is this bad?** This is probably a copy & paste error. If arm bodies\n are the same on purpose, you can factor them\n [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).\n\n **Known problems:** False positive possible with order dependent `match`\n (see issue\n [#860](https://github.com/rust-lang/rust-clippy/issues/860)).\n\n **Example:**\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => bar(), // <= oops\n }\n ```\n\n This should probably be\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => baz(), // <= fixed\n }\n ```\n\n or if the original code was not a typo:\n ```rust,ignore\n match foo {\n Bar | Baz => bar(), // <= shows the intent better\n Quz => quz(),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mem_discriminant_non_enum", + "id_span": { + "path": "clippy_lints/src/mem_discriminant.rs", + "line": 25 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.\n **Why is this bad?** The value of `mem::discriminant()` on non-enum types\n is unspecified.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n mem::discriminant(&\"hello\");\n mem::discriminant(&&Some(2));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mem_forget", + "id_span": { + "path": "clippy_lints/src/mem_forget.rs", + "line": 21 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is `Drop`.\n\n **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its\n destructor, possibly causing leaks.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::mem;\n # use std::rc::Rc;\n mem::forget(Rc::new(55))\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mem_replace_option_with_none", + "id_span": { + "path": "clippy_lints/src/mem_replace.rs", + "line": 37 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `mem::replace()` on an `Option` with `None`.\n\n **Why is this bad?** `Option` already has the method `take()` for\n taking its current value (Some(..) or None) and replacing it with\n `None`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n let mut an_option = Some(0);\n let replaced = mem::replace(&mut an_option, None);\n ```\n Is better expressed with:\n ```rust\n let mut an_option = Some(0);\n let taken = an_option.take();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mem_replace_with_uninit", + "id_span": { + "path": "clippy_lints/src/mem_replace.rs", + "line": 69 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())` and `mem::replace(&mut _, mem::zeroed())`.\n\n **Why is this bad?** This will lead to undefined behavior even if the\n value is overwritten later, because the uninitialized value may be\n observed in the case of a panic.\n\n **Known problems:** None.\n\n **Example:**\n\n ```\n use std::mem;\n# fn may_panic(v: Vec) -> Vec { v }\n\n #[allow(deprecated, invalid_value)]\n fn myfunc (v: &mut Vec) {\n let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };\n let new_v = may_panic(taken_v); // undefined behavior on panic\n mem::forget(mem::replace(v, new_v));\n }\n ```\n\n The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,\n at the cost of either lazily creating a replacement value or aborting\n on panic, to ensure that the uninitialized value cannot be observed.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mem_replace_with_default", + "id_span": { + "path": "clippy_lints/src/mem_replace.rs", + "line": 93 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `std::mem::replace` on a value of type `T` with `T::default()`.\n\n **Why is this bad?** `std::mem` module already has the method `take` to\n take the current value and replace it with the default value of that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut text = String::from(\"foo\");\n let replaced = std::mem::replace(&mut text, String::default());\n ```\n Is better expressed with:\n ```rust\n let mut text = String::from(\"foo\");\n let taken = std::mem::take(&mut text);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unwrap_used", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 82 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", + "applicability": null + }, + { + "id": "expect_used", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 124 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", + "applicability": null + }, + { + "id": "should_implement_trait", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 153 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "wrong_self_convention", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 186 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", + "applicability": null + }, + { + "id": "wrong_pub_self_convention", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 210 + }, + "group": "clippy::restriction", + "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", + "applicability": null + }, + { + "id": "ok_expect", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 233 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_unwrap_or", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 270 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_map_or_none", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 293 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", + "applicability": null + }, + { + "id": "result_map_or_into_option", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 319 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", + "applicability": null + }, + { + "id": "bind_instead_of_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 352 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "filter_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 375 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "skip_while_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 398 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_flatten", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 421 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "filter_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 450 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_filter_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 476 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", + "applicability": null + }, + { + "id": "manual_find_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 502 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", + "applicability": null + }, + { + "id": "filter_map_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 524 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "flat_map_identity", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 546 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "search_is_some", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 570 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "chars_next_cmp", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 594 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", + "applicability": null + }, + { + "id": "or_fun_call", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 625 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "expect_fun_call", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 660 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "clone_on_copy", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 677 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "clone_on_ref_ptr", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 702 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "clone_double_ref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 724 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "inefficient_to_string", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 747 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "new_ret_no_self", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 808 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "single_char_pattern", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 829 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iterator_step_by_zero", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 848 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "iter_nth_zero", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 876 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_nth", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 902 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "iter_skip_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 926 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "get_unwrap", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 959 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "string_extend_chars", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 988 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_cloned_collect", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1011 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "chars_last_cmp", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1035 + }, + "group": "clippy::style", + "docs": " **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.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", + "applicability": null + }, + { + "id": "useless_asref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1060 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_fold", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1082 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_filter_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1111 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "into_iter_on_ref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1135 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "suspicious_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1154 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "uninit_assumed_init", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1186 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_saturating_arithmetic", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1213 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "zst_offset", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1230 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "filetype_is_file", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1270 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_as_ref_deref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1295 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_next_slice", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1321 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "single_char_add_str", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1346 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_lazy_evaluations", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1382 + }, + "group": "clippy::style", + "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "map_collect_result_unit", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1403 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "from_iter_instead_of_collect", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1436 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "inspect_for_each", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1466 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "filter_map_identity", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1489 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "bytes_nth", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1511 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "min_max", + "id_span": { + "path": "clippy_lints/src/minmax.rs", + "line": 28 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant.\n\n **Why is this bad?** This is in all probability not the intended outcome. At\n the least it hurts readability of the code.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n min(0, max(100, x))\n ```\n or\n ```ignore\n x.max(100).min(0)\n ```\n It will always be equal to `0`. Probably the author meant to clamp the value\n between 0 and 100, but has erroneously swapped `min` and `max`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "toplevel_ref_arg", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 53 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for function arguments and let bindings denoted as `ref`.\n\n **Why is this bad?** The `ref` declaration makes the function take an owned\n value, but turns the argument into a reference (which means that the value\n is destroyed when exiting the function). This adds not much value: either\n take a reference type, or take an owned value and create references in the\n body.\n\n For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The\n type of `x` is more obvious with the former.\n\n **Known problems:** If the argument is dereferenced within the function,\n removing the `ref` will lead to errors. This can be fixed by removing the\n dereferences, e.g., changing `*x` to `x` within the function.\n\n **Example:**\n ```rust,ignore\n // Bad\n fn foo(ref x: u8) -> bool {\n true\n }\n\n // Good\n fn foo(x: &u8) -> bool {\n true\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "cmp_nan", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 76 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons to NaN.\n **Why is this bad?** NaN does not compare meaningfully to anything – not\n even itself – so those comparisons are simply wrong.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1.0;\n\n // Bad\n if x == f32::NAN { }\n\n // Good\n if x.is_nan() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "float_cmp", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 109 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1.2331f64;\n let y = 1.2332f64;\n\n // Bad\n if y == 1.23f64 { }\n if y != x {} // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (y - 1.23f64).abs() < error_margin { }\n if (y - x).abs() > error_margin { }\n ```\n", + "applicability": null + }, + { + "id": "cmp_owned", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 136 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for conversions to owned values just for the sake of a comparison.\n\n **Why is this bad?** The comparison can operate on a reference, so creating\n an owned value effectively throws it away directly afterwards, which is\n needlessly consuming code and heap space.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x.to_owned() == y {}\n ```\n Could be written as\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x == y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "modulo_one", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 159 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for getting the remainder of a division by one or minus one.\n\n **Why is this bad?** The result for a divisor of one can only ever be zero; for\n minus one it can cause panic/overflow (if the left operand is the minimal value of\n the respective integer type) or results in zero. No one will write such code\n deliberately, unless trying to win an Underhanded Rust Contest. Even for that\n contest, it's probably a bad idea. Use something more underhanded.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n let a = x % 1;\n let a = x % -1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "used_underscore_binding", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 181 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for the use of bindings with a single leading underscore.\n\n **Why is this bad?** A single leading underscore is usually used to indicate\n that a binding will not be used. Using such a binding breaks this\n expectation.\n\n **Known problems:** The lint does not work properly with desugaring and\n macro, it has been allowed in the mean time.\n\n **Example:**\n ```rust\n let _x = 0;\n let y = _x + 1; // Here we are using `_x`, even though it has a leading\n // underscore. We should rename `_x` to `x`\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "short_circuit_statement", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 201 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the use of short circuit boolean conditions as a\n statement.\n\n **Why is this bad?** Using a short circuit boolean condition as a statement\n may hide the fact that the second part is executed or not depending on the\n outcome of the first part.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n f() && g(); // We should write `if f() { g(); }`.\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "zero_ptr", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 223 + }, + "group": "clippy::style", + "docs": " **What it does:** Catch casts from `0` to some pointer type\n **Why is this bad?** This generally means `null` and is better expressed as\n {`std`, `core`}`::ptr::`{`null`, `null_mut`}.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let a = 0 as *const u32;\n\n // Good\n let a = std::ptr::null::();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "float_cmp_const", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 254 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point value and constant, except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: f64 = 1.0;\n const ONE: f64 = 1.00;\n\n // Bad\n if x == ONE { } // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (x - ONE).abs() < error_margin { }\n ```\n", + "applicability": null + }, + { + "id": "unneeded_field_pattern", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 44 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for structure field patterns bound to wildcards.\n **Why is this bad?** Using `..` instead is shorter and leaves the focus on\n the fields that are actually bound.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Foo {\n # a: i32,\n # b: i32,\n # c: i32,\n # }\n let f = Foo { a: 0, b: 0, c: 0 };\n\n // Bad\n match f {\n Foo { a: _, b: 0, .. } => {},\n Foo { a: _, b: _, c: _ } => {},\n }\n\n // Good\n match f {\n Foo { b: 0, .. } => {},\n Foo { .. } => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "duplicate_underscore_argument", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 65 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for function arguments having the similar names differing by an underscore.\n\n **Why is this bad?** It affects code readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo(a: i32, _a: i32) {}\n\n // Good\n fn bar(a: i32, _b: i32) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "double_neg", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 83 + }, + "group": "clippy::style", + "docs": " **What it does:** Detects expressions of the form `--x`.\n **Why is this bad?** It can mislead C/C++ programmers to think `x` was\n decremented.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut x = 3;\n --x;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mixed_case_hex_literals", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 104 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns on hexadecimal literals with mixed-case letter digits.\n\n **Why is this bad?** It looks confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 0x1a9BAcD;\n\n // Good\n let y = 0x1A9BACD;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unseparated_literal_suffix", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 125 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Warns if literal suffixes are not separated by an underscore.\n\n **Why is this bad?** It is much less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 123832i32;\n\n // Good\n let y = 123832_i32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "zero_prefixed_literal", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 163 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Warns if an integral constant literal starts with `0`.\n **Why is this bad?** In some languages (including the infamous C language\n and most of its\n family), this marks an octal constant. In Rust however, this is a decimal\n constant. This could\n be confusing for both the writer and a reader of the constant.\n\n **Known problems:** None.\n\n **Example:**\n\n In Rust:\n ```rust\n fn main() {\n let a = 0123;\n println!(\"{}\", a);\n }\n ```\n\n prints `123`, while in C:\n\n ```c\n #include \n\n int main() {\n int a = 0123;\n printf(\"%d\\n\", a);\n }\n ```\n\n prints `83` (as `83 == 0o123` while `123 == 0o173`).\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "builtin_type_shadow", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 184 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns if a generic shadows a built-in type.\n **Why is this bad?** This gives surprising type errors.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n impl Foo {\n fn impl_func(&self) -> u32 {\n 42\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_pattern", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 213 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for patterns in the form `name @ _`.\n **Why is this bad?** It's almost always more readable to just use direct\n bindings.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = Some(\"abc\");\n\n // Bad\n match v {\n Some(x) => (),\n y @ _ => (),\n }\n\n // Good\n match v {\n Some(x) => (),\n y => (),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unneeded_wildcard_pattern", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 247 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`).\n\n _NOTE_: While `_, ..` means there is at least one element left, `..`\n means there are 0 or more elements left. This can make a difference\n when refactoring, but shouldn't result in errors in the refactored code,\n since the wildcard pattern isn't used anyway.\n **Why is this bad?** The wildcard pattern is unneeded as the rest pattern\n can match that element as well.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct TupleStruct(u32, u32, u32);\n # let t = TupleStruct(1, 2, 3);\n // Bad\n match t {\n TupleStruct(0, .., _) => (),\n _ => (),\n }\n\n // Good\n match t {\n TupleStruct(0, ..) => (),\n _ => (),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "missing_const_for_fn", + "id_span": { + "path": "clippy_lints/src/missing_const_for_fn.rs", + "line": 72 + }, + "group": "clippy::nursery", + "docs": " **What it does:**\n Suggests the use of `const` in functions and methods where possible.\n\n **Why is this bad?**\n\n Not having the function const prevents callers of the function from being const as well.\n\n **Known problems:**\n\n Const functions are currently still being worked on, with some features only being available\n on nightly. This lint does not consider all edge cases currently and the suggestions may be\n incorrect if you are using this lint on stable.\n\n Also, the lint only runs one pass over the code. Consider these two non-const functions:\n\n ```rust\n fn a() -> i32 {\n 0\n }\n fn b() -> i32 {\n a()\n }\n ```\n\n When running Clippy, the lint will only suggest to make `a` const, because `b` at this time\n can't be const as it calls a non-const function. Making `a` const and running Clippy again,\n will suggest to make `b` const, too.\n\n **Example:**\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n\n Could be a const fn:\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n const fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_docs_in_private_items", + "id_span": { + "path": "clippy_lints/src/missing_doc.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns if there is missing doc for any documentable item (public or private).\n\n **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`\n allowed-by-default lint for\n public members, but has no way to enforce documentation of private items.\n This lint fixes that.\n\n **Known problems:** None.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_inline_in_public_items", + "id_span": { + "path": "clippy_lints/src/missing_inline.rs", + "line": 55 + }, + "group": "clippy::restriction", + "docs": " **What it does:** it lints if an exported function, method, trait method with default impl, or trait method impl is not `#[inline]`.\n\n **Why is this bad?** In general, it is not. Functions can be inlined across\n crates when that's profitable as long as any form of LTO is used. When LTO is disabled,\n functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates\n might intend for most of the methods in their public API to be able to be inlined across\n crates even when LTO is disabled. For these types of crates, enabling this lint might make\n sense. It allows the crate to require all exported methods to be `#[inline]` by default, and\n then opt out for specific methods where this might not make sense.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo() {} // missing #[inline]\n fn ok() {} // ok\n #[inline] pub fn bar() {} // ok\n #[inline(always)] pub fn baz() {} // ok\n\n pub trait Bar {\n fn bar(); // ok\n fn def_bar() {} // missing #[inline]\n }\n\n struct Baz;\n impl Baz {\n fn private() {} // ok\n }\n\n impl Bar for Baz {\n fn bar() {} // ok - Baz is not exported\n }\n\n pub struct PubBaz;\n impl PubBaz {\n fn private() {} // ok\n pub fn not_ptrivate() {} // missing #[inline]\n }\n\n impl Bar for PubBaz {\n fn bar() {} // missing #[inline]\n fn def_bar() {} // missing #[inline]\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "modulo_arithmetic", + "id_span": { + "path": "clippy_lints/src/modulo_arithmetic.rs", + "line": 26 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for modulo arithmetic.\n **Why is this bad?** The results of modulo (%) operation might differ\n depending on the language, when negative numbers are involved.\n If you interop with different languages it might be beneficial\n to double check all places that use modulo arithmetic.\n\n For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = -17 % 3;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "multiple_crate_versions", + "id_span": { + "path": "clippy_lints/src/multiple_crate_versions.rs", + "line": 32 + }, + "group": "clippy::cargo", + "docs": " **What it does:** Checks to see if multiple versions of a crate are being used.\n\n **Why is this bad?** This bloats the size of targets, and can lead to\n confusing error messages when structs or traits are used interchangeably\n between different versions of a crate.\n\n **Known problems:** Because this can be caused purely by the dependencies\n themselves, it's not always possible to fix this issue.\n\n **Example:**\n ```toml\n # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.\n [dependencies]\n ctrlc = \"=3.1.0\"\n ansi_term = \"=0.11.0\"\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mutable_key_type", + "id_span": { + "path": "clippy_lints/src/mut_key.rs", + "line": 50 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for sets/maps with mutable key types.\n **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and\n `BtreeSet` rely on either the hash or the order of keys be unchanging,\n so having types with interior mutability is a bad idea.\n\n **Known problems:** It's correct to use a struct, that contains interior mutability\n as a key, when its `Hash` implementation doesn't access any of the interior mutable types.\n However, this lint is unable to recognize this, so it causes a false positive in theses cases.\n The `bytes` crate is a great example of this.\n\n **Example:**\n ```rust\n use std::cmp::{PartialEq, Eq};\n use std::collections::HashSet;\n use std::hash::{Hash, Hasher};\n use std::sync::atomic::AtomicUsize;\n# #[allow(unused)]\n\n struct Bad(AtomicUsize);\n impl PartialEq for Bad {\n fn eq(&self, rhs: &Self) -> bool {\n ..\n ; unimplemented!();\n }\n }\n\n impl Eq for Bad {}\n\n impl Hash for Bad {\n fn hash(&self, h: &mut H) {\n ..\n ; unimplemented!();\n }\n }\n\n fn main() {\n let _: HashSet = HashSet::new();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_mut", + "id_span": { + "path": "clippy_lints/src/mut_mut.rs", + "line": 24 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for instances of `mut mut` references.\n **Why is this bad?** Multiple `mut`s don't add anything meaningful to the\n source. This is either a copy'n'paste error, or it shows a fundamental\n misunderstanding of references.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut y = 1;\n let x = &mut &mut y;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_mutex_lock", + "id_span": { + "path": "clippy_lints/src/mut_mutex_lock.rs", + "line": 40 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `&mut Mutex::lock` calls\n **Why is this bad?** `Mutex::lock` is less efficient than\n calling `Mutex::get_mut`. In addition you also have a statically\n guarantee that the mutex isn't locked, instead of just a runtime\n guarantee.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let mut value = value_mutex.lock().unwrap();\n *value += 1;\n ```\n Use instead:\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let value = value_mutex.get_mut().unwrap();\n *value += 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "unnecessary_mut_passed", + "id_span": { + "path": "clippy_lints/src/mut_reference.rs", + "line": 25 + }, + "group": "clippy::style", + "docs": " **What it does:** Detects passing a mutable reference to a function that only requires an immutable reference.\n\n **Why is this bad?** The mutable reference rules out all other references to\n the value. Also the code misleads about the intent of the call site.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n my_vec.push(&mut value)\n\n // Good\n my_vec.push(&value)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "debug_assert_with_mut_call", + "id_span": { + "path": "clippy_lints/src/mutable_debug_assertion.rs", + "line": 28 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for function/method calls with a mutable parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.\n\n **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the\n compiler.\n Therefore mutating something in a `debug_assert!` macro results in different behaviour\n between a release and debug build.\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n debug_assert_eq!(vec![3].pop(), Some(3));\n // or\n fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }\n debug_assert!(take_a_mut_parameter(&mut 5));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mutex_atomic", + "id_span": { + "path": "clippy_lints/src/mutex_atomic.rs", + "line": 34 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for usages of `Mutex` where an atomic will do.\n **Why is this bad?** Using a mutex just to make access to a plain bool or\n reference sequential is shooting flies with cannons.\n `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and\n faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # let y = true;\n\n // Bad\n # use std::sync::Mutex;\n let x = Mutex::new(&y);\n\n // Good\n # use std::sync::atomic::AtomicBool;\n let x = AtomicBool::new(y);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mutex_integer", + "id_span": { + "path": "clippy_lints/src/mutex_atomic.rs", + "line": 59 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for usages of `Mutex` where `X` is an integral type.\n\n **Why is this bad?** Using a mutex just to make access to a plain integer\n sequential is\n shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # use std::sync::Mutex;\n let x = Mutex::new(0usize);\n\n // Good\n # use std::sync::atomic::AtomicUsize;\n let x = AtomicUsize::new(0usize);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_arbitrary_self_type", + "id_span": { + "path": "clippy_lints/src/needless_arbitrary_self_type.rs", + "line": 55 + }, + "group": "clippy::complexity", + "docs": " **What it does:** The lint checks for `self` in fn parameters that specify the `Self`-type explicitly\n **Why is this bad?** Increases the amount and decreases the readability of code\n\n **Known problems:** None\n\n **Example:**\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self: Self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n\n Could be rewritten as\n\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_bool", + "id_span": { + "path": "clippy_lints/src/needless_bool.rs", + "line": 38 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expressions of the form `if c { true } else { false }` (or vice versa) and suggests using the condition directly.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** Maybe false positives: Sometimes, the two branches are\n painstakingly documented (which we, of course, do not detect), so they *may*\n have some value. Even then, the documentation can be rewritten to match the\n shorter code.\n\n **Example:**\n ```rust,ignore\n if x {\n false\n } else {\n true\n }\n ```\n Could be written as\n ```rust,ignore\n !x\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "bool_comparison", + "id_span": { + "path": "clippy_lints/src/needless_bool.rs", + "line": 62 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expressions of the form `x == true`, `x != true` and order comparisons such as `x < true` (or vice versa) and\n suggest using the variable directly.\n\n **Why is this bad?** Unnecessary code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x == true {}\n if y == false {}\n ```\n use `x` directly:\n ```rust,ignore\n if x {}\n if !y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_borrow", + "id_span": { + "path": "clippy_lints/src/needless_borrow.rs", + "line": 32 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for address of operations (`&`) that are going to be dereferenced immediately by the compiler.\n\n **Why is this bad?** Suggests that the receiver of the expression borrows\n the expression.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x: &i32 = &&&&&&5;\n\n // Good\n let x: &i32 = &5;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_borrowed_reference", + "id_span": { + "path": "clippy_lints/src/needless_borrowed_ref.rs", + "line": 48 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for useless borrowed references.\n **Why is this bad?** It is mostly useless and make the code look more\n complex than it\n actually is.\n\n **Known problems:** It seems that the `&ref` pattern is sometimes useful.\n For instance in the following snippet:\n ```rust,ignore\n enum Animal {\n Cat(u64),\n Dog(u64),\n }\n\n fn foo(a: &Animal, b: &Animal) {\n match (a, b) {\n (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error\n (&Animal::Dog(ref c), &Animal::Dog(_)) => ()\n }\n }\n ```\n There is a lifetime mismatch error for `k` (indeed a and b have distinct\n lifetime).\n This can be fixed by using the `&ref` pattern.\n However, the code can also be fixed by much cleaner ways\n\n **Example:**\n ```rust\n let mut v = Vec::::new();\n let _ = v.iter_mut().filter(|&ref a| a.is_empty());\n ```\n This closure takes a reference on something that has been matched as a\n reference and\n de-referenced.\n As such, it could just be |a| a.is_empty()\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_continue", + "id_span": { + "path": "clippy_lints/src/needless_continue.rs", + "line": 114 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** The lint checks for `if`-statements appearing in loops that contain a `continue` statement in either their main blocks or their\n `else`-blocks, when omitting the `else`-block possibly with some\n rearrangement of code can make the code easier to understand.\n\n **Why is this bad?** Having explicit `else` blocks for `if` statements\n containing `continue` in their THEN branch adds unnecessary branching and\n nesting to the code. Having an else block containing just `continue` can\n also be better written by grouping the statements following the whole `if`\n statement within the THEN block and omitting the else block completely.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n } else {\n continue;\n }\n println!(\"Hello, world\");\n }\n ```\n\n Could be rewritten as\n\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n println!(\"Hello, world\");\n }\n }\n ```\n\n As another example, the following code\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n } else {\n // Do something useful\n }\n # break;\n }\n ```\n Could be rewritten as\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n }\n // Do something useful\n # break;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_pass_by_value", + "id_span": { + "path": "clippy_lints/src/needless_pass_by_value.rs", + "line": 50 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions taking arguments by value, but not consuming them in its\n body.\n\n **Why is this bad?** Taking arguments by reference is more flexible and can\n sometimes avoid\n unnecessary allocations.\n\n **Known problems:**\n * This lint suggests taking an argument by reference,\n however sometimes it is better to let users decide the argument type\n (by using `Borrow` trait, for example), depending on how the function is used.\n\n **Example:**\n ```rust\n fn foo(v: Vec) {\n assert_eq!(v.len(), 42);\n }\n ```\n should be\n ```rust\n fn foo(v: &[i32]) {\n assert_eq!(v.len(), 42);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": null + } + }, + { + "id": "needless_question_mark", + "id_span": { + "path": "clippy_lints/src/needless_question_mark.rs", + "line": 57 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Suggests alternatives for useless applications of `?` in terminating expressions\n\n **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n Some(to.magic?)\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| Ok(t.magic?))\n }\n\n ```\n Use instead:\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n to.magic\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| t.magic)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_update", + "id_span": { + "path": "clippy_lints/src/needless_update.rs", + "line": 43 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for needlessly including a base struct on update when all fields are changed anyway.\n\n This lint is not applied to structs marked with\n [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).\n\n **Why is this bad?** This will cost resources (because the base has to be\n somewhere), and make the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Point {\n # x: i32,\n # y: i32,\n # z: i32,\n # }\n # let zero_point = Point { x: 0, y: 0, z: 0 };\n\n // Bad\n Point {\n x: 1,\n y: 1,\n z: 1,\n ..zero_point\n };\n\n // Ok\n Point {\n x: 1,\n y: 1,\n ..zero_point\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "neg_cmp_op_on_partial_ord", + "id_span": { + "path": "clippy_lints/src/neg_cmp_op_on_partial_ord.rs", + "line": 41 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the usage of negated comparison operators on types which only implement\n `PartialOrd` (e.g., `f64`).\n\n **Why is this bad?**\n These operators make it easy to forget that the underlying types actually allow not only three\n potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is\n especially easy to miss if the operator based comparison result is negated.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::cmp::Ordering;\n\n // Bad\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = !(a <= b);\n\n // Good\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = match a.partial_cmp(&b) {\n None | Some(Ordering::Greater) => true,\n _ => false,\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "neg_multiply", + "id_span": { + "path": "clippy_lints/src/neg_multiply.rs", + "line": 21 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for multiplication by -1 as a form of negation.\n **Why is this bad?** It's more readable to just negate.\n\n **Known problems:** This only catches integers (for now).\n\n **Example:**\n ```ignore\n x * -1\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "new_without_default", + "id_span": { + "path": "clippy_lints/src/new_without_default.rs", + "line": 48 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for types with a `fn new() -> Self` method and no implementation of\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).\n\n **Why is this bad?** The user might expect to be able to use\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the\n type can be constructed without arguments.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n\n ```ignore\n struct Foo(Bar);\n\n impl Foo {\n fn new() -> Self {\n Foo(Bar::new())\n }\n }\n ```\n\n To fix the lint, add a `Default` implementation that delegates to `new`:\n\n ```ignore\n struct Foo(Bar);\n\n impl Default for Foo {\n fn default() -> Self {\n Foo::new()\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "no_effect", + "id_span": { + "path": "clippy_lints/src/no_effect.rs", + "line": 22 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for statements which have no effect.\n **Why is this bad?** Similar to dead code, these statements are actually\n executed. However, as they have no effect, all they do is make the code less\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unnecessary_operation", + "id_span": { + "path": "clippy_lints/src/no_effect.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expression statements that can be reduced to a sub-expression.\n\n **Why is this bad?** Expressions by themselves often have no side-effects.\n Having such expressions reduces readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n compute_array()[0];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "declare_interior_mutable_const", + "id_span": { + "path": "clippy_lints/src/non_copy_const.rs", + "line": 69 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for declaration of `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` should better be replaced by a `static` item if a global\n variable is wanted, or replaced by a `const fn` if a constructor is wanted.\n\n **Known problems:** A \"non-constant\" const item is a legacy way to supply an\n initialized value to downstream `static` items (e.g., the\n `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,\n and this lint should be suppressed.\n\n Even though the lint avoids triggering on a constant whose type has enums that have variants\n with interior mutability, and its value uses non interior mutable variants (see\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);\n it complains about associated constants without default values only based on its types;\n which might not be preferable.\n There're other enums plus associated constants cases that the lint cannot handle.\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n\n // Bad.\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", + "applicability": null + }, + { + "id": "borrow_interior_mutable_const", + "id_span": { + "path": "clippy_lints/src/non_copy_const.rs", + "line": 110 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks if `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` value should be stored inside a `static` item.\n\n **Known problems:** When an enum has variants with interior mutability, use of its non\n interior mutable variants can generate false positives. See issue\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n\n // Bad.\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = CONST_ATOM;\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", + "applicability": null + }, + { + "id": "similar_names", + "id_span": { + "path": "clippy_lints/src/non_expressive_names.rs", + "line": 27 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for names that are very similar and thus confusing.\n **Why is this bad?** It's hard to distinguish between names that differ only\n by a single character.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let checked_exp = something;\n let checked_expr = something_else;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "many_single_char_names", + "id_span": { + "path": "clippy_lints/src/non_expressive_names.rs", + "line": 45 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for too many variables whose name consists of a single character.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let (a, b, c, d, e, f, g) = (...);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "just_underscores_and_digits", + "id_span": { + "path": "clippy_lints/src/non_expressive_names.rs", + "line": 65 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks if you have variables whose name consists of just underscores and digits.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```rust\n let _1 = 1;\n let ___1 = 1;\n let __1___2 = 11;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "nonsensical_open_options", + "id_span": { + "path": "clippy_lints/src/open_options.rs", + "line": 23 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for duplicate open options as well as combinations that make no sense.\n\n **Why is this bad?** In the best case, the code will be harder to read than\n necessary. I don't know the worst case.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::fs::OpenOptions;\n\n OpenOptions::new().read(true).truncate(true);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_env_unwrap", + "id_span": { + "path": "clippy_lints/src/option_env_unwrap.rs", + "line": 28 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for usage of `option_env!(...).unwrap()` and suggests usage of the `env!` macro.\n\n **Why is this bad?** Unwrapping the result of `option_env!` will panic\n at run-time if the environment variable doesn't exist, whereas `env!`\n catches it at compile-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n let _ = option_env!(\"HOME\").unwrap();\n ```\n\n Is better expressed as:\n\n ```rust,no_run\n let _ = env!(\"HOME\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_if_let_else", + "id_span": { + "path": "clippy_lints/src/option_if_let_else.rs", + "line": 59 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Lints usage of `if let Some(v) = ... { y } else { x }` which is more\n idiomatically done with `Option::map_or` (if the else bit is a pure\n expression) or `Option::map_or_else` (if the else bit is an impure\n expression).\n\n **Why is this bad?**\n Using the dedicated functions of the Option type is clearer and\n more concise than an `if let` expression.\n\n **Known problems:**\n This lint uses a deliberately conservative metric for checking\n if the inside of either body contains breaks or continues which will\n cause it to not suggest a fix if either block contains a loop with\n continues or breaks contained within the loop.\n\n **Example:**\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n 5\n };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n let y = do_complicated_function();\n y*y\n };\n ```\n\n should be\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = optional.map_or(5, |foo| foo);\n let _ = optional.map_or_else(||{\n let y = do_complicated_function();\n y*y\n }, |foo| foo);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "overflow_check_conditional", + "id_span": { + "path": "clippy_lints/src/overflow_check_conditional.rs", + "line": 21 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects classic underflow/overflow checks.\n **Why is this bad?** Most classic C underflow/overflow checks will fail in\n Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n a + b < a;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "panic_in_result_fn", + "id_span": { + "path": "clippy_lints/src/panic_in_result_fn.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.\n **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.\n\n **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.\n\n **Example:**\n\n ```rust\n fn result_with_panic() -> Result\n {\n panic!(\"error\");\n }\n ```\n Use instead:\n ```rust\n fn result_without_panic() -> Result {\n Err(String::from(\"error\"))\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "panic", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 19 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `panic!`.\n **Why is this bad?** `panic!` will stop the execution of the executable\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n panic!(\"even with a good reason\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unimplemented", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 35 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `unimplemented!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unimplemented!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "todo", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 51 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `todo!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n todo!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unreachable", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 67 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `unreachable!`.\n **Why is this bad?** This macro can cause code to panic\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unreachable!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "partialeq_ne_impl", + "id_span": { + "path": "clippy_lints/src/partialeq_ne_impl.rs", + "line": 27 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for manual re-implementations of `PartialEq::ne`.\n **Why is this bad?** `PartialEq::ne` is required to always return the\n negated result of `PartialEq::eq`, which is exactly what the default\n implementation does. Therefore, there should never be any need to\n re-implement it.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo;\n\n impl PartialEq for Foo {\n fn eq(&self, other: &Foo) -> bool { true }\n fn ne(&self, other: &Foo) -> bool { !(self == other) }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "trivially_copy_pass_by_ref", + "id_span": { + "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "line": 52 + }, + "group": "clippy::pedantic", + "docs": " **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\n pass by value.\n\n **Why is this bad?** In many calling conventions instances of structs will\n be passed through registers if they fit into two or less general purpose\n registers.\n\n **Known problems:** This lint is target register size dependent, it is\n limited to 32-bit to try and reduce portability problems between 32 and\n 64-bit, but if you are compiling for 8 or 16-bit targets then the limit\n will be different.\n\n The configuration option `trivial_copy_size_limit` can be set to override\n this limit for a project.\n\n This lint attempts to allow passing arguments by reference if a reference\n to that argument is returned. This is implemented by comparing the lifetime\n of the argument and return value for equality. However, this can cause\n false positives in cases involving multiple lifetimes that are bounded by\n each other.\n\n **Example:**\n\n ```rust\n // Bad\n fn foo(v: &u32) {}\n ```\n\n ```rust\n // Better\n fn foo(v: u32) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "large_types_passed_by_value", + "id_span": { + "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "line": 84 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions taking arguments by value, where the argument type is `Copy` and large enough to be worth considering\n passing by reference. Does not trigger if the function is being exported,\n because that might induce API breakage, if the parameter is declared as mutable,\n or if the argument is a `self`.\n\n **Why is this bad?** Arguments passed by value might result in an unnecessary\n shallow copy, taking up more space in the stack and requiring a call to\n `memcpy`, which can be expensive.\n\n **Example:**\n\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Bad\n fn foo(v: TooLarge) {}\n ```\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Good\n fn foo(v: &TooLarge) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "path_buf_push_overwrite", + "id_span": { + "path": "clippy_lints/src/path_buf_push_overwrite.rs", + "line": 36 + }, + "group": "clippy::nursery", + "docs": " **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) calls on `PathBuf` that can cause overwrites.\n\n **Why is this bad?** Calling `push` with a root path at the start can overwrite the\n previous defined path.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"/bar\");\n assert_eq!(x, PathBuf::from(\"/bar\"));\n ```\n Could be written:\n\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"bar\");\n assert_eq!(x, PathBuf::from(\"/foo/bar\"));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "pattern_type_mismatch", + "id_span": { + "path": "clippy_lints/src/pattern_type_mismatch.rs", + "line": 79 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for patterns that aren't exact representations of the types they are applied to.\n\n To satisfy this lint, you will have to adjust either the expression that is matched\n against or the pattern itself, as well as the bindings that are introduced by the\n adjusted patterns. For matching you will have to either dereference the expression\n with the `*` operator, or amend the patterns to explicitly match against `&`\n or `&mut ` depending on the reference mutability. For the bindings you need\n to use the inverse. You can leave them as plain bindings if you wish for the value\n to be copied, but you must use `ref mut ` or `ref ` to construct\n a reference into the matched structure.\n\n If you are looking for a way to learn about ownership semantics in more detail, it\n is recommended to look at IDE options available to you to highlight types, lifetimes\n and reference semantics in your code. The available tooling would expose these things\n in a general way even outside of the various pattern matching mechanics. Of course\n this lint can still be used to highlight areas of interest and ensure a good understanding\n of ownership semantics.\n\n **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable\n because it increases ownership hints in the code, and will guard against some changes\n in ownership.\n\n **Known problems:** None.\n\n **Example:**\n\n This example shows the basic adjustments necessary to satisfy the lint. Note how\n the matched expression is explicitly dereferenced with `*` and the `inner` variable\n is bound to a shared borrow via `ref inner`.\n\n ```rust,ignore\n // Bad\n let value = &Some(Box::new(23));\n match value {\n Some(inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n\n // Good\n let value = &Some(Box::new(23));\n match *value {\n Some(ref inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n ```\n\n The following example demonstrates one of the advantages of the more verbose style.\n Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable\n borrow, while `b` is simply taken by value. This ensures that the loop body cannot\n accidentally modify the wrong part of the structure.\n\n ```rust,ignore\n // Bad\n let mut values = vec![(2, 3), (3, 4)];\n for (a, b) in &mut values {\n *a += *b;\n }\n\n // Good\n let mut values = vec![(2, 3), (3, 4)];\n for &mut (ref mut a, b) in &mut values {\n *a += b;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "precedence", + "id_span": { + "path": "clippy_lints/src/precedence.rs", + "line": 44 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:\n * mixed usage of arithmetic and bit shifting/combining operators without\n parentheses\n * a \"negative\" numeric literal (which is really a unary `-` followed by a\n numeric literal)\n followed by a method call\n\n **Why is this bad?** Not everyone knows the precedence of those operators by\n heart, so expressions like these may trip others trying to reason about the\n code.\n\n **Known problems:** None.\n\n **Example:**\n * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7\n * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ptr_arg", + "id_span": { + "path": "clippy_lints/src/ptr.rs", + "line": 69 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless the references are mutable. It will also suggest you\n replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\n calls.\n\n **Why is this bad?** Requiring the argument to be of the specific size\n makes the function less useful for no benefit; slices in the form of `&[T]`\n or `&str` usually suffice and can be obtained from other types, too.\n\n **Known problems:** The lint does not follow data. So if you have an\n argument `x` and write `let y = x; y.clone()` the lint will not suggest\n changing that `.clone()` to `.to_owned()`.\n\n Other functions called from this function taking a `&String` or `&Vec`\n argument may also fail to compile if you change the argument. Applying\n this lint on them will fix the problem, but they may be in other crates.\n\n One notable example of a function that may cause issues, and which cannot\n easily be changed due to being in the standard library is `Vec::contains`.\n when called on a `Vec>`. If a `&Vec` is passed to that method then\n it will compile, but if a `&[T]` is passed then it will not compile.\n\n ```ignore\n fn cannot_take_a_slice(v: &Vec) -> bool {\n let vec_of_vecs: Vec> = some_other_fn();\n\n vec_of_vecs.contains(v)\n }\n ```\n\n Also there may be `fn(&Vec)`-typed references pointing to your function.\n If you have them, you will get a compiler error after applying this lint's\n suggestions. You then have the choice to undo your changes or change the\n type of the reference.\n\n Note that if the function is part of your public interface, there may be\n other crates referencing it, of which you may not be aware. Carefully\n deprecate the function before applying the lint suggestions in this case.\n\n **Example:**\n ```ignore\n // Bad\n fn foo(&Vec) { .. }\n\n // Good\n fn foo(&[u32]) { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "cmp_null", + "id_span": { + "path": "clippy_lints/src/ptr.rs", + "line": 95 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint checks for equality comparisons with `ptr::null`\n **Why is this bad?** It's easier and more readable to use the inherent\n `.is_null()`\n method instead\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n if x == ptr::null {\n ..\n }\n\n // Good\n if x.is_null() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_from_ref", + "id_span": { + "path": "clippy_lints/src/ptr.rs", + "line": 117 + }, + "group": "clippy::correctness", + "docs": " **What it does:** This lint checks for functions that take immutable references and return mutable ones.\n\n **Why is this bad?** This is trivially unsound, as one can create two\n mutable references from the same (immutable!) source.\n This [error](https://github.com/rust-lang/rust/issues/39465)\n actually lead to an interim Rust release 1.15.1.\n\n **Known problems:** To be on the conservative side, if there's at least one\n mutable reference with the output lifetime, this lint will not trigger.\n In practice, this case is unlikely anyway.\n\n **Example:**\n ```ignore\n fn foo(&Foo) -> &mut Bar { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ptr_eq", + "id_span": { + "path": "clippy_lints/src/ptr_eq.rs", + "line": 32 + }, + "group": "clippy::style", + "docs": " **What it does:** Use `std::ptr::eq` when applicable\n **Why is this bad?** `ptr::eq` can be used to compare `&T` references\n (which coerce to `*const T` implicitly) by their address rather than\n comparing the values they point to.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(a as *const _ as usize == b as *const _ as usize);\n ```\n Use instead:\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(std::ptr::eq(a, b));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ptr_offset_with_cast", + "id_span": { + "path": "clippy_lints/src/ptr_offset_with_cast.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an `isize`.\n\n **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric\n cast by using the `add` method instead.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.offset(offset as isize);\n }\n ```\n\n Could be written:\n\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.add(offset);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "question_mark", + "id_span": { + "path": "clippy_lints/src/question_mark.rs", + "line": 34 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for expressions that could be replaced by the question mark operator.\n **Why is this bad?** Question mark usage is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n if option.is_none() {\n return None;\n }\n ```\n\n Could be written:\n\n ```ignore\n option?;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "range_zip_with_len", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for zipping a collection with the range of `0.._.len()`.\n\n **Why is this bad?** The code is better expressed with `.enumerate()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = vec![1];\n x.iter().zip(0..x.len());\n ```\n Could be written as\n ```rust\n # let x = vec![1];\n x.iter().enumerate();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "range_plus_one", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 74 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for exclusive ranges where 1 is added to the upper bound, e.g., `x..(y+1)`.\n\n **Why is this bad?** The code is more readable with an inclusive range\n like `x..=y`.\n\n **Known problems:** Will add unnecessary pair of parentheses when the\n expression is not wrapped in a pair but starts with a opening parenthesis\n and ends with a closing one.\n I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.\n\n Also in many cases, inclusive ranges are still slower to run than\n exclusive ranges, because they essentially add an extra branch that\n LLVM may fail to hoist out of the loop.\n\n This will cause a warning that cannot be fixed if the consumer of the\n range only accepts a specific range type, instead of the generic\n `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..(y+1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..=y { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "range_minus_one", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 99 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., `x..=(y-1)`.\n\n **Why is this bad?** The code is more readable with an exclusive range\n like `x..y`.\n\n **Known problems:** This will cause a warning that cannot be fixed if\n the consumer of the range only accepts a specific range type, instead of\n the generic `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..=(y-1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..y { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "reversed_empty_ranges", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 132 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for range expressions `x..y` where both `x` and `y` are constant and `x` is greater or equal to `y`.\n\n **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.\n Moreover, trying to use a reversed range to index a slice will panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n fn main() {\n (10..=0).for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[3..1];\n }\n ```\n Use instead:\n ```rust\n fn main() {\n (0..=10).rev().for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[1..3];\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "manual_range_contains", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 159 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for expressions like `x >= 3 && x < 8` that could be more readably expressed as `(3..8).contains(x)`.\n\n **Why is this bad?** `contains` expresses the intent better and has less\n failure modes (such as fencepost errors or using `||` instead of `&&`).\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // given\n let x = 6;\n\n assert!(x >= 3 && x < 8);\n ```\n Use instead:\n ```rust\n# let x = 6;\n assert!((3..8).contains(&x));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_clone", + "id_span": { + "path": "clippy_lints/src/redundant_clone.rs", + "line": 63 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned value that is going to be dropped without further use.\n\n **Why is this bad?** It is not always possible for the compiler to eliminate useless\n allocations and deallocations generated by redundant `clone()`s.\n\n **Known problems:**\n\n False-negatives: analysis performed by this lint is conservative and limited.\n\n **Example:**\n ```rust\n # use std::path::Path;\n # #[derive(Clone)]\n # struct Foo;\n # impl Foo {\n # fn new() -> Self { Foo {} }\n # }\n # fn call(x: Foo) {}\n {\n let x = Foo::new();\n call(x.clone());\n call(x.clone()); // this can just pass `x`\n }\n\n [\"lorem\", \"ipsum\"].join(\" \").to_string();\n\n Path::new(\"/a/b\").join(\"c\").to_path_buf();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_closure_call", + "id_span": { + "path": "clippy_lints/src/redundant_closure_call.rs", + "line": 32 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects closures called in the same expression where they are defined.\n\n **Why is this bad?** It is unnecessarily adding to the expression's\n complexity.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = (|| 42)()\n\n // Good\n let a = 42\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_else", + "id_span": { + "path": "clippy_lints/src/redundant_else.rs", + "line": 37 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `else` blocks that can be removed without changing semantics.\n **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.\n\n **Known problems:** Some may prefer to keep the `else` block for clarity.\n\n **Example:**\n\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n } else {\n print!(\"Moving on...\");\n }\n }\n ```\n Use instead:\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n }\n print!(\"Moving on...\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_field_names", + "id_span": { + "path": "clippy_lints/src/redundant_field_names.rs", + "line": 34 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for fields in struct literals where shorthands could be used.\n\n **Why is this bad?** If the field and variable names are the same,\n the field name is redundant.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let bar: u8 = 123;\n\n struct Foo {\n bar: u8,\n }\n\n let foo = Foo { bar: bar };\n ```\n the last line can be simplified to\n ```ignore\n let foo = Foo { bar };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_pub_crate", + "id_span": { + "path": "clippy_lints/src/redundant_pub_crate.rs", + "line": 30 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they are inside a private module.\n\n **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent\n module's visibility.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n mod internal {\n pub(crate) fn internal_fn() { }\n }\n ```\n This function is not visible outside the module and it can be declared with `pub` or\n private visibility\n ```rust\n mod internal {\n pub fn internal_fn() { }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_slicing", + "id_span": { + "path": "clippy_lints/src/redundant_slicing.rs", + "line": 33 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for redundant slicing expressions which use the full range, and do not change the type.\n\n **Why is this bad?** It unnecessarily adds complexity to the expression.\n\n **Known problems:** If the type being sliced has an implementation of `Index`\n that actually changes anything then it can't be removed. However, this would be surprising\n to people reading the code and should have a note with it.\n\n **Example:**\n\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n &x[..]\n }\n ```\n Use instead:\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n x\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_static_lifetimes", + "id_span": { + "path": "clippy_lints/src/redundant_static_lifetimes.rs", + "line": 30 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for constants and statics with an explicit `'static` lifetime.\n **Why is this bad?** Adding `'static` to every reference can create very\n complicated types.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n ```\n This code can be rewritten as\n ```ignore\n const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ref_option_ref", + "id_span": { + "path": "clippy_lints/src/ref_option_ref.rs", + "line": 28 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `&Option<&T>`.\n **Why is this bad?** Since `&` is Copy, it's useless to have a\n reference on `Option<&T>`.\n\n **Known problems:** It may be irrelevant to use this lint on\n public API code as it will make a breaking change to apply it.\n\n **Example:**\n\n ```rust,ignore\n let x: &Option<&u32> = &Some(&0u32);\n ```\n Use instead:\n ```rust,ignore\n let x: Option<&u32> = Some(&0u32);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "deref_addrof", + "id_span": { + "path": "clippy_lints/src/reference.rs", + "line": 29 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `*&` and `*&mut` in expressions.\n **Why is this bad?** Immediately dereferencing a reference is no-op and\n makes the code less clear.\n\n **Known problems:** Multiple dereference/addrof pairs are not handled so\n the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = f(*&mut b);\n let c = *&d;\n\n // Good\n let a = f(b);\n let c = d;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ref_in_deref", + "id_span": { + "path": "clippy_lints/src/reference.rs", + "line": 120 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for references in expressions that use auto dereference.\n\n **Why is this bad?** The reference is a no-op and is automatically\n dereferenced by the compiler and makes the code less clear.\n\n **Example:**\n ```rust\n struct Point(u32, u32);\n let point = Point(30, 20);\n let x = (&point).0;\n ```\n Use instead:\n ```rust\n # struct Point(u32, u32);\n # let point = Point(30, 20);\n let x = point.0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "invalid_regex", + "id_span": { + "path": "clippy_lints/src/regex.rs", + "line": 25 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\n regex syntax.\n\n **Why is this bad?** This will lead to a runtime panic.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n Regex::new(\"|\")\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "trivial_regex", + "id_span": { + "path": "clippy_lints/src/regex.rs", + "line": 46 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).\n\n **Why is this bad?** Matching the regex can likely be replaced by `==` or\n `str::starts_with`, `str::ends_with` or `std::contains` or other `str`\n methods.\n\n **Known problems:** If the same regex is going to be applied to multiple\n inputs, the precomputations done by `Regex` construction can give\n significantly better performance than any of the `str`-based methods.\n\n **Example:**\n ```ignore\n Regex::new(\"^foobar\")\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "repeat_once", + "id_span": { + "path": "clippy_lints/src/repeat_once.rs", + "line": 33 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. - `.to_string()` for `str`\n - `.clone()` for `String`\n - `.to_vec()` for `slice`\n\n **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.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n let x = String::from(\"hello world\").repeat(1);\n }\n ```\n Use instead:\n ```rust\n fn main() {\n let x = String::from(\"hello world\").clone();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "let_and_return", + "id_span": { + "path": "clippy_lints/src/returns.rs", + "line": 38 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `let`-bindings, which are subsequently returned.\n\n **Why is this bad?** It is just extraneous code. Remove it to make your code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo() -> String {\n let x = String::new();\n x\n }\n ```\n instead, use\n ```\n fn foo() -> String {\n String::new()\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_return", + "id_span": { + "path": "clippy_lints/src/returns.rs", + "line": 63 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for return statements at the end of a block.\n **Why is this bad?** Removing the `return` and semicolon will make the code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n simplify to\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "self_assignment", + "id_span": { + "path": "clippy_lints/src/self_assignment.rs", + "line": 29 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for explicit self-assignments.\n **Why is this bad?** Self-assignments are redundant and unlikely to be\n intentional.\n\n **Known problems:** If expression contains any deref coercions or\n indexing operations they are assumed not to have any side effects.\n\n **Example:**\n\n ```rust\n struct Event {\n id: usize,\n x: i32,\n y: i32,\n }\n\n fn copy_position(a: &mut Event, b: &Event) {\n a.x = b.x;\n a.y = a.y;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "semicolon_if_nothing_returned", + "id_span": { + "path": "clippy_lints/src/semicolon_if_nothing_returned.rs", + "line": 30 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Looks for blocks of expressions and fires if the last expression returns `()` but is not followed by a semicolon.\n\n **Why is this bad?** The semicolon might be optional but when\n extending the block with new code, it doesn't require a change in previous last line.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n println!(\"Hello world\")\n }\n ```\n Use instead:\n ```rust\n fn main() {\n println!(\"Hello world\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "serde_api_misuse", + "id_span": { + "path": "clippy_lints/src/serde_api.rs", + "line": 16 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for mis-uses of the serde API.\n **Why is this bad?** Serde is very finnicky about how its API should be\n used, but the type system can't be used to enforce it (yet?).\n\n **Known problems:** None.\n\n **Example:** Implementing `Visitor::visit_string` but not\n `Visitor::visit_str`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "shadow_same", + "id_span": { + "path": "clippy_lints/src/shadow.rs", + "line": 34 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.\n\n **Why is this bad?** Not much, in fact it's a very common pattern in Rust\n code. Still, some may opt to avoid it in their code base, they can set this\n lint to `Warn`.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n # let x = 1;\n // Bad\n let x = &x;\n\n // Good\n let y = &x; // use different variable name\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "shadow_reuse", + "id_span": { + "path": "clippy_lints/src/shadow.rs", + "line": 61 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while reusing the original value.\n\n **Why is this bad?** Not too much, in fact it's a common pattern in Rust\n code. Still, some argue that name shadowing like this hurts readability,\n because a value may be bound to different things depending on position in\n the code.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n let x = 2;\n let x = x + 1;\n ```\n use different variable name:\n ```rust\n let x = 2;\n let y = x + 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "shadow_unrelated", + "id_span": { + "path": "clippy_lints/src/shadow.rs", + "line": 93 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use\n the original value.\n\n **Why is this bad?** Name shadowing can hurt readability, especially in\n large code bases, because it is easy to lose track of the active binding at\n any place in the code. This can be alleviated by either giving more specific\n names to bindings or introducing more scopes to contain the bindings.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns. Note that\n `allow`/`warn`/`deny`/`forbid` attributes only work on the function level\n for this lint.\n\n **Example:**\n ```rust\n # let y = 1;\n # let z = 2;\n let x = y;\n\n // Bad\n let x = z; // shadows the earlier binding\n\n // Good\n let w = z; // use different variable name\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "single_component_path_imports", + "id_span": { + "path": "clippy_lints/src/single_component_path_imports.rs", + "line": 32 + }, + "group": "clippy::style", + "docs": " **What it does:** Checking for imports with single component use path.\n **Why is this bad?** Import with single component use path such as `use cratename;`\n is not necessary, and thus should be removed.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use regex;\n\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n Better as\n ```rust,ignore\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "size_of_in_element_count", + "id_span": { + "path": "clippy_lints/src/size_of_in_element_count.rs", + "line": 31 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Detects expressions where `size_of::` or `size_of_val::` is used as a\n count of elements of type `T`\n\n **Why is this bad?** These functions expect a count\n of `T` and not a number of bytes\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::ptr::copy_nonoverlapping;\n # use std::mem::size_of;\n const SIZE: usize = 128;\n let x = [2u8; SIZE];\n let mut y = [2u8; SIZE];\n unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "slow_vector_initialization", + "id_span": { + "path": "clippy_lints/src/slow_vector_initialization.rs", + "line": 37 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks slow zero-filled vector initialization\n **Why is this bad?** These structures are non-idiomatic and less efficient than simply using\n `vec![0; len]`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use core::iter::repeat;\n # let len = 4;\n\n // Bad\n let mut vec1 = Vec::with_capacity(len);\n vec1.resize(len, 0);\n\n let mut vec2 = Vec::with_capacity(len);\n vec2.extend(repeat(0).take(len));\n\n // Good\n let mut vec1 = vec![0; len];\n let mut vec2 = vec![0; len];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "stable_sort_primitive", + "id_span": { + "path": "clippy_lints/src/stable_sort_primitive.rs", + "line": 36 + }, + "group": "clippy::perf", + "docs": " **What it does:** When sorting primitive values (integers, bools, chars, as well\n as arrays, slices, and tuples of such items), it is better to\n use an unstable sort than a stable sort.\n\n **Why is this bad?**\n Using a stable sort consumes more memory and cpu cycles. Because\n values which compare equal are identical, preserving their\n relative order (the guarantee that a stable sort provides) means\n nothing, while the extra costs still apply.\n\n **Known problems:**\n None\n\n **Example:**\n\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort();\n ```\n Use instead:\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort_unstable();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "string_add_assign", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 37 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for string appends of the form `x = x + y` (without `let`!).\n\n **Why is this bad?** It's not really bad, but some people think that the\n `.push_str(_)` method is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut x = \"Hello\".to_owned();\n x = x + \", World\";\n\n // More readable\n x += \", World\";\n x.push_str(\", World\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "string_add", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 65 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for all instances of `x + _` where `x` is of type `String`, but only if [`string_add_assign`](#string_add_assign) does *not*\n match.\n\n **Why is this bad?** It's not bad in and of itself. However, this particular\n `Add` implementation is asymmetric (the other operand need not be `String`,\n but `x` does), while addition as mathematically defined is symmetric, also\n the `String::push_str(_)` function is a perfectly good replacement.\n Therefore, some dislike it and wish not to have it in their code.\n\n That said, other people think that string addition, having a long tradition\n in other languages is actually fine, which is why we decided to make this\n particular lint `allow` by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = \"Hello\".to_owned();\n x + \", World\";\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "string_lit_as_bytes", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 107 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for the `as_bytes` method called on string literals that contain only ASCII characters.\n\n **Why is this bad?** Byte string literals (e.g., `b\"foo\"`) can be used\n instead. They are shorter but less discoverable than `as_bytes()`.\n\n **Known Problems:**\n `\"str\".as_bytes()` and the suggested replacement of `b\"str\"` are not\n equivalent because they have different types. The former is `&[u8]`\n while the latter is `&[u8; 3]`. That means in general they will have a\n different set of methods and different trait implementations.\n\n ```compile_fail\n fn f(v: Vec) {}\n\n f(\"...\".as_bytes().to_owned()); // works\n f(b\"...\".to_owned()); // does not work, because arg is [u8; 3] not Vec\n\n fn g(r: impl std::io::Read) {}\n\n g(\"...\".as_bytes()); // works\n g(b\"...\"); // does not work\n ```\n\n The actual equivalent of `\"str\".as_bytes()` with the same type is not\n `b\"str\"` but `&b\"str\"[..]`, which is a great deal of punctuation and not\n more readable than a function call.\n\n **Example:**\n ```rust\n // Bad\n let bs = \"a byte string\".as_bytes();\n\n // Good\n let bs = b\"a byte string\";\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "string_from_utf8_as_bytes", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 196 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Check if the string is transformed to byte array and casted back to string.\n **Why is this bad?** It's unnecessary, the string can be used directly.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = std::str::from_utf8(&\"Hello World!\".as_bytes()[6..11]).unwrap();\n ```\n could be written as\n ```rust\n let _ = &\"Hello World!\"[6..11];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "str_to_string", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 313 + }, + "group": "clippy::restriction", + "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better\n expressed with `.to_owned()`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let _ = \"str\".to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let _ = \"str\".to_owned();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "string_to_string", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 362 + }, + "group": "clippy::restriction", + "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `String`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let msg = String::from(\"Hello World\");\n let _ = msg.to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let msg = String::from(\"Hello World\");\n let _ = msg.clone();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_operation_groupings", + "id_span": { + "path": "clippy_lints/src/suspicious_operation_groupings.rs", + "line": 60 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for unlikely usages of binary operators that are almost\n certainly typos and/or copy/paste errors, given the other usages\n of binary operators nearby.\n **Why is this bad?**\n They are probably bugs and if they aren't then they look like bugs\n and you should add a comment explaining why you are doing such an\n odd set of operations.\n **Known problems:**\n There may be some false positives if you are trying to do something\n unusual that happens to look like a typo.\n\n **Example:**\n\n ```rust\n struct Vec3 {\n x: f64,\n y: f64,\n z: f64,\n }\n\n impl Eq for Vec3 {}\n\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // This should trigger the lint because `self.x` is compared to `other.y`\n self.x == other.y && self.y == other.y && self.z == other.z\n }\n }\n ```\n Use instead:\n ```rust\n # struct Vec3 {\n # x: f64,\n # y: f64,\n # z: f64,\n # }\n // same as above except:\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // Note we now compare other.x to self.x\n self.x == other.x && self.y == other.y && self.z == other.z\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_arithmetic_impl", + "id_span": { + "path": "clippy_lints/src/suspicious_trait_impl.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. subtracting elements in an Add impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl Add for Foo {\n type Output = Foo;\n\n fn add(self, other: Foo) -> Foo {\n Foo(self.0 - other.0)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_op_assign_impl", + "id_span": { + "path": "clippy_lints/src/suspicious_trait_impl.rs", + "line": 48 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. subtracting elements in an AddAssign impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl AddAssign for Foo {\n fn add_assign(&mut self, other: Foo) {\n *self = *self - other;\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_swap", + "id_span": { + "path": "clippy_lints/src/swap.rs", + "line": 36 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for manual swapping.\n **Why is this bad?** The `std::mem::swap` function exposes the intent better\n without deinitializing or copying either variable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut a = 42;\n let mut b = 1337;\n\n let t = b;\n b = a;\n a = t;\n ```\n Use std::mem::swap():\n ```rust\n let mut a = 1;\n let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "almost_swapped", + "id_span": { + "path": "clippy_lints/src/swap.rs", + "line": 61 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `foo = bar; bar = foo` sequences.\n **Why is this bad?** This looks like a failed attempt to swap.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n a = b;\n b = a;\n ```\n If swapping is intended, use `swap()` instead:\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "tabs_in_doc_comments", + "id_span": { + "path": "clippy_lints/src/tabs_in_doc_comments.rs", + "line": 54 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks doc comments for usage of tab characters.\n **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.\n To keep a consistent view on the source, also doc comments should not have tabs.\n Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the\n display settings of the author and reader differ.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n ///\n /// Struct to hold two strings:\n /// \t- first\t\tone\n /// \t- second\tone\n pub struct DoubleString {\n ///\n /// \t- First String:\n /// \t\t- needs to be inside here\n first_string: String,\n ///\n /// \t- Second String:\n /// \t\t- needs to be inside here\n second_string: String,\n}\n ```\n\n Will be converted to:\n ```rust\n ///\n /// Struct to hold two strings:\n /// - first one\n /// - second one\n pub struct DoubleString {\n ///\n /// - First String:\n /// - needs to be inside here\n first_string: String,\n ///\n /// - Second String:\n /// - needs to be inside here\n second_string: String,\n}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "temporary_assignment", + "id_span": { + "path": "clippy_lints/src/temporary_assignment.rs", + "line": 19 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for construction of a structure or tuple just to assign a value in it.\n\n **Why is this bad?** Readability. If the structure is only created to be\n updated, why not write the structure you want in the first place?\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n (0, 0).0 = 1\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "to_digit_is_some", + "id_span": { + "path": "clippy_lints/src/to_digit_is_some.rs", + "line": 27 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.\n **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's\n more straight forward to use the dedicated `is_digit` method.\n\n **Example:**\n ```rust\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.to_digit(radix).is_some();\n ```\n can be written as:\n ```\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.is_digit(radix);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "to_string_in_display", + "id_span": { + "path": "clippy_lints/src/to_string_in_display.rs", + "line": 40 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for uses of `to_string()` in `Display` traits.\n **Why is this bad?** Usually `to_string` is implemented indirectly\n via `Display`. Hence using it while implementing `Display` would\n lead to infinite recursion.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.to_string())\n }\n }\n\n ```\n Use instead:\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.0)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "type_repetition_in_bounds", + "id_span": { + "path": "clippy_lints/src/trait_bounds.rs", + "line": 28 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** This lint warns about unnecessary type repetitions in trait bounds\n **Why is this bad?** Repeating the type for every bound makes the code\n less readable than combining the bounds\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo(t: T) where T: Copy, T: Clone {}\n ```\n\n Could be written as:\n\n ```rust\n pub fn foo(t: T) where T: Copy + Clone {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "trait_duplication_in_bounds", + "id_span": { + "path": "clippy_lints/src/trait_bounds.rs", + "line": 57 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for cases where generics are being used and multiple syntax specifications for trait bounds are used simultaneously.\n\n **Why is this bad?** Duplicate bounds makes the code\n less readable than specifing them only once.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n\n Could be written as:\n\n ```rust\n fn func(arg: T) {}\n ```\n or\n\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "wrong_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 29 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "useless_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 48 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmutes_expressible_as_ptr_casts", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 73 + }, + "group": "clippy::complexity", + "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "crosspointer_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 91 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "transmute_ptr_to_ref", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 116 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_int_to_char", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 147 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_bytes_to_str", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 178 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_int_to_bool", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 200 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_int_to_float", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 222 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_float_to_int", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 244 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_ptr_to_ptr", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 271 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "unsound_collection_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 299 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "transmuting_null", + "id_span": { + "path": "clippy_lints/src/transmuting_null.rs", + "line": 23 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for transmute calls which would receive a null pointer.\n **Why is this bad?** Transmuting a null pointer is undefined behavior.\n\n **Known problems:** Not all cases can be detected at the moment of this writing.\n For example, variables which hold a null pointer and are then fed to a `transmute`\n call, aren't detectable yet.\n\n **Example:**\n ```rust\n let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "try_err", + "id_span": { + "path": "clippy_lints/src/try_err.rs", + "line": 43 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usages of `Err(x)?`.\n **Why is this bad?** The `?` operator is designed to allow calls that\n can fail to be easily chained. For example, `foo()?.bar()` or\n `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will\n always return), it is more clear to write `return Err(x)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n Err(\"failed\")?;\n }\n Ok(0)\n }\n ```\n Could be written:\n\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n return Err(\"failed\".into());\n }\n Ok(0)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "box_vec", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 66 + }, + "group": "clippy::perf", + "docs": " **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.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` it, you just add another level of indirection\n without any benefit whatsoever.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct X {\n values: Box>,\n }\n ```\n\n Better:\n\n ```rust,ignore\n struct X {\n values: Vec,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "vec_box", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 95 + }, + "group": "clippy::complexity", + "docs": " **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.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` its contents, you just add another level of indirection.\n\n **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),\n 1st comment).\n\n **Example:**\n ```rust\n struct X {\n values: Vec>,\n }\n ```\n\n Better:\n\n ```rust\n struct X {\n values: Vec,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "option_option", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 133 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for use of `Option>` in function signatures and type definitions\n\n **Why is this bad?** `Option<_>` represents an optional value. `Option>`\n represents an optional optional value which is logically the same thing as an optional\n value but has an unneeded extra level of wrapping.\n\n If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,\n consider a custom `enum` instead, with clear names for each case.\n\n **Known problems:** None.\n\n **Example**\n ```rust\n fn get_data() -> Option> {\n None\n }\n ```\n\n Better:\n\n ```rust\n pub enum Contents {\n Data(Vec), // Was Some(Some(Vec))\n NotYetFetched, // Was Some(None)\n None, // Was None\n }\n\n fn get_data() -> Contents {\n Contents::None\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "linkedlist", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 169 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`).\n\n **Why is this bad?** Gankro says:\n\n > The TL;DR of `LinkedList` is that it's built on a massive amount of\n pointers and indirection.\n > It wastes memory, it has terrible cache locality, and is all-around slow.\n `RingBuf`, while\n > \"only\" amortized for push/pop, should be faster in the general case for\n almost every possible\n > workload, and isn't even amortized at all if you can predict the capacity\n you need.\n >\n > `LinkedList`s are only really good if you're doing a lot of merging or\n splitting of lists.\n > This is because they can just mangle some pointers instead of actually\n copying the data. Even\n > if you're doing a lot of insertion in the middle of the list, `RingBuf`\n can still be better\n > because of how expensive it is to seek to the middle of a `LinkedList`.\n\n **Known problems:** False positives – the instances where using a\n `LinkedList` makes sense are few and far between, but they can still happen.\n\n **Example:**\n ```rust\n # use std::collections::LinkedList;\n let x: LinkedList = LinkedList::new();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "borrowed_box", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 193 + }, + "group": "clippy::complexity", + "docs": " **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.\n\n **Why is this bad?** Any `&Box` can also be a `&T`, which is more\n general.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn foo(bar: &Box) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(bar: &T) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "redundant_allocation", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 217 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for use of redundant allocations anywhere in the code.\n **Why is this bad?** Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Box<&T>`\n add an unnecessary level of indirection.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n fn foo(bar: Rc<&usize>) {}\n ```\n\n Better:\n\n ```rust\n fn foo(bar: &usize) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "rc_buffer", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 248 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`.\n **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since\n it is larger and involves an extra level of indirection, and doesn't implement `Borrow`.\n\n While mutating a buffer type would still be possible with `Rc::get_mut()`, it only\n works if there are no additional references yet, which usually defeats the purpose of\n enclosing it in a shared ownership type. Instead, additionally wrapping the inner\n type with an interior mutable container (such as `RefCell` or `Mutex`) would normally\n be used.\n\n **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for\n cases where mutation only happens before there are any additional references.\n\n **Example:**\n ```rust,ignore\n # use std::rc::Rc;\n fn foo(interned: Rc) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(interned: Rc) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "let_unit_value", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 768 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for binding a unit value.\n **Why is this bad?** A unit value cannot usefully be used anywhere. So\n binding one is kind of pointless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = {\n 1;\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unit_cmp", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 849 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons to unit. This includes all binary comparisons (like `==` and `<`) and asserts.\n\n **Why is this bad?** Unit is always equal to itself, and thus is just a\n clumsily written constant. Mostly this happens when someone accidentally\n adds semicolons at the end of the operands.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n if {\n foo();\n } == {\n bar();\n } {\n baz();\n }\n ```\n is equal to\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n {\n foo();\n bar();\n baz();\n }\n ```\n\n For asserts:\n ```rust\n # fn foo() {};\n # fn bar() {};\n assert_eq!({ foo(); }, { bar(); });\n ```\n will always succeed\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unit_arg", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 922 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for passing a unit value as an argument to a function without using a unit literal (`()`).\n\n **Why is this bad?** This is likely the result of an accidental semicolon.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n foo({\n let a = bar();\n baz(a);\n })\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "cast_precision_loss", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1158 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts from any numerical to a float type where the receiving type cannot store all values from the original type without\n rounding errors. This possible rounding is to be expected, so this lint is\n `Allow` by default.\n\n Basically, this warns on casting any integer with 32 or more bits to `f32`\n or any 64-bit integer to `f64`.\n\n **Why is this bad?** It's not bad at all. But in some applications it can be\n helpful to know where precision loss can take place. This lint can help find\n those places in the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = u64::MAX;\n x as f64;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_sign_loss", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1179 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts from a signed to an unsigned numerical type. In this case, negative values wrap around to large positive values,\n which can be quite surprising in practice. However, as the cast works as\n defined, this lint is `Allow` by default.\n\n **Why is this bad?** Possibly surprising results. You can activate this lint\n as a one-time check to see where numerical wrapping can arise.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let y: i8 = -1;\n y as u128; // will return 18446744073709551615\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_possible_truncation", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1201 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by\n default.\n\n **Why is this bad?** In some problem domains, it is good practice to avoid\n truncation. This lint can be activated to help assess where additional\n checks could be beneficial.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u8(x: u64) -> u8 {\n x as u8\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_possible_wrap", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1224 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts from an unsigned type to a signed type of the same size. Performing such a cast is a 'no-op' for the compiler,\n i.e., nothing is changed at the bit level, and the binary representation of\n the value is reinterpreted. This can cause wrapping if the value is too big\n for the target signed type. However, the cast works as defined, so this lint\n is `Allow` by default.\n\n **Why is this bad?** While such a cast is not bad in itself, the results can\n be surprising when this is not the intended behavior, as demonstrated by the\n example below.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n u32::MAX as i32; // will yield a value of `-1`\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_lossless", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1256 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts between numerical types that may be replaced by safe conversion functions.\n\n **Why is this bad?** Rust's `as` keyword will perform many kinds of\n conversions, including silently lossy conversions. Conversion functions such\n as `i32::from` will only perform lossless conversions. Using the conversion\n functions prevents conversions from turning into silent lossy conversions if\n the types of the input expressions ever change, and make it easier for\n people reading the code to know that the conversion is lossless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u64(x: u8) -> u64 {\n x as u64\n }\n ```\n\n Using `::from` would look like this:\n\n ```rust\n fn as_u64(x: u8) -> u64 {\n u64::from(x)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_cast", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1281 + }, + "group": "clippy::complexity", + "docs": " **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.\n\n **Why is this bad?** It's just unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = 2i32 as i32;\n let _ = 0.5 as f32;\n ```\n\n Better:\n\n ```rust\n let _ = 2_i32;\n let _ = 0.5_f32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "cast_ptr_alignment", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1305 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts, using `as` or `pointer::cast`, from a less-strictly-aligned pointer to a more-strictly-aligned pointer\n\n **Why is this bad?** Dereferencing the resulting pointer may be undefined\n behavior.\n\n **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar\n on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like\n u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.\n\n **Example:**\n ```rust\n let _ = (&1u8 as *const u8) as *const u16;\n let _ = (&mut 1u8 as *mut u8) as *mut u16;\n\n (&1u8 as *const u8).cast::();\n (&mut 1u8 as *mut u8).cast::();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fn_to_numeric_cast", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1332 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for casts of function pointers to something other than usize\n **Why is this bad?**\n Casting a function pointer to anything other than usize/isize is not portable across\n architectures, because you end up losing bits if the target type is too small or end up with a\n bunch of extra bits that waste space and add more instructions to the final binary than\n strictly necessary for the problem\n\n Casting to isize also doesn't make sense since there are no signed addresses.\n\n **Example**\n\n ```rust\n // Bad\n fn fun() -> i32 { 1 }\n let a = fun as i64;\n\n // Good\n fn fun2() -> i32 { 1 }\n let a = fun2 as usize;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "fn_to_numeric_cast_with_truncation", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1362 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to store address.\n\n **Why is this bad?**\n Such a cast discards some bits of the function's address. If this is intended, it would be more\n clearly expressed by casting to usize first, then casting the usize to the intended type (with\n a comment) to perform the truncation.\n\n **Example**\n\n ```rust\n // Bad\n fn fn1() -> i16 {\n 1\n };\n let _ = fn1 as i32;\n\n // Better: Cast to usize first, then comment with the reason for the truncation\n fn fn2() -> i16 {\n 1\n };\n let fn_ptr = fn2 as usize;\n let fn_ptr_truncated = fn_ptr as i32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "type_complexity", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1897 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for types used in structs, parameters and `let` declarations above a certain complexity threshold.\n\n **Why is this bad?** Too complex types make the code less readable. Consider\n using a `type` definition to simplify them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n struct Foo {\n inner: Rc>>>,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "char_lit_as_u8", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2068 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expressions where a character literal is cast to `u8` and suggests using a byte literal instead.\n\n **Why is this bad?** In general, casting values to smaller types is\n error-prone and should be avoided where possible. In the particular case of\n converting a character literal to u8, it is easy to avoid by just using a\n byte literal instead. As an added bonus, `b'a'` is even slightly shorter\n than `'a' as u8`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n 'x' as u8\n ```\n\n A better version, using the byte literal:\n\n ```rust,ignore\n b'x'\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "absurd_extreme_comparisons", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2133 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a\n case that is always true or always false. Only integer and boolean types are\n checked.\n\n **Why is this bad?** An expression like `min <= x` may misleadingly imply\n that it is possible for `x` to be less than the minimum. Expressions like\n `max < x` are probably mistakes.\n\n **Known problems:** For `usize` the size of the current compile target will\n be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such\n a comparison to detect target pointer width will trigger this lint. One can\n use `mem::sizeof` and compare its value or conditional compilation\n attributes\n like `#[cfg(target_pointer_width = \"64\")] ..` instead.\n\n **Example:**\n\n ```rust\n let vec: Vec = Vec::new();\n if vec.len() <= 0 {}\n if 100 > i32::MAX {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "invalid_upcast_comparisons", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2294 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for comparisons where the relation is always either true or false, but where one side has been upcast so that the comparison is\n necessary. Only integer types are checked.\n\n **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`\n will mistakenly imply that it is possible for `x` to be outside the range of\n `u8`.\n\n **Known problems:**\n https://github.com/rust-lang/rust-clippy/issues/886\n\n **Example:**\n ```rust\n let x: u8 = 1;\n (x as u32) > 300;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "implicit_hasher", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2515 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for public `impl` or `fn` missing generalization over different hashers and implicitly defaulting to the default hashing\n algorithm (`SipHash`).\n\n **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be\n used with them.\n\n **Known problems:** Suggestions for replacing constructors can contain\n false-positives. Also applying suggestions can require modification of other\n pieces of code, possibly including external crates.\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n could be rewritten as\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_ref_to_mut", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2866 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.\n **Why is this bad?** It’s basically guaranteed to be undefined behaviour.\n `UnsafeCell` is the only way to obtain aliasable data that is considered\n mutable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn x(r: &i32) {\n unsafe {\n *(r as *const _ as *mut _) += 1;\n }\n }\n ```\n\n Instead consider using interior mutability types.\n\n ```rust\n use std::cell::UnsafeCell;\n\n fn x(r: &UnsafeCell) {\n unsafe {\n *r.get() += 1;\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ptr_as_ptr", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2922 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `as` casts between raw pointers without changing its mutability,\n namely `*const T` to `*const U` and `*mut T` to `*mut U`.\n\n **Why is this bad?**\n Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because\n it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr as *const i32;\n let _ = mut_ptr as *mut i32;\n ```\n Use instead:\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr.cast::();\n let _ = mut_ptr.cast::();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "undropped_manually_drops", + "id_span": { + "path": "clippy_lints/src/undropped_manually_drops.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.\n **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.\n\n **Known problems:** Does not catch cases if the user binds `std::mem::drop`\n to a different name and calls it that way.\n\n **Example:**\n\n ```rust\n struct S;\n drop(std::mem::ManuallyDrop::new(S));\n ```\n Use instead:\n ```rust\n struct S;\n unsafe {\n std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "invisible_characters", + "id_span": { + "path": "clippy_lints/src/unicode.rs", + "line": 20 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for invisible Unicode characters in the code.\n **Why is this bad?** Having an invisible character in the code makes for all\n sorts of April fools, but otherwise is very much frowned upon.\n\n **Known problems:** None.\n\n **Example:** You don't see it, but there may be a zero-width space or soft hyphen\n some­where in this text.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "non_ascii_literal", + "id_span": { + "path": "clippy_lints/src/unicode.rs", + "line": 44 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for non-ASCII characters in string literals.\n **Why is this bad?** Yeah, we know, the 90's called and wanted their charset\n back. Even so, there still are editors and other programs out there that\n don't work well with Unicode. So if the code is meant to be used\n internationally, on multiple operating systems, or has other portability\n requirements, activating this lint could be useful.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = String::from(\"€\");\n ```\n Could be written as:\n ```rust\n let x = String::from(\"\\u{20ac}\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unicode_not_nfc", + "id_span": { + "path": "clippy_lints/src/unicode.rs", + "line": 61 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for string literals that contain Unicode in a form that is not equal to its\n [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).\n\n **Why is this bad?** If such a string is compared to another, the results\n may be surprising.\n\n **Known problems** None.\n\n **Example:** You may not see it, but \"à\"\" and \"à\"\" aren't the same string. The\n former when escaped is actually `\"a\\u{300}\"` while the latter is `\"\\u{e0}\"`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unit_return_expecting_ord", + "id_span": { + "path": "clippy_lints/src/unit_return_expecting_ord.rs", + "line": 30 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for functions that expect closures of type Fn(...) -> Ord where the implemented closure returns the unit type.\n The lint also suggests to remove the semi-colon at the end of the statement if present.\n\n **Why is this bad?** Likely, returning the unit type is unintentional, and\n could simply be caused by an extra semi-colon. Since () implements Ord\n it doesn't cause a compilation error.\n This is the same reasoning behind the unit_cmp lint.\n\n **Known problems:** If returning unit is intentional, then there is no\n way of specifying this without triggering needless_return lint\n\n **Example:**\n\n ```rust\n let mut twins = vec!((1, 1), (2, 2));\n twins.sort_by_key(|x| { x.1; });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fn_address_comparisons", + "id_span": { + "path": "clippy_lints/src/unnamed_address.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons with an address of a function item.\n **Why is this bad?** Function item address is not guaranteed to be unique and could vary\n between different code generation units. Furthermore different function items could have\n the same address after being merged together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n type F = fn();\n fn a() {}\n let f: F = a;\n if f == a {\n // ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "vtable_address_comparisons", + "id_span": { + "path": "clippy_lints/src/unnamed_address.rs", + "line": 51 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons with an address of a trait vtable.\n **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which\n are not guaranteed to be unique and could vary between different code generation units.\n Furthermore vtables for different types could have the same address after being merged\n together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n let a: Rc = ...\n let b: Rc = ...\n if Rc::ptr_eq(&a, &b) {\n ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unnecessary_sort_by", + "id_span": { + "path": "clippy_lints/src/unnecessary_sort_by.rs", + "line": 41 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects uses of `Vec::sort_by` passing in a closure\n which compares the two arguments, either directly or indirectly.\n\n **Why is this bad?**\n It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if\n possible) than to use `Vec::sort_by` and a more complicated\n closure.\n\n **Known problems:**\n If the suggested `Vec::sort_by_key` uses Reverse and it isn't already\n imported by a use statement, then it will need to be added manually.\n\n **Example:**\n\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by(|a, b| a.foo().cmp(&b.foo()));\n ```\n Use instead:\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by_key(|a| a.foo());\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_wraps", + "id_span": { + "path": "clippy_lints/src/unnecessary_wraps.rs", + "line": 50 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for private functions that only return `Ok` or `Some`.\n **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.\n\n **Known problems:** There can be false positives if the function signature is designed to\n fit some external requirement.\n\n **Example:**\n\n ```rust\n fn get_cool_number(a: bool, b: bool) -> Option {\n if a && b {\n return Some(50);\n }\n if a {\n Some(0)\n } else {\n Some(10)\n }\n }\n ```\n Use instead:\n ```rust\n fn get_cool_number(a: bool, b: bool) -> i32 {\n if a && b {\n return 50;\n }\n if a {\n 0\n } else {\n 10\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "unnested_or_patterns", + "id_span": { + "path": "clippy_lints/src/unnested_or_patterns.rs", + "line": 47 + }, + "group": "clippy::pedantic", + "docs": " **What it does:**\n Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\n suggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\n Another way to think of this is that it rewrites patterns in\n *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.\n\n **Why is this bad?**\n\n In the example above, `Some` is repeated, which unncessarily complicates the pattern.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n if let Some(0) | Some(2) = Some(0) {}\n }\n ```\n Use instead:\n ```rust\n #![feature(or_patterns)]\n\n fn main() {\n if let Some(0 | 2) = Some(0) {}\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unsafe_removed_from_name", + "id_span": { + "path": "clippy_lints/src/unsafe_removed_from_name.rs", + "line": 24 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for imports that remove \"unsafe\" from an item's name.\n\n **Why is this bad?** Renaming makes it less clear which traits and\n structures are unsafe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n use std::cell::{UnsafeCell as TotallySafeCell};\n\n extern crate crossbeam;\n use crossbeam::{spawn_unsafe as spawn};\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unused_io_amount", + "id_span": { + "path": "clippy_lints/src/unused_io_amount.rs", + "line": 28 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for unused written/read amount.\n **Why is this bad?** `io::Write::write(_vectored)` and\n `io::Read::read(_vectored)` are not guaranteed to\n process the entire buffer. They return how many bytes were processed, which\n might be smaller\n than a given buffer's length. If you don't need to deal with\n partial-write/read, use\n `write_all`/`read_exact` instead.\n\n **Known problems:** Detects only common patterns.\n\n **Example:**\n ```rust,ignore\n use std::io;\n fn foo(w: &mut W) -> io::Result<()> {\n // must be `w.write_all(b\"foo\")?;`\n w.write(b\"foo\")?;\n Ok(())\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unused_self", + "id_span": { + "path": "clippy_lints/src/unused_self.rs", + "line": 33 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks methods that contain a `self` argument but don't use it\n **Why is this bad?** It may be clearer to define the method as an associated function instead\n of an instance method if it doesn't require `self`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct A;\n impl A {\n fn method(&self) {}\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n struct A;\n impl A {\n fn method() {}\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unused_unit", + "id_span": { + "path": "clippy_lints/src/unused_unit.rs", + "line": 27 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for unit (`()`) expressions that can be removed.\n **Why is this bad?** Such expressions add no value, but can make the code\n less readable. Depending on formatting they can make a `break` or `return`\n statement look like a function call.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn return_unit() -> () {\n ()\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_unwrap", + "id_span": { + "path": "clippy_lints/src/unwrap.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.\n **Why is this bad?** Using `if let` or `match` is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_some() {\n do_something_with(option.unwrap())\n }\n ```\n\n Could be written:\n\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if let Some(value) = option {\n do_something_with(value)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "panicking_unwrap", + "id_span": { + "path": "clippy_lints/src/unwrap.rs", + "line": 63 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.\n **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.\n\n **Known problems:** This lint only checks `if` conditions not assignments.\n So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_none() {\n do_something_with(option.unwrap())\n }\n ```\n\n This code will always panic. The if condition should probably be inverted.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unwrap_in_result", + "id_span": { + "path": "clippy_lints/src/unwrap_in_result.rs", + "line": 47 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`\n **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.\n\n **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.\n\n **Example:**\n Before:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .expect(\"cannot divide the input by three\");\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n\n After:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .map_err(|e| format!(\"cannot divide the input by three: {}\", e))?;\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "upper_case_acronyms", + "id_span": { + "path": "clippy_lints/src/upper_case_acronyms.rs", + "line": 35 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for fully capitalized names and optionally names containing a capitalized acronym.\n **Why is this bad?** In CamelCase, acronyms count as one word.\n See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)\n for more.\n\n By default, the lint only triggers on fully-capitalized names.\n You can use the `upper-case-acronyms-aggressive: true` config option to enable linting\n on all camel case names\n\n **Known problems:** When two acronyms are contiguous, the lint can't tell where\n the first acronym ends and the second starts, so it suggests to lowercase all of\n the letters in the second acronym.\n\n **Example:**\n\n ```rust\n struct HTTPResponse;\n ```\n Use instead:\n ```rust\n struct HttpResponse;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "use_self", + "id_span": { + "path": "clippy_lints/src/use_self.rs", + "line": 53 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for unnecessary repetition of structure name when a replacement with `Self` is applicable.\n\n **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct\n name\n feels inconsistent.\n\n **Known problems:**\n - Unaddressed false negative in fn bodies of trait implementations\n - False positive with assotiated types in traits (#4140)\n\n **Example:**\n\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Foo {\n Foo {}\n }\n }\n ```\n could be\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Self {\n Self {}\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_conversion", + "id_span": { + "path": "clippy_lints/src/useless_conversion.rs", + "line": 32 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls which uselessly convert to the same type.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n // format!() returns a `String`\n let s: String = format!(\"hello\").into();\n\n // Good\n let s: String = format!(\"hello\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_vec", + "id_span": { + "path": "clippy_lints/src/vec.rs", + "line": 36 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would be possible.\n\n **Why is this bad?** This is less efficient.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(my_vec: &[u8]) {}\n\n // Bad\n foo(&vec![1, 2]);\n\n // Good\n foo(&[1, 2]);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "vec_init_then_push", + "id_span": { + "path": "clippy_lints/src/vec_init_then_push.rs", + "line": 32 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.\n **Why is this bad?** The `vec![]` macro is both more performant and easier to read than\n multiple `push` calls.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut v = Vec::new();\n v.push(0);\n ```\n Use instead:\n ```rust\n let v = vec![0];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "vec_resize_to_zero", + "id_span": { + "path": "clippy_lints/src/vec_resize_to_zero.rs", + "line": 24 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Finds occurrences of `Vec::resize(0, an_int)`\n **Why is this bad?** This is probably an argument inversion mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n vec!(1, 2, 3, 4, 5).resize(0, 5)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "verbose_file_reads", + "id_span": { + "path": "clippy_lints/src/verbose_file_reads.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for use of File::read_to_end and File::read_to_string.\n **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.\n 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)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # use std::io::Read;\n # use std::fs::File;\n let mut f = File::open(\"foo.txt\").unwrap();\n let mut bytes = Vec::new();\n f.read_to_end(&mut bytes).unwrap();\n ```\n Can be written more concisely as\n ```rust,no_run\n # use std::fs;\n let mut bytes = fs::read(\"foo.txt\").unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "wildcard_dependencies", + "id_span": { + "path": "clippy_lints/src/wildcard_dependencies.rs", + "line": 24 + }, + "group": "clippy::cargo", + "docs": " **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.\n **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),\n it is highly unlikely that you work with any possible version of your dependency,\n and wildcard dependencies would cause unnecessary breakage in the ecosystem.\n\n **Known problems:** None.\n\n **Example:**\n\n ```toml\n [dependencies]\n regex = \"*\"\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "enum_glob_use", + "id_span": { + "path": "clippy_lints/src/wildcard_imports.rs", + "line": 32 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", + "applicability": null + }, + { + "id": "wildcard_imports", + "id_span": { + "path": "clippy_lints/src/wildcard_imports.rs", + "line": 83 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", + "applicability": null + }, + { + "id": "println_empty_string", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 33 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `println!(\"\")` to print a newline.\n\n **Why is this bad?** You should use `println!()`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n println!(\"\");\n\n // Good\n println!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "print_with_newline", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 57 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `print!()` with a format string that ends in a newline.\n\n **Why is this bad?** You should use `println!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"World\";\n print!(\"Hello {}!\\n\", name);\n ```\n use println!() instead\n ```rust\n # let name = \"World\";\n println!(\"Hello {}!\", name);\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "print_stdout", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 75 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for printing on *stdout*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stdout* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `print!` and `println!` calls.\n\n **Example:**\n ```rust\n println!(\"Hello world!\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "print_stderr", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 93 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for printing on *stderr*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stderr* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `eprint!` and `eprintln!` calls.\n\n **Example:**\n ```rust\n eprintln!(\"Hello world!\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "use_debug", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 110 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for use of `Debug` formatting. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** The purpose of the `Debug` trait is to facilitate\n debugging Rust code. It should not be used in user-facing output.\n\n **Example:**\n ```rust\n # let foo = \"bar\";\n println!(\"{:?}\", foo);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "print_literal", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 133 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", + "applicability": null + }, + { + "id": "writeln_empty_string", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 156 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `writeln!(buf, \"\")` to print a newline.\n\n **Why is this bad?** You should use `writeln!(buf)`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"\");\n\n // Good\n writeln!(buf);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "write_with_newline", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 182 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `write!()` with a format string that\n ends in a newline.\n\n **Why is this bad?** You should use `writeln!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n # let name = \"World\";\n // Bad\n write!(buf, \"Hello {}!\\n\", name);\n\n // Good\n writeln!(buf, \"Hello {}!\", name);\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "write_literal", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 207 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", + "applicability": null + }, + { + "id": "zero_divided_by_zero", + "id_span": { + "path": "clippy_lints/src/zero_div_zero.rs", + "line": 23 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `0.0 / 0.0`.\n **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let nan = 0.0f32 / 0.0;\n\n // Good\n let nan = f32::NAN;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "zero_sized_map_values", + "id_span": { + "path": "clippy_lints/src/zero_sized_map_values.rs", + "line": 37 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for maps with zero-sized value types anywhere in the code.\n **Why is this bad?** Since there is only a single value for a zero-sized type, a map\n containing zero sized values is effectively a set. Using a set in that case improves\n readability and communicates intent more clearly.\n\n **Known problems:**\n * A zero-sized type cannot be recovered later if it contains private fields.\n * This lints the signature of public items\n\n **Example:**\n\n ```rust\n # use std::collections::HashMap;\n fn unique_words(text: &str) -> HashMap<&str, ()> {\n todo!();\n }\n ```\n Use instead:\n ```rust\n # use std::collections::HashSet;\n fn unique_words(text: &str) -> HashSet<&str> {\n todo!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + } +] -- cgit 1.4.1-3-g733a5 From 35844d0a48e0a644072ae81857782a6e0bd54740 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 6 Mar 2021 01:11:41 +0100 Subject: Metadata collection: Resolve lint from locals --- .../src/utils/internal_lints/metadata_collector.rs | 82 +- clippy_utils/src/paths.rs | 2 +- metadata_collection.json | 1184 +++++++++++--------- 3 files changed, 699 insertions(+), 569 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b2cc6372277..0425689b0dd 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -15,8 +15,6 @@ // # Applicability // - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-28: 1x reference to closure -// - See clippy_lints/src/needless_pass_by_value.rs@NeedlessPassByValue::check_fn // - TODO xFrednet 2021-02-28: 4x weird emission forwarding // - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name // - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from @@ -26,14 +24,12 @@ // - See clippy_lints/src/misc.rs@check_binary // - TODO xFrednet 2021-02-28: 2x lint from local from method call // - See clippy_lints/src/non_copy_const.rs@lint -// - TODO xFrednet 2021-02-28: 20x lint from local -// - See clippy_lints/src/map_unit_fn.rs@lint_map_unit_fn // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath}; +use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, def::DefKind}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -49,7 +45,7 @@ use crate::utils::{ }; /// This is the output file of the lint collector. -const OUTPUT_FILE: &str = "metadata_collection.json"; +const OUTPUT_FILE: &str = "../metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like @@ -270,12 +266,16 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ``` fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { if let Some(args) = match_lint_emission(cx, expr) { - if let Some((lint_name, applicability, is_multi_part)) = extract_complex_emission_info(cx, args) { + let mut emission_info = extract_complex_emission_info(cx, args); + if emission_info.is_empty() { + lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + return; + } + + for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; app_info.is_multi_suggestion = is_multi_part; - } else { - lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); } } } @@ -380,8 +380,8 @@ fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) fn extract_complex_emission_info<'hir>( cx: &LateContext<'hir>, args: &'hir [hir::Expr<'hir>], -) -> Option<(String, Option, bool)> { - let mut lint_name = None; +) -> Vec<(String, Option, bool)> { + let mut lints= Vec::new(); let mut applicability = None; let mut multi_part = false; @@ -390,9 +390,8 @@ fn extract_complex_emission_info<'hir>( if match_type(cx, arg_ty, &paths::LINT) { // If we found the lint arg, extract the lint name - if let ExprKind::Path(ref lint_path) = arg.kind { - lint_name = Some(last_path_segment(lint_path).ident.name); - } + let mut resolved_lints = resolve_lints(cx, arg); + lints.append(&mut resolved_lints); } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { applicability = resolve_applicability(cx, arg); } else if arg_ty.is_closure() { @@ -402,7 +401,14 @@ fn extract_complex_emission_info<'hir>( } } - lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_part)) + lints.drain(..).map(|lint_name| (lint_name, applicability.clone(), multi_part)).collect() +} + +/// Resolves the possible lints that this expression could reference +fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec { + let mut resolver = LintResolver::new(cx); + resolver.visit_expr(expr); + resolver.lints } /// This function tries to resolve the linked applicability to the given expression. @@ -426,6 +432,50 @@ fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hi false } +struct LintResolver<'a, 'hir> { + cx: &'a LateContext<'hir>, + lints: Vec, +} + +impl<'a, 'hir> LintResolver<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>) -> Self { + Self { + cx, + lints: Vec::::default(), + } + } +} + +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if_chain! { + if let ExprKind::Path(qpath) = &expr.kind; + if let QPath::Resolved(_, path) = qpath; + + let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr)); + if match_type(self.cx, expr_ty, &paths::LINT); + then { + if let hir::def::Res::Def(DefKind::Static, _) = path.res { + let lint_name = last_path_segment(qpath).ident.name; + self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); + } else if let Some(local) = get_parent_local(self.cx, expr) { + if let Some(local_init) = local.init { + intravisit::walk_expr(self, local_init); + } + } + } + } + + intravisit::walk_expr(self, expr); + } +} + /// This visitor finds the highest applicability value in the visited expressions struct ApplicabilityResolver<'a, 'hir> { cx: &'a LateContext<'hir>, @@ -488,7 +538,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } } -/// This returns the parent local node if the expression is a reference to +/// This returns the parent local node if the expression is a reference one fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { if let hir::def::Res::Local(local_hir) = path.res { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a6292b87768..1f19724ad25 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -83,7 +83,7 @@ pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; -#[cfg(feature = "internal-lints")] +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; diff --git a/metadata_collection.json b/metadata_collection.json index 507752a782c..f6f72ffa5fa 100644 --- a/metadata_collection.json +++ b/metadata_collection.json @@ -2,7 +2,7 @@ { "id": "approx_constant", "id_span": { - "path": "clippy_lints/src/approx_const.rs", + "path": "src/approx_const.rs", "line": 34 }, "group": "clippy::correctness", @@ -15,7 +15,7 @@ { "id": "integer_arithmetic", "id_span": { - "path": "clippy_lints/src/arithmetic.rs", + "path": "src/arithmetic.rs", "line": 28 }, "group": "clippy::restriction", @@ -28,7 +28,7 @@ { "id": "float_arithmetic", "id_span": { - "path": "clippy_lints/src/arithmetic.rs", + "path": "src/arithmetic.rs", "line": 46 }, "group": "clippy::restriction", @@ -41,7 +41,7 @@ { "id": "as_conversions", "id_span": { - "path": "clippy_lints/src/as_conversions.rs", + "path": "src/as_conversions.rs", "line": 42 }, "group": "clippy::restriction", @@ -54,7 +54,7 @@ { "id": "inline_asm_x86_intel_syntax", "id_span": { - "path": "clippy_lints/src/asm_syntax.rs", + "path": "src/asm_syntax.rs", "line": 78 }, "group": "clippy::restriction", @@ -64,7 +64,7 @@ { "id": "inline_asm_x86_att_syntax", "id_span": { - "path": "clippy_lints/src/asm_syntax.rs", + "path": "src/asm_syntax.rs", "line": 114 }, "group": "clippy::restriction", @@ -74,7 +74,7 @@ { "id": "assertions_on_constants", "id_span": { - "path": "clippy_lints/src/assertions_on_constants.rs", + "path": "src/assertions_on_constants.rs", "line": 23 }, "group": "clippy::style", @@ -87,7 +87,7 @@ { "id": "assign_op_pattern", "id_span": { - "path": "clippy_lints/src/assign_ops.rs", + "path": "src/assign_ops.rs", "line": 33 }, "group": "clippy::style", @@ -100,7 +100,7 @@ { "id": "misrefactored_assign_op", "id_span": { - "path": "clippy_lints/src/assign_ops.rs", + "path": "src/assign_ops.rs", "line": 56 }, "group": "clippy::complexity", @@ -113,7 +113,7 @@ { "id": "async_yields_async", "id_span": { - "path": "clippy_lints/src/async_yields_async.rs", + "path": "src/async_yields_async.rs", "line": 36 }, "group": "clippy::correctness", @@ -126,7 +126,7 @@ { "id": "invalid_atomic_ordering", "id_span": { - "path": "clippy_lints/src/atomic_ordering.rs", + "path": "src/atomic_ordering.rs", "line": 47 }, "group": "clippy::correctness", @@ -139,7 +139,7 @@ { "id": "inline_always", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 65 }, "group": "clippy::pedantic", @@ -152,7 +152,7 @@ { "id": "useless_attribute", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 99 }, "group": "clippy::correctness", @@ -165,7 +165,7 @@ { "id": "deprecated_semver", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 118 }, "group": "clippy::correctness", @@ -178,7 +178,7 @@ { "id": "empty_line_after_outer_attr", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 153 }, "group": "clippy::nursery", @@ -191,7 +191,7 @@ { "id": "blanket_clippy_restriction_lints", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 176 }, "group": "clippy::style", @@ -204,7 +204,7 @@ { "id": "deprecated_cfg_attr", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 205 }, "group": "clippy::complexity", @@ -217,7 +217,7 @@ { "id": "mismatched_target_os", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 238 }, "group": "clippy::correctness", @@ -230,7 +230,7 @@ { "id": "await_holding_lock", "id_span": { - "path": "clippy_lints/src/await_holding_invalid.rs", + "path": "src/await_holding_invalid.rs", "line": 47 }, "group": "clippy::pedantic", @@ -243,7 +243,7 @@ { "id": "await_holding_refcell_ref", "id_span": { - "path": "clippy_lints/src/await_holding_invalid.rs", + "path": "src/await_holding_invalid.rs", "line": 86 }, "group": "clippy::pedantic", @@ -256,7 +256,7 @@ { "id": "bad_bit_mask", "id_span": { - "path": "clippy_lints/src/bit_mask.rs", + "path": "src/bit_mask.rs", "line": 44 }, "group": "clippy::correctness", @@ -269,7 +269,7 @@ { "id": "ineffective_bit_mask", "id_span": { - "path": "clippy_lints/src/bit_mask.rs", + "path": "src/bit_mask.rs", "line": 73 }, "group": "clippy::correctness", @@ -282,7 +282,7 @@ { "id": "verbose_bit_mask", "id_span": { - "path": "clippy_lints/src/bit_mask.rs", + "path": "src/bit_mask.rs", "line": 92 }, "group": "clippy::pedantic", @@ -295,7 +295,7 @@ { "id": "blacklisted_name", "id_span": { - "path": "clippy_lints/src/blacklisted_name.rs", + "path": "src/blacklisted_name.rs", "line": 20 }, "group": "clippy::style", @@ -308,7 +308,7 @@ { "id": "blocks_in_if_conditions", "id_span": { - "path": "clippy_lints/src/blocks_in_if_conditions.rs", + "path": "src/blocks_in_if_conditions.rs", "line": 42 }, "group": "clippy::style", @@ -321,7 +321,7 @@ { "id": "nonminimal_bool", "id_span": { - "path": "clippy_lints/src/booleans.rs", + "path": "src/booleans.rs", "line": 31 }, "group": "clippy::complexity", @@ -334,7 +334,7 @@ { "id": "logic_bug", "id_span": { - "path": "clippy_lints/src/booleans.rs", + "path": "src/booleans.rs", "line": 49 }, "group": "clippy::correctness", @@ -347,10 +347,10 @@ { "id": "naive_bytecount", "id_span": { - "path": "clippy_lints/src/bytecount.rs", + "path": "src/bytecount.rs", "line": 30 }, - "group": "clippy::perf", + "group": "clippy::pedantic", "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", "applicability": { "is_multi_suggestion": false, @@ -360,7 +360,7 @@ { "id": "cargo_common_metadata", "id_span": { - "path": "clippy_lints/src/cargo_common_metadata.rs", + "path": "src/cargo_common_metadata.rs", "line": 49 }, "group": "clippy::cargo", @@ -373,7 +373,7 @@ { "id": "case_sensitive_file_extension_comparisons", "id_span": { - "path": "clippy_lints/src/case_sensitive_file_extension_comparisons.rs", + "path": "src/case_sensitive_file_extension_comparisons.rs", "line": 34 }, "group": "clippy::pedantic", @@ -386,7 +386,7 @@ { "id": "checked_conversions", "id_span": { - "path": "clippy_lints/src/checked_conversions.rs", + "path": "src/checked_conversions.rs", "line": 40 }, "group": "clippy::pedantic", @@ -399,7 +399,7 @@ { "id": "cognitive_complexity", "id_span": { - "path": "clippy_lints/src/cognitive_complexity.rs", + "path": "src/cognitive_complexity.rs", "line": 24 }, "group": "clippy::nursery", @@ -412,7 +412,7 @@ { "id": "collapsible_if", "id_span": { - "path": "clippy_lints/src/collapsible_if.rs", + "path": "src/collapsible_if.rs", "line": 50 }, "group": "clippy::style", @@ -425,7 +425,7 @@ { "id": "collapsible_else_if", "id_span": { - "path": "clippy_lints/src/collapsible_if.rs", + "path": "src/collapsible_if.rs", "line": 85 }, "group": "clippy::style", @@ -438,7 +438,7 @@ { "id": "collapsible_match", "id_span": { - "path": "clippy_lints/src/collapsible_match.rs", + "path": "src/collapsible_match.rs", "line": 44 }, "group": "clippy::style", @@ -451,7 +451,7 @@ { "id": "comparison_chain", "id_span": { - "path": "clippy_lints/src/comparison_chain.rs", + "path": "src/comparison_chain.rs", "line": 49 }, "group": "clippy::style", @@ -464,7 +464,7 @@ { "id": "ifs_same_cond", "id_span": { - "path": "clippy_lints/src/copies.rs", + "path": "src/copies.rs", "line": 33 }, "group": "clippy::correctness", @@ -477,7 +477,7 @@ { "id": "same_functions_in_if_condition", "id_span": { - "path": "clippy_lints/src/copies.rs", + "path": "src/copies.rs", "line": 80 }, "group": "clippy::pedantic", @@ -490,7 +490,7 @@ { "id": "if_same_then_else", "id_span": { - "path": "clippy_lints/src/copies.rs", + "path": "src/copies.rs", "line": 101 }, "group": "clippy::correctness", @@ -503,7 +503,7 @@ { "id": "copy_iterator", "id_span": { - "path": "clippy_lints/src/copy_iterator.rs", + "path": "src/copy_iterator.rs", "line": 27 }, "group": "clippy::pedantic", @@ -516,7 +516,7 @@ { "id": "create_dir", "id_span": { - "path": "clippy_lints/src/create_dir.rs", + "path": "src/create_dir.rs", "line": 24 }, "group": "clippy::restriction", @@ -529,7 +529,7 @@ { "id": "dbg_macro", "id_span": { - "path": "clippy_lints/src/dbg_macro.rs", + "path": "src/dbg_macro.rs", "line": 25 }, "group": "clippy::restriction", @@ -542,7 +542,7 @@ { "id": "default_trait_access", "id_span": { - "path": "clippy_lints/src/default.rs", + "path": "src/default.rs", "line": 33 }, "group": "clippy::pedantic", @@ -555,7 +555,7 @@ { "id": "field_reassign_with_default", "id_span": { - "path": "clippy_lints/src/default.rs", + "path": "src/default.rs", "line": 63 }, "group": "clippy::style", @@ -568,7 +568,7 @@ { "id": "default_numeric_fallback", "id_span": { - "path": "clippy_lints/src/default_numeric_fallback.rs", + "path": "src/default_numeric_fallback.rs", "line": 44 }, "group": "clippy::restriction", @@ -581,7 +581,7 @@ { "id": "explicit_deref_methods", "id_span": { - "path": "clippy_lints/src/dereference.rs", + "path": "src/dereference.rs", "line": 32 }, "group": "clippy::pedantic", @@ -594,7 +594,7 @@ { "id": "derive_hash_xor_eq", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 42 }, "group": "clippy::correctness", @@ -607,7 +607,7 @@ { "id": "derive_ord_xor_partial_ord", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 93 }, "group": "clippy::correctness", @@ -620,7 +620,7 @@ { "id": "expl_impl_clone_on_copy", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 119 }, "group": "clippy::pedantic", @@ -633,7 +633,7 @@ { "id": "unsafe_derive_deserialize", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 153 }, "group": "clippy::pedantic", @@ -646,7 +646,7 @@ { "id": "disallowed_method", "id_span": { - "path": "clippy_lints/src/disallowed_method.rs", + "path": "src/disallowed_method.rs", "line": 46 }, "group": "clippy::nursery", @@ -659,7 +659,7 @@ { "id": "doc_markdown", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 63 }, "group": "clippy::pedantic", @@ -672,7 +672,7 @@ { "id": "missing_safety_doc", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 97 }, "group": "clippy::style", @@ -685,7 +685,7 @@ { "id": "missing_errors_doc", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 126 }, "group": "clippy::pedantic", @@ -698,7 +698,7 @@ { "id": "missing_panics_doc", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 157 }, "group": "clippy::pedantic", @@ -711,7 +711,7 @@ { "id": "needless_doctest_main", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 185 }, "group": "clippy::style", @@ -724,7 +724,7 @@ { "id": "double_comparisons", "id_span": { - "path": "clippy_lints/src/double_comparison.rs", + "path": "src/double_comparison.rs", "line": 33 }, "group": "clippy::complexity", @@ -737,7 +737,7 @@ { "id": "double_parens", "id_span": { - "path": "clippy_lints/src/double_parens.rs", + "path": "src/double_parens.rs", "line": 35 }, "group": "clippy::complexity", @@ -750,7 +750,7 @@ { "id": "drop_ref", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 26 }, "group": "clippy::correctness", @@ -760,7 +760,7 @@ { "id": "forget_ref", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 47 }, "group": "clippy::correctness", @@ -770,7 +770,7 @@ { "id": "drop_copy", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 68 }, "group": "clippy::correctness", @@ -780,7 +780,7 @@ { "id": "forget_copy", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 95 }, "group": "clippy::correctness", @@ -790,7 +790,7 @@ { "id": "duration_subsec", "id_span": { - "path": "clippy_lints/src/duration_subsec.rs", + "path": "src/duration_subsec.rs", "line": 34 }, "group": "clippy::complexity", @@ -803,7 +803,7 @@ { "id": "else_if_without_else", "id_span": { - "path": "clippy_lints/src/else_if_without_else.rs", + "path": "src/else_if_without_else.rs", "line": 44 }, "group": "clippy::restriction", @@ -816,7 +816,7 @@ { "id": "empty_enum", "id_span": { - "path": "clippy_lints/src/empty_enum.rs", + "path": "src/empty_enum.rs", "line": 38 }, "group": "clippy::pedantic", @@ -829,7 +829,7 @@ { "id": "map_entry", "id_span": { - "path": "clippy_lints/src/entry.rs", + "path": "src/entry.rs", "line": 48 }, "group": "clippy::perf", @@ -842,7 +842,7 @@ { "id": "enum_clike_unportable_variant", "id_span": { - "path": "clippy_lints/src/enum_clike.rs", + "path": "src/enum_clike.rs", "line": 31 }, "group": "clippy::correctness", @@ -855,7 +855,7 @@ { "id": "enum_variant_names", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 36 }, "group": "clippy::style", @@ -865,7 +865,7 @@ { "id": "pub_enum_variant_names", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 66 }, "group": "clippy::pedantic", @@ -875,7 +875,7 @@ { "id": "module_name_repetitions", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 91 }, "group": "clippy::pedantic", @@ -888,7 +888,7 @@ { "id": "module_inception", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 121 }, "group": "clippy::style", @@ -901,7 +901,7 @@ { "id": "eq_op", "id_span": { - "path": "clippy_lints/src/eq_op.rs", + "path": "src/eq_op.rs", "line": 34 }, "group": "clippy::correctness", @@ -914,7 +914,7 @@ { "id": "op_ref", "id_span": { - "path": "clippy_lints/src/eq_op.rs", + "path": "src/eq_op.rs", "line": 56 }, "group": "clippy::style", @@ -927,7 +927,7 @@ { "id": "erasing_op", "id_span": { - "path": "clippy_lints/src/erasing_op.rs", + "path": "src/erasing_op.rs", "line": 25 }, "group": "clippy::correctness", @@ -940,7 +940,7 @@ { "id": "boxed_local", "id_span": { - "path": "clippy_lints/src/escape.rs", + "path": "src/escape.rs", "line": 43 }, "group": "clippy::perf", @@ -953,7 +953,7 @@ { "id": "redundant_closure", "id_span": { - "path": "clippy_lints/src/eta_reduction.rs", + "path": "src/eta_reduction.rs", "line": 37 }, "group": "clippy::style", @@ -966,7 +966,7 @@ { "id": "redundant_closure_for_method_calls", "id_span": { - "path": "clippy_lints/src/eta_reduction.rs", + "path": "src/eta_reduction.rs", "line": 61 }, "group": "clippy::pedantic", @@ -979,7 +979,7 @@ { "id": "eval_order_dependence", "id_span": { - "path": "clippy_lints/src/eval_order_dependence.rs", + "path": "src/eval_order_dependence.rs", "line": 38 }, "group": "clippy::complexity", @@ -992,7 +992,7 @@ { "id": "diverging_sub_expression", "id_span": { - "path": "clippy_lints/src/eval_order_dependence.rs", + "path": "src/eval_order_dependence.rs", "line": 62 }, "group": "clippy::complexity", @@ -1005,7 +1005,7 @@ { "id": "struct_excessive_bools", "id_span": { - "path": "clippy_lints/src/excessive_bools.rs", + "path": "src/excessive_bools.rs", "line": 40 }, "group": "clippy::pedantic", @@ -1018,7 +1018,7 @@ { "id": "fn_params_excessive_bools", "id_span": { - "path": "clippy_lints/src/excessive_bools.rs", + "path": "src/excessive_bools.rs", "line": 78 }, "group": "clippy::pedantic", @@ -1031,27 +1031,33 @@ { "id": "exhaustive_enums", "id_span": { - "path": "clippy_lints/src/exhaustive_items.rs", + "path": "src/exhaustive_items.rs", "line": 34 }, "group": "clippy::restriction", "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } }, { "id": "exhaustive_structs", "id_span": { - "path": "clippy_lints/src/exhaustive_items.rs", + "path": "src/exhaustive_items.rs", "line": 64 }, "group": "clippy::restriction", "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } }, { "id": "exit", "id_span": { - "path": "clippy_lints/src/exit.rs", + "path": "src/exit.rs", "line": 20 }, "group": "clippy::restriction", @@ -1064,7 +1070,7 @@ { "id": "explicit_write", "id_span": { - "path": "clippy_lints/src/explicit_write.rs", + "path": "src/explicit_write.rs", "line": 25 }, "group": "clippy::complexity", @@ -1077,7 +1083,7 @@ { "id": "fallible_impl_from", "id_span": { - "path": "clippy_lints/src/fallible_impl_from.rs", + "path": "src/fallible_impl_from.rs", "line": 45 }, "group": "clippy::nursery", @@ -1090,7 +1096,7 @@ { "id": "float_equality_without_abs", "id_span": { - "path": "clippy_lints/src/float_equality_without_abs.rs", + "path": "src/float_equality_without_abs.rs", "line": 37 }, "group": "clippy::correctness", @@ -1103,7 +1109,7 @@ { "id": "excessive_precision", "id_span": { - "path": "clippy_lints/src/float_literal.rs", + "path": "src/float_literal.rs", "line": 30 }, "group": "clippy::style", @@ -1116,7 +1122,7 @@ { "id": "lossy_float_literal", "id_span": { - "path": "clippy_lints/src/float_literal.rs", + "path": "src/float_literal.rs", "line": 54 }, "group": "clippy::restriction", @@ -1129,7 +1135,7 @@ { "id": "imprecise_flops", "id_span": { - "path": "clippy_lints/src/floating_point_arithmetic.rs", + "path": "src/floating_point_arithmetic.rs", "line": 45 }, "group": "clippy::nursery", @@ -1142,7 +1148,7 @@ { "id": "suboptimal_flops", "id_span": { - "path": "clippy_lints/src/floating_point_arithmetic.rs", + "path": "src/floating_point_arithmetic.rs", "line": 102 }, "group": "clippy::nursery", @@ -1155,7 +1161,7 @@ { "id": "useless_format", "id_span": { - "path": "clippy_lints/src/format.rs", + "path": "src/format.rs", "line": 37 }, "group": "clippy::complexity", @@ -1168,7 +1174,7 @@ { "id": "suspicious_assignment_formatting", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 22 }, "group": "clippy::style", @@ -1181,7 +1187,7 @@ { "id": "suspicious_unary_op_formatting", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 44 }, "group": "clippy::style", @@ -1194,7 +1200,7 @@ { "id": "suspicious_else_formatting", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 80 }, "group": "clippy::style", @@ -1207,7 +1213,7 @@ { "id": "possible_missing_comma", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 100 }, "group": "clippy::correctness", @@ -1220,7 +1226,7 @@ { "id": "from_over_into", "id_span": { - "path": "clippy_lints/src/from_over_into.rs", + "path": "src/from_over_into.rs", "line": 39 }, "group": "clippy::style", @@ -1233,7 +1239,7 @@ { "id": "from_str_radix_10", "id_span": { - "path": "clippy_lints/src/from_str_radix_10.rs", + "path": "src/from_str_radix_10.rs", "line": 37 }, "group": "clippy::style", @@ -1246,7 +1252,7 @@ { "id": "too_many_arguments", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 39 }, "group": "clippy::complexity", @@ -1259,7 +1265,7 @@ { "id": "too_many_lines", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 62 }, "group": "clippy::pedantic", @@ -1272,7 +1278,7 @@ { "id": "not_unsafe_ptr_arg_deref", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 96 }, "group": "clippy::correctness", @@ -1285,7 +1291,7 @@ { "id": "must_use_unit", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 117 }, "group": "clippy::style", @@ -1298,7 +1304,7 @@ { "id": "double_must_use", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 142 }, "group": "clippy::style", @@ -1311,7 +1317,7 @@ { "id": "must_use_candidate", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 170 }, "group": "clippy::pedantic", @@ -1324,7 +1330,7 @@ { "id": "result_unit_err", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 216 }, "group": "clippy::style", @@ -1337,7 +1343,7 @@ { "id": "future_not_send", "id_span": { - "path": "clippy_lints/src/future_not_send.rs", + "path": "src/future_not_send.rs", "line": 44 }, "group": "clippy::nursery", @@ -1350,7 +1356,7 @@ { "id": "get_last_with_len", "id_span": { - "path": "clippy_lints/src/get_last_with_len.rs", + "path": "src/get_last_with_len.rs", "line": 40 }, "group": "clippy::complexity", @@ -1363,7 +1369,7 @@ { "id": "identity_op", "id_span": { - "path": "clippy_lints/src/identity_op.rs", + "path": "src/identity_op.rs", "line": 24 }, "group": "clippy::complexity", @@ -1376,7 +1382,7 @@ { "id": "if_let_mutex", "id_span": { - "path": "clippy_lints/src/if_let_mutex.rs", + "path": "src/if_let_mutex.rs", "line": 36 }, "group": "clippy::correctness", @@ -1389,7 +1395,7 @@ { "id": "if_let_some_result", "id_span": { - "path": "clippy_lints/src/if_let_some_result.rs", + "path": "src/if_let_some_result.rs", "line": 34 }, "group": "clippy::style", @@ -1402,7 +1408,7 @@ { "id": "if_not_else", "id_span": { - "path": "clippy_lints/src/if_not_else.rs", + "path": "src/if_not_else.rs", "line": 43 }, "group": "clippy::pedantic", @@ -1415,7 +1421,7 @@ { "id": "implicit_return", "id_span": { - "path": "clippy_lints/src/implicit_return.rs", + "path": "src/implicit_return.rs", "line": 33 }, "group": "clippy::restriction", @@ -1428,7 +1434,7 @@ { "id": "implicit_saturating_sub", "id_span": { - "path": "clippy_lints/src/implicit_saturating_sub.rs", + "path": "src/implicit_saturating_sub.rs", "line": 32 }, "group": "clippy::pedantic", @@ -1441,7 +1447,7 @@ { "id": "inconsistent_struct_constructor", "id_span": { - "path": "clippy_lints/src/inconsistent_struct_constructor.rs", + "path": "src/inconsistent_struct_constructor.rs", "line": 58 }, "group": "clippy::style", @@ -1454,7 +1460,7 @@ { "id": "out_of_bounds_indexing", "id_span": { - "path": "clippy_lints/src/indexing_slicing.rs", + "path": "src/indexing_slicing.rs", "line": 32 }, "group": "clippy::correctness", @@ -1467,7 +1473,7 @@ { "id": "indexing_slicing", "id_span": { - "path": "clippy_lints/src/indexing_slicing.rs", + "path": "src/indexing_slicing.rs", "line": 81 }, "group": "clippy::restriction", @@ -1480,27 +1486,33 @@ { "id": "infinite_iter", "id_span": { - "path": "clippy_lints/src/infinite_iter.rs", + "path": "src/infinite_iter.rs", "line": 21 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "maybe_infinite_iter", "id_span": { - "path": "clippy_lints/src/infinite_iter.rs", + "path": "src/infinite_iter.rs", "line": 40 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "multiple_inherent_impl", "id_span": { - "path": "clippy_lints/src/inherent_impl.rs", + "path": "src/inherent_impl.rs", "line": 37 }, "group": "clippy::restriction", @@ -1513,7 +1525,7 @@ { "id": "inherent_to_string", "id_span": { - "path": "clippy_lints/src/inherent_to_string.rs", + "path": "src/inherent_to_string.rs", "line": 44 }, "group": "clippy::style", @@ -1526,7 +1538,7 @@ { "id": "inherent_to_string_shadow_display", "id_span": { - "path": "clippy_lints/src/inherent_to_string.rs", + "path": "src/inherent_to_string.rs", "line": 89 }, "group": "clippy::correctness", @@ -1539,7 +1551,7 @@ { "id": "inline_fn_without_body", "id_span": { - "path": "clippy_lints/src/inline_fn_without_body.rs", + "path": "src/inline_fn_without_body.rs", "line": 27 }, "group": "clippy::correctness", @@ -1552,7 +1564,7 @@ { "id": "int_plus_one", "id_span": { - "path": "clippy_lints/src/int_plus_one.rs", + "path": "src/int_plus_one.rs", "line": 31 }, "group": "clippy::complexity", @@ -1565,7 +1577,7 @@ { "id": "integer_division", "id_span": { - "path": "clippy_lints/src/integer_division.rs", + "path": "src/integer_division.rs", "line": 26 }, "group": "clippy::restriction", @@ -1578,7 +1590,7 @@ { "id": "items_after_statements", "id_span": { - "path": "clippy_lints/src/items_after_statements.rs", + "path": "src/items_after_statements.rs", "line": 48 }, "group": "clippy::pedantic", @@ -1591,7 +1603,7 @@ { "id": "large_const_arrays", "id_span": { - "path": "clippy_lints/src/large_const_arrays.rs", + "path": "src/large_const_arrays.rs", "line": 30 }, "group": "clippy::perf", @@ -1604,7 +1616,7 @@ { "id": "large_enum_variant", "id_span": { - "path": "clippy_lints/src/large_enum_variant.rs", + "path": "src/large_enum_variant.rs", "line": 39 }, "group": "clippy::perf", @@ -1617,7 +1629,7 @@ { "id": "large_stack_arrays", "id_span": { - "path": "clippy_lints/src/large_stack_arrays.rs", + "path": "src/large_stack_arrays.rs", "line": 23 }, "group": "clippy::pedantic", @@ -1630,7 +1642,7 @@ { "id": "len_zero", "id_span": { - "path": "clippy_lints/src/len_zero.rs", + "path": "src/len_zero.rs", "line": 41 }, "group": "clippy::style", @@ -1643,7 +1655,7 @@ { "id": "len_without_is_empty", "id_span": { - "path": "clippy_lints/src/len_zero.rs", + "path": "src/len_zero.rs", "line": 66 }, "group": "clippy::style", @@ -1656,7 +1668,7 @@ { "id": "comparison_to_empty", "id_span": { - "path": "clippy_lints/src/len_zero.rs", + "path": "src/len_zero.rs", "line": 103 }, "group": "clippy::style", @@ -1669,7 +1681,7 @@ { "id": "useless_let_if_seq", "id_span": { - "path": "clippy_lints/src/let_if_seq.rs", + "path": "src/let_if_seq.rs", "line": 49 }, "group": "clippy::nursery", @@ -1682,7 +1694,7 @@ { "id": "let_underscore_must_use", "id_span": { - "path": "clippy_lints/src/let_underscore.rs", + "path": "src/let_underscore.rs", "line": 29 }, "group": "clippy::restriction", @@ -1695,7 +1707,7 @@ { "id": "let_underscore_lock", "id_span": { - "path": "clippy_lints/src/let_underscore.rs", + "path": "src/let_underscore.rs", "line": 56 }, "group": "clippy::correctness", @@ -1708,7 +1720,7 @@ { "id": "let_underscore_drop", "id_span": { - "path": "clippy_lints/src/let_underscore.rs", + "path": "src/let_underscore.rs", "line": 97 }, "group": "clippy::pedantic", @@ -1721,7 +1733,7 @@ { "id": "needless_lifetimes", "id_span": { - "path": "clippy_lints/src/lifetimes.rs", + "path": "src/lifetimes.rs", "line": 46 }, "group": "clippy::complexity", @@ -1734,7 +1746,7 @@ { "id": "extra_unused_lifetimes", "id_span": { - "path": "clippy_lints/src/lifetimes.rs", + "path": "src/lifetimes.rs", "line": 74 }, "group": "clippy::complexity", @@ -1747,7 +1759,7 @@ { "id": "unreadable_literal", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 33 }, "group": "clippy::pedantic", @@ -1760,7 +1772,7 @@ { "id": "mistyped_literal_suffixes", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 57 }, "group": "clippy::correctness", @@ -1773,7 +1785,7 @@ { "id": "inconsistent_digit_grouping", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 80 }, "group": "clippy::style", @@ -1786,7 +1798,7 @@ { "id": "unusual_byte_groupings", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 99 }, "group": "clippy::style", @@ -1799,7 +1811,7 @@ { "id": "large_digit_groups", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 118 }, "group": "clippy::pedantic", @@ -1812,7 +1824,7 @@ { "id": "decimal_literal_representation", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 136 }, "group": "clippy::restriction", @@ -1825,8 +1837,8 @@ { "id": "manual_memcpy", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 57 + "path": "src/loops/mod.rs", + "line": 50 }, "group": "clippy::perf", "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", @@ -1838,8 +1850,8 @@ { "id": "needless_range_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 85 + "path": "src/loops/mod.rs", + "line": 78 }, "group": "clippy::style", "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", @@ -1851,8 +1863,8 @@ { "id": "explicit_iter_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 114 + "path": "src/loops/mod.rs", + "line": 107 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", @@ -1864,8 +1876,8 @@ { "id": "explicit_into_iter_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 142 + "path": "src/loops/mod.rs", + "line": 135 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", @@ -1877,8 +1889,8 @@ { "id": "iter_next_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 165 + "path": "src/loops/mod.rs", + "line": 158 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", @@ -1890,8 +1902,8 @@ { "id": "for_loops_over_fallibles", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 208 + "path": "src/loops/mod.rs", + "line": 201 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", @@ -1903,8 +1915,8 @@ { "id": "while_let_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 237 + "path": "src/loops/mod.rs", + "line": 230 }, "group": "clippy::complexity", "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", @@ -1916,8 +1928,8 @@ { "id": "needless_collect", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 259 + "path": "src/loops/mod.rs", + "line": 252 }, "group": "clippy::perf", "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", @@ -1929,8 +1941,8 @@ { "id": "explicit_counter_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 289 + "path": "src/loops/mod.rs", + "line": 282 }, "group": "clippy::complexity", "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", @@ -1942,8 +1954,8 @@ { "id": "empty_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 322 + "path": "src/loops/mod.rs", + "line": 315 }, "group": "clippy::style", "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", @@ -1955,8 +1967,8 @@ { "id": "while_let_on_iterator", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 341 + "path": "src/loops/mod.rs", + "line": 334 }, "group": "clippy::style", "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", @@ -1968,8 +1980,8 @@ { "id": "for_kv_map", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 369 + "path": "src/loops/mod.rs", + "line": 362 }, "group": "clippy::style", "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", @@ -1981,8 +1993,8 @@ { "id": "never_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 390 + "path": "src/loops/mod.rs", + "line": 383 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", @@ -1994,8 +2006,8 @@ { "id": "mut_range_bound", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 410 + "path": "src/loops/mod.rs", + "line": 403 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", @@ -2007,8 +2019,8 @@ { "id": "while_immutable_condition", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 433 + "path": "src/loops/mod.rs", + "line": 426 }, "group": "clippy::correctness", "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", @@ -2020,8 +2032,8 @@ { "id": "same_item_push", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 466 + "path": "src/loops/mod.rs", + "line": 459 }, "group": "clippy::style", "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", @@ -2033,8 +2045,8 @@ { "id": "single_element_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 491 + "path": "src/loops/mod.rs", + "line": 484 }, "group": "clippy::complexity", "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", @@ -2046,8 +2058,8 @@ { "id": "manual_flatten", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 522 + "path": "src/loops/mod.rs", + "line": 515 }, "group": "clippy::complexity", "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", @@ -2059,7 +2071,7 @@ { "id": "macro_use_imports", "id_span": { - "path": "clippy_lints/src/macro_use.rs", + "path": "src/macro_use.rs", "line": 25 }, "group": "clippy::pedantic", @@ -2072,7 +2084,7 @@ { "id": "main_recursion", "id_span": { - "path": "clippy_lints/src/main_recursion.rs", + "path": "src/main_recursion.rs", "line": 22 }, "group": "clippy::style", @@ -2085,7 +2097,7 @@ { "id": "manual_async_fn", "id_span": { - "path": "clippy_lints/src/manual_async_fn.rs", + "path": "src/manual_async_fn.rs", "line": 32 }, "group": "clippy::style", @@ -2098,11 +2110,11 @@ { "id": "manual_map", "id_span": { - "path": "clippy_lints/src/manual_map.rs", - "line": 36 + "path": "src/manual_map.rs", + "line": 44 }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** `map` is not capable of representing some control flow which works fine in `match`.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", + "group": "clippy::style", + "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", "applicability": { "is_multi_suggestion": false, "applicability": "MachineApplicable" @@ -2111,7 +2123,7 @@ { "id": "manual_non_exhaustive", "id_span": { - "path": "clippy_lints/src/manual_non_exhaustive.rs", + "path": "src/manual_non_exhaustive.rs", "line": 56 }, "group": "clippy::style", @@ -2124,7 +2136,7 @@ { "id": "manual_ok_or", "id_span": { - "path": "clippy_lints/src/manual_ok_or.rs", + "path": "src/manual_ok_or.rs", "line": 34 }, "group": "clippy::pedantic", @@ -2137,7 +2149,7 @@ { "id": "manual_strip", "id_span": { - "path": "clippy_lints/src/manual_strip.rs", + "path": "src/manual_strip.rs", "line": 52 }, "group": "clippy::complexity", @@ -2150,7 +2162,7 @@ { "id": "manual_unwrap_or", "id_span": { - "path": "clippy_lints/src/manual_unwrap_or.rs", + "path": "src/manual_unwrap_or.rs", "line": 36 }, "group": "clippy::complexity", @@ -2163,7 +2175,7 @@ { "id": "map_clone", "id_span": { - "path": "clippy_lints/src/map_clone.rs", + "path": "src/map_clone.rs", "line": 40 }, "group": "clippy::style", @@ -2176,7 +2188,7 @@ { "id": "map_err_ignore", "id_span": { - "path": "clippy_lints/src/map_err_ignore.rs", + "path": "src/map_err_ignore.rs", "line": 101 }, "group": "clippy::restriction", @@ -2189,7 +2201,7 @@ { "id": "map_identity", "id_span": { - "path": "clippy_lints/src/map_identity.rs", + "path": "src/map_identity.rs", "line": 30 }, "group": "clippy::complexity", @@ -2202,27 +2214,33 @@ { "id": "option_map_unit_fn", "id_span": { - "path": "clippy_lints/src/map_unit_fn.rs", + "path": "src/map_unit_fn.rs", "line": 48 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } }, { "id": "result_map_unit_fn", "id_span": { - "path": "clippy_lints/src/map_unit_fn.rs", + "path": "src/map_unit_fn.rs", "line": 89 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } }, { "id": "match_on_vec_items", "id_span": { - "path": "clippy_lints/src/match_on_vec_items.rs", + "path": "src/match_on_vec_items.rs", "line": 41 }, "group": "clippy::pedantic", @@ -2235,27 +2253,33 @@ { "id": "single_match", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 55 }, "group": "clippy::style", "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } }, { "id": "single_match_else", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 94 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } }, { "id": "match_ref_pats", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 125 }, "group": "clippy::style", @@ -2268,7 +2292,7 @@ { "id": "match_bool", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 159 }, "group": "clippy::pedantic", @@ -2281,7 +2305,7 @@ { "id": "match_overlapping_arm", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 181 }, "group": "clippy::style", @@ -2294,7 +2318,7 @@ { "id": "match_wild_err_arm", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 203 }, "group": "clippy::pedantic", @@ -2307,7 +2331,7 @@ { "id": "match_as_ref", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 229 }, "group": "clippy::complexity", @@ -2320,7 +2344,7 @@ { "id": "wildcard_enum_match_arm", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 258 }, "group": "clippy::restriction", @@ -2333,7 +2357,7 @@ { "id": "match_wildcard_for_single_variants", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 290 }, "group": "clippy::pedantic", @@ -2346,7 +2370,7 @@ { "id": "wildcard_in_or_patterns", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 317 }, "group": "clippy::complexity", @@ -2359,7 +2383,7 @@ { "id": "infallible_destructuring_match", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 352 }, "group": "clippy::style", @@ -2372,7 +2396,7 @@ { "id": "match_single_binding", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 380 }, "group": "clippy::complexity", @@ -2385,7 +2409,7 @@ { "id": "rest_pat_in_fully_bound_structs", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 410 }, "group": "clippy::restriction", @@ -2398,7 +2422,7 @@ { "id": "redundant_pattern_matching", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 458 }, "group": "clippy::style", @@ -2411,7 +2435,7 @@ { "id": "match_like_matches_macro", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 491 }, "group": "clippy::style", @@ -2424,7 +2448,7 @@ { "id": "match_same_arms", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 532 }, "group": "clippy::pedantic", @@ -2437,7 +2461,7 @@ { "id": "mem_discriminant_non_enum", "id_span": { - "path": "clippy_lints/src/mem_discriminant.rs", + "path": "src/mem_discriminant.rs", "line": 25 }, "group": "clippy::correctness", @@ -2450,7 +2474,7 @@ { "id": "mem_forget", "id_span": { - "path": "clippy_lints/src/mem_forget.rs", + "path": "src/mem_forget.rs", "line": 21 }, "group": "clippy::restriction", @@ -2463,7 +2487,7 @@ { "id": "mem_replace_option_with_none", "id_span": { - "path": "clippy_lints/src/mem_replace.rs", + "path": "src/mem_replace.rs", "line": 37 }, "group": "clippy::style", @@ -2476,7 +2500,7 @@ { "id": "mem_replace_with_uninit", "id_span": { - "path": "clippy_lints/src/mem_replace.rs", + "path": "src/mem_replace.rs", "line": 69 }, "group": "clippy::correctness", @@ -2489,7 +2513,7 @@ { "id": "mem_replace_with_default", "id_span": { - "path": "clippy_lints/src/mem_replace.rs", + "path": "src/mem_replace.rs", "line": 93 }, "group": "clippy::style", @@ -2502,8 +2526,8 @@ { "id": "unwrap_used", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 82 + "path": "src/methods/mod.rs", + "line": 84 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", @@ -2512,8 +2536,8 @@ { "id": "expect_used", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 124 + "path": "src/methods/mod.rs", + "line": 126 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", @@ -2522,8 +2546,8 @@ { "id": "should_implement_trait", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 153 + "path": "src/methods/mod.rs", + "line": 155 }, "group": "clippy::style", "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", @@ -2535,28 +2559,34 @@ { "id": "wrong_self_convention", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 186 + "path": "src/methods/mod.rs", + "line": 188 }, "group": "clippy::style", "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "wrong_pub_self_convention", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 210 + "path": "src/methods/mod.rs", + "line": 212 }, "group": "clippy::restriction", "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "ok_expect", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 233 + "path": "src/methods/mod.rs", + "line": 235 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", @@ -2568,8 +2598,8 @@ { "id": "map_unwrap_or", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 270 + "path": "src/methods/mod.rs", + "line": 272 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", @@ -2581,28 +2611,34 @@ { "id": "option_map_or_none", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 293 + "path": "src/methods/mod.rs", + "line": 295 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "result_map_or_into_option", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 319 + "path": "src/methods/mod.rs", + "line": 321 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "bind_instead_of_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 352 + "path": "src/methods/mod.rs", + "line": 354 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", @@ -2614,8 +2650,8 @@ { "id": "filter_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 375 + "path": "src/methods/mod.rs", + "line": 377 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", @@ -2627,8 +2663,8 @@ { "id": "skip_while_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 398 + "path": "src/methods/mod.rs", + "line": 400 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", @@ -2640,8 +2676,8 @@ { "id": "map_flatten", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 421 + "path": "src/methods/mod.rs", + "line": 423 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", @@ -2653,8 +2689,8 @@ { "id": "filter_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 450 + "path": "src/methods/mod.rs", + "line": 452 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", @@ -2666,28 +2702,34 @@ { "id": "manual_filter_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 476 + "path": "src/methods/mod.rs", + "line": 478 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "manual_find_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 502 + "path": "src/methods/mod.rs", + "line": 504 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "filter_map_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 524 + "path": "src/methods/mod.rs", + "line": 526 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", @@ -2699,8 +2741,8 @@ { "id": "flat_map_identity", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 546 + "path": "src/methods/mod.rs", + "line": 548 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", @@ -2712,8 +2754,8 @@ { "id": "search_is_some", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 570 + "path": "src/methods/mod.rs", + "line": 572 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", @@ -2725,8 +2767,8 @@ { "id": "chars_next_cmp", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 594 + "path": "src/methods/mod.rs", + "line": 596 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", @@ -2735,8 +2777,8 @@ { "id": "or_fun_call", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 625 + "path": "src/methods/mod.rs", + "line": 627 }, "group": "clippy::perf", "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", @@ -2748,8 +2790,8 @@ { "id": "expect_fun_call", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 660 + "path": "src/methods/mod.rs", + "line": 662 }, "group": "clippy::perf", "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", @@ -2761,8 +2803,8 @@ { "id": "clone_on_copy", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 677 + "path": "src/methods/mod.rs", + "line": 679 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", @@ -2774,8 +2816,8 @@ { "id": "clone_on_ref_ptr", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 702 + "path": "src/methods/mod.rs", + "line": 704 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", @@ -2787,8 +2829,8 @@ { "id": "clone_double_ref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 724 + "path": "src/methods/mod.rs", + "line": 726 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", @@ -2800,8 +2842,8 @@ { "id": "inefficient_to_string", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 747 + "path": "src/methods/mod.rs", + "line": 749 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", @@ -2813,8 +2855,8 @@ { "id": "new_ret_no_self", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 808 + "path": "src/methods/mod.rs", + "line": 810 }, "group": "clippy::style", "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", @@ -2826,8 +2868,8 @@ { "id": "single_char_pattern", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 829 + "path": "src/methods/mod.rs", + "line": 831 }, "group": "clippy::perf", "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", @@ -2839,8 +2881,8 @@ { "id": "iterator_step_by_zero", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 848 + "path": "src/methods/mod.rs", + "line": 850 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", @@ -2852,8 +2894,8 @@ { "id": "iter_nth_zero", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 876 + "path": "src/methods/mod.rs", + "line": 878 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", @@ -2865,8 +2907,8 @@ { "id": "iter_nth", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 902 + "path": "src/methods/mod.rs", + "line": 904 }, "group": "clippy::perf", "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", @@ -2878,8 +2920,8 @@ { "id": "iter_skip_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 926 + "path": "src/methods/mod.rs", + "line": 928 }, "group": "clippy::style", "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", @@ -2891,8 +2933,8 @@ { "id": "get_unwrap", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 959 + "path": "src/methods/mod.rs", + "line": 961 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", @@ -2904,8 +2946,8 @@ { "id": "string_extend_chars", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 988 + "path": "src/methods/mod.rs", + "line": 990 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", @@ -2917,8 +2959,8 @@ { "id": "iter_cloned_collect", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1011 + "path": "src/methods/mod.rs", + "line": 1013 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", @@ -2930,8 +2972,8 @@ { "id": "chars_last_cmp", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1035 + "path": "src/methods/mod.rs", + "line": 1037 }, "group": "clippy::style", "docs": " **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.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", @@ -2940,8 +2982,8 @@ { "id": "useless_asref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1060 + "path": "src/methods/mod.rs", + "line": 1062 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", @@ -2953,8 +2995,8 @@ { "id": "unnecessary_fold", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1082 + "path": "src/methods/mod.rs", + "line": 1084 }, "group": "clippy::style", "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", @@ -2966,8 +3008,8 @@ { "id": "unnecessary_filter_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1111 + "path": "src/methods/mod.rs", + "line": 1113 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", @@ -2979,8 +3021,8 @@ { "id": "into_iter_on_ref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1135 + "path": "src/methods/mod.rs", + "line": 1137 }, "group": "clippy::style", "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", @@ -2992,8 +3034,8 @@ { "id": "suspicious_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1154 + "path": "src/methods/mod.rs", + "line": 1156 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", @@ -3005,8 +3047,8 @@ { "id": "uninit_assumed_init", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1186 + "path": "src/methods/mod.rs", + "line": 1188 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", @@ -3018,8 +3060,8 @@ { "id": "manual_saturating_arithmetic", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1213 + "path": "src/methods/mod.rs", + "line": 1215 }, "group": "clippy::style", "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", @@ -3031,8 +3073,8 @@ { "id": "zst_offset", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1230 + "path": "src/methods/mod.rs", + "line": 1232 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", @@ -3044,8 +3086,8 @@ { "id": "filetype_is_file", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1270 + "path": "src/methods/mod.rs", + "line": 1272 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", @@ -3057,8 +3099,8 @@ { "id": "option_as_ref_deref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1295 + "path": "src/methods/mod.rs", + "line": 1297 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", @@ -3070,8 +3112,8 @@ { "id": "iter_next_slice", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1321 + "path": "src/methods/mod.rs", + "line": 1323 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", @@ -3083,8 +3125,8 @@ { "id": "single_char_add_str", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1346 + "path": "src/methods/mod.rs", + "line": 1348 }, "group": "clippy::style", "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", @@ -3096,8 +3138,8 @@ { "id": "unnecessary_lazy_evaluations", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1382 + "path": "src/methods/mod.rs", + "line": 1384 }, "group": "clippy::style", "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", @@ -3109,8 +3151,8 @@ { "id": "map_collect_result_unit", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1403 + "path": "src/methods/mod.rs", + "line": 1405 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", @@ -3122,8 +3164,8 @@ { "id": "from_iter_instead_of_collect", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1436 + "path": "src/methods/mod.rs", + "line": 1438 }, "group": "clippy::style", "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", @@ -3135,8 +3177,8 @@ { "id": "inspect_for_each", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1466 + "path": "src/methods/mod.rs", + "line": 1468 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", @@ -3148,8 +3190,8 @@ { "id": "filter_map_identity", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1489 + "path": "src/methods/mod.rs", + "line": 1491 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", @@ -3161,8 +3203,8 @@ { "id": "bytes_nth", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1511 + "path": "src/methods/mod.rs", + "line": 1513 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", @@ -3171,10 +3213,36 @@ "applicability": "MachineApplicable" } }, + { + "id": "implicit_clone", + "id_span": { + "path": "src/methods/mod.rs", + "line": 1539 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.\n **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as\n to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = vec![1, 2, 3];\n let b = a.to_vec();\n let c = a.to_owned();\n ```\n Use instead:\n ```rust\n let a = vec![1, 2, 3];\n let b = a.clone();\n let c = a.clone();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_count", + "id_span": { + "path": "src/methods/mod.rs", + "line": 1565 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the use of `.iter().count()`.\n **Why is this bad?** `.len()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.iter().count();\n let _ = &some_vec[..].iter().count();\n\n // Good\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.len();\n let _ = &some_vec[..].len();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, { "id": "min_max", "id_span": { - "path": "clippy_lints/src/minmax.rs", + "path": "src/minmax.rs", "line": 28 }, "group": "clippy::correctness", @@ -3187,7 +3255,7 @@ { "id": "toplevel_ref_arg", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 53 }, "group": "clippy::style", @@ -3200,7 +3268,7 @@ { "id": "cmp_nan", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 76 }, "group": "clippy::correctness", @@ -3213,7 +3281,7 @@ { "id": "float_cmp", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 109 }, "group": "clippy::correctness", @@ -3223,7 +3291,7 @@ { "id": "cmp_owned", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 136 }, "group": "clippy::perf", @@ -3236,7 +3304,7 @@ { "id": "modulo_one", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 159 }, "group": "clippy::correctness", @@ -3249,7 +3317,7 @@ { "id": "used_underscore_binding", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 181 }, "group": "clippy::pedantic", @@ -3262,7 +3330,7 @@ { "id": "short_circuit_statement", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 201 }, "group": "clippy::complexity", @@ -3275,7 +3343,7 @@ { "id": "zero_ptr", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 223 }, "group": "clippy::style", @@ -3288,7 +3356,7 @@ { "id": "float_cmp_const", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 254 }, "group": "clippy::restriction", @@ -3298,7 +3366,7 @@ { "id": "unneeded_field_pattern", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 44 }, "group": "clippy::restriction", @@ -3311,7 +3379,7 @@ { "id": "duplicate_underscore_argument", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 65 }, "group": "clippy::style", @@ -3324,7 +3392,7 @@ { "id": "double_neg", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 83 }, "group": "clippy::style", @@ -3337,7 +3405,7 @@ { "id": "mixed_case_hex_literals", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 104 }, "group": "clippy::style", @@ -3350,7 +3418,7 @@ { "id": "unseparated_literal_suffix", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 125 }, "group": "clippy::pedantic", @@ -3363,7 +3431,7 @@ { "id": "zero_prefixed_literal", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 163 }, "group": "clippy::complexity", @@ -3376,7 +3444,7 @@ { "id": "builtin_type_shadow", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 184 }, "group": "clippy::style", @@ -3389,7 +3457,7 @@ { "id": "redundant_pattern", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 213 }, "group": "clippy::style", @@ -3402,7 +3470,7 @@ { "id": "unneeded_wildcard_pattern", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 247 }, "group": "clippy::complexity", @@ -3415,7 +3483,7 @@ { "id": "missing_const_for_fn", "id_span": { - "path": "clippy_lints/src/missing_const_for_fn.rs", + "path": "src/missing_const_for_fn.rs", "line": 72 }, "group": "clippy::nursery", @@ -3428,7 +3496,7 @@ { "id": "missing_docs_in_private_items", "id_span": { - "path": "clippy_lints/src/missing_doc.rs", + "path": "src/missing_doc.rs", "line": 29 }, "group": "clippy::restriction", @@ -3441,7 +3509,7 @@ { "id": "missing_inline_in_public_items", "id_span": { - "path": "clippy_lints/src/missing_inline.rs", + "path": "src/missing_inline.rs", "line": 55 }, "group": "clippy::restriction", @@ -3454,7 +3522,7 @@ { "id": "modulo_arithmetic", "id_span": { - "path": "clippy_lints/src/modulo_arithmetic.rs", + "path": "src/modulo_arithmetic.rs", "line": 26 }, "group": "clippy::restriction", @@ -3467,7 +3535,7 @@ { "id": "multiple_crate_versions", "id_span": { - "path": "clippy_lints/src/multiple_crate_versions.rs", + "path": "src/multiple_crate_versions.rs", "line": 32 }, "group": "clippy::cargo", @@ -3480,7 +3548,7 @@ { "id": "mutable_key_type", "id_span": { - "path": "clippy_lints/src/mut_key.rs", + "path": "src/mut_key.rs", "line": 50 }, "group": "clippy::correctness", @@ -3493,7 +3561,7 @@ { "id": "mut_mut", "id_span": { - "path": "clippy_lints/src/mut_mut.rs", + "path": "src/mut_mut.rs", "line": 24 }, "group": "clippy::pedantic", @@ -3506,7 +3574,7 @@ { "id": "mut_mutex_lock", "id_span": { - "path": "clippy_lints/src/mut_mutex_lock.rs", + "path": "src/mut_mutex_lock.rs", "line": 40 }, "group": "clippy::style", @@ -3519,7 +3587,7 @@ { "id": "unnecessary_mut_passed", "id_span": { - "path": "clippy_lints/src/mut_reference.rs", + "path": "src/mut_reference.rs", "line": 25 }, "group": "clippy::style", @@ -3532,7 +3600,7 @@ { "id": "debug_assert_with_mut_call", "id_span": { - "path": "clippy_lints/src/mutable_debug_assertion.rs", + "path": "src/mutable_debug_assertion.rs", "line": 28 }, "group": "clippy::nursery", @@ -3545,7 +3613,7 @@ { "id": "mutex_atomic", "id_span": { - "path": "clippy_lints/src/mutex_atomic.rs", + "path": "src/mutex_atomic.rs", "line": 34 }, "group": "clippy::perf", @@ -3558,7 +3626,7 @@ { "id": "mutex_integer", "id_span": { - "path": "clippy_lints/src/mutex_atomic.rs", + "path": "src/mutex_atomic.rs", "line": 59 }, "group": "clippy::nursery", @@ -3571,7 +3639,7 @@ { "id": "needless_arbitrary_self_type", "id_span": { - "path": "clippy_lints/src/needless_arbitrary_self_type.rs", + "path": "src/needless_arbitrary_self_type.rs", "line": 55 }, "group": "clippy::complexity", @@ -3584,7 +3652,7 @@ { "id": "needless_bool", "id_span": { - "path": "clippy_lints/src/needless_bool.rs", + "path": "src/needless_bool.rs", "line": 38 }, "group": "clippy::complexity", @@ -3597,7 +3665,7 @@ { "id": "bool_comparison", "id_span": { - "path": "clippy_lints/src/needless_bool.rs", + "path": "src/needless_bool.rs", "line": 62 }, "group": "clippy::complexity", @@ -3610,7 +3678,7 @@ { "id": "needless_borrow", "id_span": { - "path": "clippy_lints/src/needless_borrow.rs", + "path": "src/needless_borrow.rs", "line": 32 }, "group": "clippy::nursery", @@ -3623,7 +3691,7 @@ { "id": "needless_borrowed_reference", "id_span": { - "path": "clippy_lints/src/needless_borrowed_ref.rs", + "path": "src/needless_borrowed_ref.rs", "line": 48 }, "group": "clippy::complexity", @@ -3636,7 +3704,7 @@ { "id": "needless_continue", "id_span": { - "path": "clippy_lints/src/needless_continue.rs", + "path": "src/needless_continue.rs", "line": 114 }, "group": "clippy::pedantic", @@ -3649,7 +3717,7 @@ { "id": "needless_pass_by_value", "id_span": { - "path": "clippy_lints/src/needless_pass_by_value.rs", + "path": "src/needless_pass_by_value.rs", "line": 50 }, "group": "clippy::pedantic", @@ -3662,7 +3730,7 @@ { "id": "needless_question_mark", "id_span": { - "path": "clippy_lints/src/needless_question_mark.rs", + "path": "src/needless_question_mark.rs", "line": 57 }, "group": "clippy::complexity", @@ -3675,7 +3743,7 @@ { "id": "needless_update", "id_span": { - "path": "clippy_lints/src/needless_update.rs", + "path": "src/needless_update.rs", "line": 43 }, "group": "clippy::complexity", @@ -3688,7 +3756,7 @@ { "id": "neg_cmp_op_on_partial_ord", "id_span": { - "path": "clippy_lints/src/neg_cmp_op_on_partial_ord.rs", + "path": "src/neg_cmp_op_on_partial_ord.rs", "line": 41 }, "group": "clippy::complexity", @@ -3701,7 +3769,7 @@ { "id": "neg_multiply", "id_span": { - "path": "clippy_lints/src/neg_multiply.rs", + "path": "src/neg_multiply.rs", "line": 21 }, "group": "clippy::style", @@ -3714,7 +3782,7 @@ { "id": "new_without_default", "id_span": { - "path": "clippy_lints/src/new_without_default.rs", + "path": "src/new_without_default.rs", "line": 48 }, "group": "clippy::style", @@ -3727,7 +3795,7 @@ { "id": "no_effect", "id_span": { - "path": "clippy_lints/src/no_effect.rs", + "path": "src/no_effect.rs", "line": 22 }, "group": "clippy::complexity", @@ -3740,7 +3808,7 @@ { "id": "unnecessary_operation", "id_span": { - "path": "clippy_lints/src/no_effect.rs", + "path": "src/no_effect.rs", "line": 40 }, "group": "clippy::complexity", @@ -3753,7 +3821,7 @@ { "id": "declare_interior_mutable_const", "id_span": { - "path": "clippy_lints/src/non_copy_const.rs", + "path": "src/non_copy_const.rs", "line": 69 }, "group": "clippy::style", @@ -3763,7 +3831,7 @@ { "id": "borrow_interior_mutable_const", "id_span": { - "path": "clippy_lints/src/non_copy_const.rs", + "path": "src/non_copy_const.rs", "line": 110 }, "group": "clippy::style", @@ -3773,7 +3841,7 @@ { "id": "similar_names", "id_span": { - "path": "clippy_lints/src/non_expressive_names.rs", + "path": "src/non_expressive_names.rs", "line": 27 }, "group": "clippy::pedantic", @@ -3786,7 +3854,7 @@ { "id": "many_single_char_names", "id_span": { - "path": "clippy_lints/src/non_expressive_names.rs", + "path": "src/non_expressive_names.rs", "line": 45 }, "group": "clippy::style", @@ -3799,7 +3867,7 @@ { "id": "just_underscores_and_digits", "id_span": { - "path": "clippy_lints/src/non_expressive_names.rs", + "path": "src/non_expressive_names.rs", "line": 65 }, "group": "clippy::style", @@ -3812,7 +3880,7 @@ { "id": "nonsensical_open_options", "id_span": { - "path": "clippy_lints/src/open_options.rs", + "path": "src/open_options.rs", "line": 23 }, "group": "clippy::correctness", @@ -3825,7 +3893,7 @@ { "id": "option_env_unwrap", "id_span": { - "path": "clippy_lints/src/option_env_unwrap.rs", + "path": "src/option_env_unwrap.rs", "line": 28 }, "group": "clippy::correctness", @@ -3838,7 +3906,7 @@ { "id": "option_if_let_else", "id_span": { - "path": "clippy_lints/src/option_if_let_else.rs", + "path": "src/option_if_let_else.rs", "line": 59 }, "group": "clippy::pedantic", @@ -3851,7 +3919,7 @@ { "id": "overflow_check_conditional", "id_span": { - "path": "clippy_lints/src/overflow_check_conditional.rs", + "path": "src/overflow_check_conditional.rs", "line": 21 }, "group": "clippy::complexity", @@ -3864,7 +3932,7 @@ { "id": "panic_in_result_fn", "id_span": { - "path": "clippy_lints/src/panic_in_result_fn.rs", + "path": "src/panic_in_result_fn.rs", "line": 29 }, "group": "clippy::restriction", @@ -3877,7 +3945,7 @@ { "id": "panic", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 19 }, "group": "clippy::restriction", @@ -3890,7 +3958,7 @@ { "id": "unimplemented", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 35 }, "group": "clippy::restriction", @@ -3903,7 +3971,7 @@ { "id": "todo", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 51 }, "group": "clippy::restriction", @@ -3916,7 +3984,7 @@ { "id": "unreachable", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 67 }, "group": "clippy::restriction", @@ -3929,7 +3997,7 @@ { "id": "partialeq_ne_impl", "id_span": { - "path": "clippy_lints/src/partialeq_ne_impl.rs", + "path": "src/partialeq_ne_impl.rs", "line": 27 }, "group": "clippy::complexity", @@ -3942,7 +4010,7 @@ { "id": "trivially_copy_pass_by_ref", "id_span": { - "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "path": "src/pass_by_ref_or_value.rs", "line": 52 }, "group": "clippy::pedantic", @@ -3955,7 +4023,7 @@ { "id": "large_types_passed_by_value", "id_span": { - "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "path": "src/pass_by_ref_or_value.rs", "line": 84 }, "group": "clippy::pedantic", @@ -3968,7 +4036,7 @@ { "id": "path_buf_push_overwrite", "id_span": { - "path": "clippy_lints/src/path_buf_push_overwrite.rs", + "path": "src/path_buf_push_overwrite.rs", "line": 36 }, "group": "clippy::nursery", @@ -3981,7 +4049,7 @@ { "id": "pattern_type_mismatch", "id_span": { - "path": "clippy_lints/src/pattern_type_mismatch.rs", + "path": "src/pattern_type_mismatch.rs", "line": 79 }, "group": "clippy::restriction", @@ -3994,7 +4062,7 @@ { "id": "precedence", "id_span": { - "path": "clippy_lints/src/precedence.rs", + "path": "src/precedence.rs", "line": 44 }, "group": "clippy::complexity", @@ -4007,7 +4075,7 @@ { "id": "ptr_arg", "id_span": { - "path": "clippy_lints/src/ptr.rs", + "path": "src/ptr.rs", "line": 69 }, "group": "clippy::style", @@ -4020,7 +4088,7 @@ { "id": "cmp_null", "id_span": { - "path": "clippy_lints/src/ptr.rs", + "path": "src/ptr.rs", "line": 95 }, "group": "clippy::style", @@ -4033,7 +4101,7 @@ { "id": "mut_from_ref", "id_span": { - "path": "clippy_lints/src/ptr.rs", + "path": "src/ptr.rs", "line": 117 }, "group": "clippy::correctness", @@ -4046,7 +4114,7 @@ { "id": "ptr_eq", "id_span": { - "path": "clippy_lints/src/ptr_eq.rs", + "path": "src/ptr_eq.rs", "line": 32 }, "group": "clippy::style", @@ -4059,7 +4127,7 @@ { "id": "ptr_offset_with_cast", "id_span": { - "path": "clippy_lints/src/ptr_offset_with_cast.rs", + "path": "src/ptr_offset_with_cast.rs", "line": 40 }, "group": "clippy::complexity", @@ -4072,7 +4140,7 @@ { "id": "question_mark", "id_span": { - "path": "clippy_lints/src/question_mark.rs", + "path": "src/question_mark.rs", "line": 34 }, "group": "clippy::style", @@ -4085,7 +4153,7 @@ { "id": "range_zip_with_len", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 40 }, "group": "clippy::complexity", @@ -4098,7 +4166,7 @@ { "id": "range_plus_one", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 74 }, "group": "clippy::pedantic", @@ -4111,7 +4179,7 @@ { "id": "range_minus_one", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 99 }, "group": "clippy::pedantic", @@ -4124,7 +4192,7 @@ { "id": "reversed_empty_ranges", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 132 }, "group": "clippy::correctness", @@ -4137,7 +4205,7 @@ { "id": "manual_range_contains", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 159 }, "group": "clippy::style", @@ -4150,7 +4218,7 @@ { "id": "redundant_clone", "id_span": { - "path": "clippy_lints/src/redundant_clone.rs", + "path": "src/redundant_clone.rs", "line": 63 }, "group": "clippy::perf", @@ -4163,7 +4231,7 @@ { "id": "redundant_closure_call", "id_span": { - "path": "clippy_lints/src/redundant_closure_call.rs", + "path": "src/redundant_closure_call.rs", "line": 32 }, "group": "clippy::complexity", @@ -4176,7 +4244,7 @@ { "id": "redundant_else", "id_span": { - "path": "clippy_lints/src/redundant_else.rs", + "path": "src/redundant_else.rs", "line": 37 }, "group": "clippy::pedantic", @@ -4189,7 +4257,7 @@ { "id": "redundant_field_names", "id_span": { - "path": "clippy_lints/src/redundant_field_names.rs", + "path": "src/redundant_field_names.rs", "line": 34 }, "group": "clippy::style", @@ -4202,7 +4270,7 @@ { "id": "redundant_pub_crate", "id_span": { - "path": "clippy_lints/src/redundant_pub_crate.rs", + "path": "src/redundant_pub_crate.rs", "line": 30 }, "group": "clippy::nursery", @@ -4215,7 +4283,7 @@ { "id": "redundant_slicing", "id_span": { - "path": "clippy_lints/src/redundant_slicing.rs", + "path": "src/redundant_slicing.rs", "line": 33 }, "group": "clippy::complexity", @@ -4228,7 +4296,7 @@ { "id": "redundant_static_lifetimes", "id_span": { - "path": "clippy_lints/src/redundant_static_lifetimes.rs", + "path": "src/redundant_static_lifetimes.rs", "line": 30 }, "group": "clippy::style", @@ -4241,7 +4309,7 @@ { "id": "ref_option_ref", "id_span": { - "path": "clippy_lints/src/ref_option_ref.rs", + "path": "src/ref_option_ref.rs", "line": 28 }, "group": "clippy::pedantic", @@ -4254,7 +4322,7 @@ { "id": "deref_addrof", "id_span": { - "path": "clippy_lints/src/reference.rs", + "path": "src/reference.rs", "line": 29 }, "group": "clippy::complexity", @@ -4267,7 +4335,7 @@ { "id": "ref_in_deref", "id_span": { - "path": "clippy_lints/src/reference.rs", + "path": "src/reference.rs", "line": 120 }, "group": "clippy::complexity", @@ -4280,7 +4348,7 @@ { "id": "invalid_regex", "id_span": { - "path": "clippy_lints/src/regex.rs", + "path": "src/regex.rs", "line": 25 }, "group": "clippy::correctness", @@ -4293,7 +4361,7 @@ { "id": "trivial_regex", "id_span": { - "path": "clippy_lints/src/regex.rs", + "path": "src/regex.rs", "line": 46 }, "group": "clippy::nursery", @@ -4306,7 +4374,7 @@ { "id": "repeat_once", "id_span": { - "path": "clippy_lints/src/repeat_once.rs", + "path": "src/repeat_once.rs", "line": 33 }, "group": "clippy::complexity", @@ -4319,7 +4387,7 @@ { "id": "let_and_return", "id_span": { - "path": "clippy_lints/src/returns.rs", + "path": "src/returns.rs", "line": 38 }, "group": "clippy::style", @@ -4332,7 +4400,7 @@ { "id": "needless_return", "id_span": { - "path": "clippy_lints/src/returns.rs", + "path": "src/returns.rs", "line": 63 }, "group": "clippy::style", @@ -4345,7 +4413,7 @@ { "id": "self_assignment", "id_span": { - "path": "clippy_lints/src/self_assignment.rs", + "path": "src/self_assignment.rs", "line": 29 }, "group": "clippy::correctness", @@ -4358,7 +4426,7 @@ { "id": "semicolon_if_nothing_returned", "id_span": { - "path": "clippy_lints/src/semicolon_if_nothing_returned.rs", + "path": "src/semicolon_if_nothing_returned.rs", "line": 30 }, "group": "clippy::restriction", @@ -4371,7 +4439,7 @@ { "id": "serde_api_misuse", "id_span": { - "path": "clippy_lints/src/serde_api.rs", + "path": "src/serde_api.rs", "line": 16 }, "group": "clippy::correctness", @@ -4384,7 +4452,7 @@ { "id": "shadow_same", "id_span": { - "path": "clippy_lints/src/shadow.rs", + "path": "src/shadow.rs", "line": 34 }, "group": "clippy::restriction", @@ -4397,7 +4465,7 @@ { "id": "shadow_reuse", "id_span": { - "path": "clippy_lints/src/shadow.rs", + "path": "src/shadow.rs", "line": 61 }, "group": "clippy::restriction", @@ -4410,7 +4478,7 @@ { "id": "shadow_unrelated", "id_span": { - "path": "clippy_lints/src/shadow.rs", + "path": "src/shadow.rs", "line": 93 }, "group": "clippy::pedantic", @@ -4423,7 +4491,7 @@ { "id": "single_component_path_imports", "id_span": { - "path": "clippy_lints/src/single_component_path_imports.rs", + "path": "src/single_component_path_imports.rs", "line": 32 }, "group": "clippy::style", @@ -4436,7 +4504,7 @@ { "id": "size_of_in_element_count", "id_span": { - "path": "clippy_lints/src/size_of_in_element_count.rs", + "path": "src/size_of_in_element_count.rs", "line": 31 }, "group": "clippy::correctness", @@ -4449,7 +4517,7 @@ { "id": "slow_vector_initialization", "id_span": { - "path": "clippy_lints/src/slow_vector_initialization.rs", + "path": "src/slow_vector_initialization.rs", "line": 37 }, "group": "clippy::perf", @@ -4462,7 +4530,7 @@ { "id": "stable_sort_primitive", "id_span": { - "path": "clippy_lints/src/stable_sort_primitive.rs", + "path": "src/stable_sort_primitive.rs", "line": 36 }, "group": "clippy::perf", @@ -4475,7 +4543,7 @@ { "id": "string_add_assign", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 37 }, "group": "clippy::pedantic", @@ -4488,7 +4556,7 @@ { "id": "string_add", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 65 }, "group": "clippy::restriction", @@ -4501,7 +4569,7 @@ { "id": "string_lit_as_bytes", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 107 }, "group": "clippy::nursery", @@ -4514,7 +4582,7 @@ { "id": "string_from_utf8_as_bytes", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 196 }, "group": "clippy::complexity", @@ -4527,7 +4595,7 @@ { "id": "str_to_string", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 313 }, "group": "clippy::restriction", @@ -4540,7 +4608,7 @@ { "id": "string_to_string", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 362 }, "group": "clippy::restriction", @@ -4553,7 +4621,7 @@ { "id": "suspicious_operation_groupings", "id_span": { - "path": "clippy_lints/src/suspicious_operation_groupings.rs", + "path": "src/suspicious_operation_groupings.rs", "line": 60 }, "group": "clippy::style", @@ -4566,7 +4634,7 @@ { "id": "suspicious_arithmetic_impl", "id_span": { - "path": "clippy_lints/src/suspicious_trait_impl.rs", + "path": "src/suspicious_trait_impl.rs", "line": 27 }, "group": "clippy::correctness", @@ -4579,7 +4647,7 @@ { "id": "suspicious_op_assign_impl", "id_span": { - "path": "clippy_lints/src/suspicious_trait_impl.rs", + "path": "src/suspicious_trait_impl.rs", "line": 48 }, "group": "clippy::correctness", @@ -4592,7 +4660,7 @@ { "id": "manual_swap", "id_span": { - "path": "clippy_lints/src/swap.rs", + "path": "src/swap.rs", "line": 36 }, "group": "clippy::complexity", @@ -4605,7 +4673,7 @@ { "id": "almost_swapped", "id_span": { - "path": "clippy_lints/src/swap.rs", + "path": "src/swap.rs", "line": 61 }, "group": "clippy::correctness", @@ -4618,7 +4686,7 @@ { "id": "tabs_in_doc_comments", "id_span": { - "path": "clippy_lints/src/tabs_in_doc_comments.rs", + "path": "src/tabs_in_doc_comments.rs", "line": 54 }, "group": "clippy::style", @@ -4631,7 +4699,7 @@ { "id": "temporary_assignment", "id_span": { - "path": "clippy_lints/src/temporary_assignment.rs", + "path": "src/temporary_assignment.rs", "line": 19 }, "group": "clippy::complexity", @@ -4644,7 +4712,7 @@ { "id": "to_digit_is_some", "id_span": { - "path": "clippy_lints/src/to_digit_is_some.rs", + "path": "src/to_digit_is_some.rs", "line": 27 }, "group": "clippy::style", @@ -4657,7 +4725,7 @@ { "id": "to_string_in_display", "id_span": { - "path": "clippy_lints/src/to_string_in_display.rs", + "path": "src/to_string_in_display.rs", "line": 40 }, "group": "clippy::correctness", @@ -4670,7 +4738,7 @@ { "id": "type_repetition_in_bounds", "id_span": { - "path": "clippy_lints/src/trait_bounds.rs", + "path": "src/trait_bounds.rs", "line": 28 }, "group": "clippy::pedantic", @@ -4683,7 +4751,7 @@ { "id": "trait_duplication_in_bounds", "id_span": { - "path": "clippy_lints/src/trait_bounds.rs", + "path": "src/trait_bounds.rs", "line": 57 }, "group": "clippy::pedantic", @@ -4696,8 +4764,8 @@ { "id": "wrong_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 29 + "path": "src/transmute/mod.rs", + "line": 34 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", @@ -4709,8 +4777,8 @@ { "id": "useless_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 48 + "path": "src/transmute/mod.rs", + "line": 53 }, "group": "clippy::nursery", "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", @@ -4722,8 +4790,8 @@ { "id": "transmutes_expressible_as_ptr_casts", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 73 + "path": "src/transmute/mod.rs", + "line": 78 }, "group": "clippy::complexity", "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", @@ -4735,8 +4803,8 @@ { "id": "crosspointer_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 91 + "path": "src/transmute/mod.rs", + "line": 96 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", @@ -4748,8 +4816,8 @@ { "id": "transmute_ptr_to_ref", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 116 + "path": "src/transmute/mod.rs", + "line": 121 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", @@ -4761,8 +4829,8 @@ { "id": "transmute_int_to_char", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 147 + "path": "src/transmute/mod.rs", + "line": 152 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", @@ -4774,8 +4842,8 @@ { "id": "transmute_bytes_to_str", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 178 + "path": "src/transmute/mod.rs", + "line": 183 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", @@ -4787,8 +4855,8 @@ { "id": "transmute_int_to_bool", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 200 + "path": "src/transmute/mod.rs", + "line": 205 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", @@ -4800,8 +4868,8 @@ { "id": "transmute_int_to_float", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 222 + "path": "src/transmute/mod.rs", + "line": 227 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", @@ -4813,8 +4881,8 @@ { "id": "transmute_float_to_int", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 244 + "path": "src/transmute/mod.rs", + "line": 249 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", @@ -4826,8 +4894,8 @@ { "id": "transmute_ptr_to_ptr", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 271 + "path": "src/transmute/mod.rs", + "line": 276 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", @@ -4839,8 +4907,8 @@ { "id": "unsound_collection_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 299 + "path": "src/transmute/mod.rs", + "line": 304 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", @@ -4852,7 +4920,7 @@ { "id": "transmuting_null", "id_span": { - "path": "clippy_lints/src/transmuting_null.rs", + "path": "src/transmuting_null.rs", "line": 23 }, "group": "clippy::correctness", @@ -4865,7 +4933,7 @@ { "id": "try_err", "id_span": { - "path": "clippy_lints/src/try_err.rs", + "path": "src/try_err.rs", "line": 43 }, "group": "clippy::style", @@ -4878,7 +4946,7 @@ { "id": "box_vec", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 66 }, "group": "clippy::perf", @@ -4891,7 +4959,7 @@ { "id": "vec_box", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 95 }, "group": "clippy::complexity", @@ -4904,7 +4972,7 @@ { "id": "option_option", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 133 }, "group": "clippy::pedantic", @@ -4917,7 +4985,7 @@ { "id": "linkedlist", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 169 }, "group": "clippy::pedantic", @@ -4930,7 +4998,7 @@ { "id": "borrowed_box", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 193 }, "group": "clippy::complexity", @@ -4943,7 +5011,7 @@ { "id": "redundant_allocation", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 217 }, "group": "clippy::perf", @@ -4956,7 +5024,7 @@ { "id": "rc_buffer", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 248 }, "group": "clippy::restriction", @@ -4969,7 +5037,7 @@ { "id": "let_unit_value", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 768 }, "group": "clippy::pedantic", @@ -4982,7 +5050,7 @@ { "id": "unit_cmp", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 849 }, "group": "clippy::correctness", @@ -4995,7 +5063,7 @@ { "id": "unit_arg", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 922 }, "group": "clippy::complexity", @@ -5008,7 +5076,7 @@ { "id": "cast_precision_loss", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1158 }, "group": "clippy::pedantic", @@ -5021,7 +5089,7 @@ { "id": "cast_sign_loss", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1179 }, "group": "clippy::pedantic", @@ -5034,7 +5102,7 @@ { "id": "cast_possible_truncation", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1201 }, "group": "clippy::pedantic", @@ -5047,7 +5115,7 @@ { "id": "cast_possible_wrap", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1224 }, "group": "clippy::pedantic", @@ -5060,7 +5128,7 @@ { "id": "cast_lossless", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1256 }, "group": "clippy::pedantic", @@ -5073,7 +5141,7 @@ { "id": "unnecessary_cast", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1281 }, "group": "clippy::complexity", @@ -5086,7 +5154,7 @@ { "id": "cast_ptr_alignment", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1305 }, "group": "clippy::pedantic", @@ -5099,7 +5167,7 @@ { "id": "fn_to_numeric_cast", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1332 }, "group": "clippy::style", @@ -5112,7 +5180,7 @@ { "id": "fn_to_numeric_cast_with_truncation", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1362 }, "group": "clippy::style", @@ -5125,7 +5193,7 @@ { "id": "type_complexity", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1897 }, "group": "clippy::complexity", @@ -5138,7 +5206,7 @@ { "id": "char_lit_as_u8", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2068 }, "group": "clippy::complexity", @@ -5151,7 +5219,7 @@ { "id": "absurd_extreme_comparisons", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2133 }, "group": "clippy::correctness", @@ -5164,7 +5232,7 @@ { "id": "invalid_upcast_comparisons", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2294 }, "group": "clippy::pedantic", @@ -5177,7 +5245,7 @@ { "id": "implicit_hasher", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2515 }, "group": "clippy::pedantic", @@ -5190,7 +5258,7 @@ { "id": "cast_ref_to_mut", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2866 }, "group": "clippy::correctness", @@ -5203,7 +5271,7 @@ { "id": "ptr_as_ptr", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2922 }, "group": "clippy::pedantic", @@ -5216,7 +5284,7 @@ { "id": "undropped_manually_drops", "id_span": { - "path": "clippy_lints/src/undropped_manually_drops.rs", + "path": "src/undropped_manually_drops.rs", "line": 27 }, "group": "clippy::correctness", @@ -5229,7 +5297,7 @@ { "id": "invisible_characters", "id_span": { - "path": "clippy_lints/src/unicode.rs", + "path": "src/unicode.rs", "line": 20 }, "group": "clippy::correctness", @@ -5242,7 +5310,7 @@ { "id": "non_ascii_literal", "id_span": { - "path": "clippy_lints/src/unicode.rs", + "path": "src/unicode.rs", "line": 44 }, "group": "clippy::pedantic", @@ -5255,7 +5323,7 @@ { "id": "unicode_not_nfc", "id_span": { - "path": "clippy_lints/src/unicode.rs", + "path": "src/unicode.rs", "line": 61 }, "group": "clippy::pedantic", @@ -5268,7 +5336,7 @@ { "id": "unit_return_expecting_ord", "id_span": { - "path": "clippy_lints/src/unit_return_expecting_ord.rs", + "path": "src/unit_return_expecting_ord.rs", "line": 30 }, "group": "clippy::correctness", @@ -5281,7 +5349,7 @@ { "id": "fn_address_comparisons", "id_span": { - "path": "clippy_lints/src/unnamed_address.rs", + "path": "src/unnamed_address.rs", "line": 27 }, "group": "clippy::correctness", @@ -5294,7 +5362,7 @@ { "id": "vtable_address_comparisons", "id_span": { - "path": "clippy_lints/src/unnamed_address.rs", + "path": "src/unnamed_address.rs", "line": 51 }, "group": "clippy::correctness", @@ -5307,7 +5375,7 @@ { "id": "unnecessary_sort_by", "id_span": { - "path": "clippy_lints/src/unnecessary_sort_by.rs", + "path": "src/unnecessary_sort_by.rs", "line": 41 }, "group": "clippy::complexity", @@ -5320,7 +5388,7 @@ { "id": "unnecessary_wraps", "id_span": { - "path": "clippy_lints/src/unnecessary_wraps.rs", + "path": "src/unnecessary_wraps.rs", "line": 50 }, "group": "clippy::pedantic", @@ -5333,7 +5401,7 @@ { "id": "unnested_or_patterns", "id_span": { - "path": "clippy_lints/src/unnested_or_patterns.rs", + "path": "src/unnested_or_patterns.rs", "line": 47 }, "group": "clippy::pedantic", @@ -5346,7 +5414,7 @@ { "id": "unsafe_removed_from_name", "id_span": { - "path": "clippy_lints/src/unsafe_removed_from_name.rs", + "path": "src/unsafe_removed_from_name.rs", "line": 24 }, "group": "clippy::style", @@ -5359,7 +5427,7 @@ { "id": "unused_io_amount", "id_span": { - "path": "clippy_lints/src/unused_io_amount.rs", + "path": "src/unused_io_amount.rs", "line": 28 }, "group": "clippy::correctness", @@ -5372,7 +5440,7 @@ { "id": "unused_self", "id_span": { - "path": "clippy_lints/src/unused_self.rs", + "path": "src/unused_self.rs", "line": 33 }, "group": "clippy::pedantic", @@ -5385,7 +5453,7 @@ { "id": "unused_unit", "id_span": { - "path": "clippy_lints/src/unused_unit.rs", + "path": "src/unused_unit.rs", "line": 27 }, "group": "clippy::style", @@ -5398,7 +5466,7 @@ { "id": "unnecessary_unwrap", "id_span": { - "path": "clippy_lints/src/unwrap.rs", + "path": "src/unwrap.rs", "line": 40 }, "group": "clippy::complexity", @@ -5411,7 +5479,7 @@ { "id": "panicking_unwrap", "id_span": { - "path": "clippy_lints/src/unwrap.rs", + "path": "src/unwrap.rs", "line": 63 }, "group": "clippy::correctness", @@ -5424,7 +5492,7 @@ { "id": "unwrap_in_result", "id_span": { - "path": "clippy_lints/src/unwrap_in_result.rs", + "path": "src/unwrap_in_result.rs", "line": 47 }, "group": "clippy::restriction", @@ -5437,7 +5505,7 @@ { "id": "upper_case_acronyms", "id_span": { - "path": "clippy_lints/src/upper_case_acronyms.rs", + "path": "src/upper_case_acronyms.rs", "line": 35 }, "group": "clippy::style", @@ -5450,7 +5518,7 @@ { "id": "use_self", "id_span": { - "path": "clippy_lints/src/use_self.rs", + "path": "src/use_self.rs", "line": 53 }, "group": "clippy::nursery", @@ -5463,7 +5531,7 @@ { "id": "useless_conversion", "id_span": { - "path": "clippy_lints/src/useless_conversion.rs", + "path": "src/useless_conversion.rs", "line": 32 }, "group": "clippy::complexity", @@ -5476,7 +5544,7 @@ { "id": "useless_vec", "id_span": { - "path": "clippy_lints/src/vec.rs", + "path": "src/vec.rs", "line": 36 }, "group": "clippy::perf", @@ -5489,7 +5557,7 @@ { "id": "vec_init_then_push", "id_span": { - "path": "clippy_lints/src/vec_init_then_push.rs", + "path": "src/vec_init_then_push.rs", "line": 32 }, "group": "clippy::perf", @@ -5502,7 +5570,7 @@ { "id": "vec_resize_to_zero", "id_span": { - "path": "clippy_lints/src/vec_resize_to_zero.rs", + "path": "src/vec_resize_to_zero.rs", "line": 24 }, "group": "clippy::correctness", @@ -5515,7 +5583,7 @@ { "id": "verbose_file_reads", "id_span": { - "path": "clippy_lints/src/verbose_file_reads.rs", + "path": "src/verbose_file_reads.rs", "line": 29 }, "group": "clippy::restriction", @@ -5528,7 +5596,7 @@ { "id": "wildcard_dependencies", "id_span": { - "path": "clippy_lints/src/wildcard_dependencies.rs", + "path": "src/wildcard_dependencies.rs", "line": 24 }, "group": "clippy::cargo", @@ -5541,27 +5609,33 @@ { "id": "enum_glob_use", "id_span": { - "path": "clippy_lints/src/wildcard_imports.rs", + "path": "src/wildcard_imports.rs", "line": 32 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "wildcard_imports", "id_span": { - "path": "clippy_lints/src/wildcard_imports.rs", + "path": "src/wildcard_imports.rs", "line": 83 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "println_empty_string", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 33 }, "group": "clippy::style", @@ -5574,7 +5648,7 @@ { "id": "print_with_newline", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 57 }, "group": "clippy::style", @@ -5587,7 +5661,7 @@ { "id": "print_stdout", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 75 }, "group": "clippy::restriction", @@ -5600,7 +5674,7 @@ { "id": "print_stderr", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 93 }, "group": "clippy::restriction", @@ -5613,7 +5687,7 @@ { "id": "use_debug", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 110 }, "group": "clippy::restriction", @@ -5626,17 +5700,20 @@ { "id": "print_literal", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 133 }, "group": "clippy::style", "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "writeln_empty_string", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 156 }, "group": "clippy::style", @@ -5649,7 +5726,7 @@ { "id": "write_with_newline", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 182 }, "group": "clippy::style", @@ -5662,17 +5739,20 @@ { "id": "write_literal", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 207 }, "group": "clippy::style", "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "zero_divided_by_zero", "id_span": { - "path": "clippy_lints/src/zero_div_zero.rs", + "path": "src/zero_div_zero.rs", "line": 23 }, "group": "clippy::complexity", @@ -5685,7 +5765,7 @@ { "id": "zero_sized_map_values", "id_span": { - "path": "clippy_lints/src/zero_sized_map_values.rs", + "path": "src/zero_sized_map_values.rs", "line": 37 }, "group": "clippy::pedantic", -- cgit 1.4.1-3-g733a5 From 4fc960301bcd4370bfc2238415c7ba3d2d8f9312 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 10 Mar 2021 23:04:12 +0100 Subject: Metadata collection: Rounding up the implementation --- .gitignore | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 152 +- clippy_utils/src/paths.rs | 6 +- metadata_collection.json | 5778 -------------------- tests/dogfood.rs | 4 - .../track_applicability_value.rs | 50 - 6 files changed, 88 insertions(+), 5904 deletions(-) delete mode 100644 metadata_collection.json delete mode 100644 tests/ui-internal/metadata-collector/track_applicability_value.rs (limited to 'clippy_lints/src') diff --git a/.gitignore b/.gitignore index 565327a7f83..523bab18828 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ out # gh pages docs util/gh-pages/lints.json -# **/metadata_collection.json +**/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 0425689b0dd..37e90bf8686 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,34 +7,21 @@ //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) -//! -//! The metadata currently contains: -//! - [x] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) -//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) -//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) - -// # Applicability -// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-28: 4x weird emission forwarding -// - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name -// - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from -// function. -// - See clippy_lints/src/methods/mod.rs@lint_binary_expr_with_method_call -// - TODO xFrednet 2021-02-28: 2x lint from local from function call -// - See clippy_lints/src/misc.rs@check_binary -// - TODO xFrednet 2021-02-28: 2x lint from local from method call -// - See clippy_lints/src/non_copy_const.rs@lint + // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, def::DefKind}; +use rustc_hir::{ + self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, +}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; -use serde::Serialize; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use std::collections::BinaryHeap; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -47,7 +34,7 @@ use crate::utils::{ /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../metadata_collection.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; @@ -107,7 +94,7 @@ declare_clippy_lint! { /// } /// ``` pub INTERNAL_METADATA_COLLECTOR, - internal, + internal_warn, "A busy bee collection metadata about lints" } @@ -116,7 +103,10 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone, Default)] pub struct MetadataCollector { - lints: Vec, + /// All collected lints + /// + /// We use a Heap here to have the lints added in alphabetic order in the export + lints: BinaryHeap, applicability_into: FxHashMap, } @@ -124,6 +114,8 @@ impl Drop for MetadataCollector { /// You might ask: How hacky is this? /// My answer: YES fn drop(&mut self) { + // The metadata collector gets dropped twice, this makes sure that we only write + // when the list is full if self.lints.is_empty() { return; } @@ -131,7 +123,8 @@ impl Drop for MetadataCollector { let mut applicability_info = std::mem::take(&mut self.applicability_into); // Mapping the final data - self.lints + let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); + lints .iter_mut() .for_each(|x| x.applicability = applicability_info.remove(&x.id)); @@ -140,11 +133,11 @@ impl Drop for MetadataCollector { fs::remove_file(OUTPUT_FILE).unwrap(); } let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); - writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); + writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct LintMetadata { id: String, id_span: SerializableSpan, @@ -155,6 +148,24 @@ struct LintMetadata { applicability: Option, } +// impl Ord for LintMetadata { +// fn cmp(&self, other: &Self) -> Ordering { +// self.id.cmp(&other.id) +// } +// } +// +// impl PartialOrd for LintMetadata { +// fn partial_cmp(&self, other: &Self) -> Option { +// Some(self.cmp(other)) +// } +// } +// +// impl PartialEq for LintMetadata { +// fn eq(&self, other: &Self) -> bool { +// self.id == other.id +// } +// } + impl LintMetadata { fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { Self { @@ -167,7 +178,7 @@ impl LintMetadata { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct SerializableSpan { path: String, line: usize, @@ -194,25 +205,30 @@ impl SerializableSpan { } } -#[derive(Debug, Clone, Default, Serialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)] struct ApplicabilityInfo { /// Indicates if any of the lint emissions uses multiple spans. This is related to /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can /// currently not be applied automatically. - is_multi_suggestion: bool, - applicability: Option, -} - -#[allow(dead_code)] -fn log_to_file(msg: &str) { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open("metadata-lint.log") - .unwrap(); - - write!(file, "{}", msg).unwrap(); + is_multi_part_suggestion: bool, + applicability: Option, +} + +impl Serialize for ApplicabilityInfo { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let index = self.applicability.unwrap_or_default(); + + let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?; + s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?; + s.serialize_field( + "applicability", + &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX], + )?; + s.end() + } } impl<'hir> LateLintPass<'hir> for MetadataCollector { @@ -266,16 +282,20 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ``` fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { if let Some(args) = match_lint_emission(cx, expr) { - let mut emission_info = extract_complex_emission_info(cx, args); + let mut emission_info = extract_emission_info(cx, args); if emission_info.is_empty() { - lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + // See: + // - src/misc.rs:734:9 + // - src/methods/mod.rs:3545:13 + // - src/methods/mod.rs:3496:13 + // We are basically unable to resolve the lint name it self. return; } for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; - app_info.is_multi_suggestion = is_multi_part; + app_info.is_multi_part_suggestion = is_multi_part; } } } @@ -289,7 +309,7 @@ fn sym_to_string(sym: Symbol) -> String { } fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option { - extract_attr_docs(item).or_else(|| { + extract_attr_docs(cx, item).or_else(|| { lint_collection_error_item(cx, item, "could not collect the lint documentation"); None }) @@ -305,8 +325,10 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option) -> Option { - item.attrs +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { + cx.tcx + .hir() + .attrs(item.hir_id()) .iter() .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string())) .reduce(|mut acc, sym| { @@ -357,15 +379,6 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s ); } -fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { - span_lint( - cx, - INTERNAL_METADATA_COLLECTOR, - span, - &format!("Metadata collection error: {}", message), - ); -} - // ================================================================== // Applicability // ================================================================== @@ -377,11 +390,15 @@ fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } -fn extract_complex_emission_info<'hir>( +fn take_higher_applicability(a: Option, b: Option) -> Option { + a.map_or(b, |a| a.max(b.unwrap_or_default()).into()) +} + +fn extract_emission_info<'hir>( cx: &LateContext<'hir>, args: &'hir [hir::Expr<'hir>], -) -> Vec<(String, Option, bool)> { - let mut lints= Vec::new(); +) -> Vec<(String, Option, bool)> { + let mut lints = Vec::new(); let mut applicability = None; let mut multi_part = false; @@ -401,7 +418,10 @@ fn extract_complex_emission_info<'hir>( } } - lints.drain(..).map(|lint_name| (lint_name, applicability.clone(), multi_part)).collect() + lints + .drain(..) + .map(|lint_name| (lint_name, applicability, multi_part)) + .collect() } /// Resolves the possible lints that this expression could reference @@ -412,7 +432,7 @@ fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec, expr: &'hir hir::Expr<'hir>) -> Option { +fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() @@ -457,7 +477,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { if_chain! { if let ExprKind::Path(qpath) = &expr.kind; if let QPath::Resolved(_, path) = qpath; - + let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr)); if match_type(self.cx, expr_ty, &paths::LINT); then { @@ -492,15 +512,11 @@ impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { } fn add_new_index(&mut self, new_index: usize) { - self.applicability_index = self - .applicability_index - .map_or(new_index, |old_index| old_index.min(new_index)) - .into(); + self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index)); } - fn complete(self) -> Option { + fn complete(self) -> Option { self.applicability_index - .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 1f19724ad25..7c7bb9b02b1 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -9,10 +9,10 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; #[cfg(feature = "metadata-collector-lint")] pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "MachineApplicable"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "Unspecified"], + ["rustc_lint_defs", "Applicability", "HasPlaceholders"], + ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], + ["rustc_lint_defs", "Applicability", "MachineApplicable"], ]; #[cfg(feature = "metadata-collector-lint")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; diff --git a/metadata_collection.json b/metadata_collection.json deleted file mode 100644 index f6f72ffa5fa..00000000000 --- a/metadata_collection.json +++ /dev/null @@ -1,5778 +0,0 @@ -[ - { - "id": "approx_constant", - "id_span": { - "path": "src/approx_const.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for floating point literals that approximate constants which are defined in\n [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\n or\n [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\n respectively, suggesting to use the predefined constant.\n\n **Why is this bad?** Usually, the definition in the standard library is more\n precise than what people come up with. If you find that your definition is\n actually more precise, please [file a Rust\n issue](https://github.com/rust-lang/rust/issues).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 3.14;\n let y = 1_f64 / x;\n ```\n Use predefined constants instead:\n ```rust\n let x = std::f32::consts::PI;\n let y = std::f64::consts::FRAC_1_PI;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "integer_arithmetic", - "id_span": { - "path": "src/arithmetic.rs", - "line": 28 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for integer arithmetic operations which could overflow or panic.\n Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\n of overflowing according to the [Rust\n Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\n or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\n attempted.\n\n **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in\n release mode. Division by zero will cause a panic in either mode. In some applications one\n wants explicitly checked, wrapping or saturating arithmetic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0;\n a + 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_arithmetic", - "id_span": { - "path": "src/arithmetic.rs", - "line": 46 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for float arithmetic.\n **Why is this bad?** For some embedded systems or kernel development, it\n can be useful to rule out floating-point numbers.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0.0;\n a + 1.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "as_conversions", - "id_span": { - "path": "src/as_conversions.rs", - "line": 42 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `as` conversions.\n Note that this lint is specialized in linting *every single* use of `as`\n regardless of whether good alternatives exist or not.\n If you want more precise lints for `as`, please consider using these separate lints:\n `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\n There is a good explanation the reason why this lint should work in this way and how it is useful\n [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).\n\n **Why is this bad?** `as` conversions will perform many kinds of\n conversions, including silently lossy conversions and dangerous coercions.\n There are cases when it makes sense to use `as`, so the lint is\n Allow by default.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a: u32;\n ...\n f(a as u16);\n ```\n\n Usually better represents the semantics you expect:\n ```rust,ignore\n f(a.try_into()?);\n ```\n or\n ```rust,ignore\n f(a.try_into().expect(\"Unexpected u16 overflow in f\"));\n ```\n\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_asm_x86_intel_syntax", - "id_span": { - "path": "src/asm_syntax.rs", - "line": 78 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of Intel x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for AT&T x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n", - "applicability": null - }, - { - "id": "inline_asm_x86_att_syntax", - "id_span": { - "path": "src/asm_syntax.rs", - "line": 114 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of AT&T x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for Intel x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n", - "applicability": null - }, - { - "id": "assertions_on_constants", - "id_span": { - "path": "src/assertions_on_constants.rs", - "line": 23 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.\n **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a\n `panic!()` or `unreachable!()`\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n assert!(false)\n assert!(true)\n const B: bool = false;\n assert!(B)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "assign_op_pattern", - "id_span": { - "path": "src/assign_ops.rs", - "line": 33 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `a = a op b` or `a = b commutative_op a` patterns.\n\n **Why is this bad?** These can be written as the shorter `a op= b`.\n\n **Known problems:** While forbidden by the spec, `OpAssign` traits may have\n implementations that differ from the regular `Op` impl.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 0;\n // ...\n // Bad\n a = a + b;\n\n // Good\n a += b;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "misrefactored_assign_op", - "id_span": { - "path": "src/assign_ops.rs", - "line": 56 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.\n **Why is this bad?** Most likely these are bugs where one meant to write `a\n op= b`.\n\n **Known problems:** Clippy cannot know for sure if `a op= a op b` should have\n been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.\n If `a op= a op b` is really the correct behaviour it should be\n written as `a = a op a op b` as it's less confusing.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 2;\n // ...\n a += a + b;\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "async_yields_async", - "id_span": { - "path": "src/async_yields_async.rs", - "line": 36 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for async blocks that yield values of types that can themselves be awaited.\n\n **Why is this bad?** An await is likely missing.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo()\n };\n }\n ```\n Use instead:\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo().await\n };\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "invalid_atomic_ordering", - "id_span": { - "path": "src/atomic_ordering.rs", - "line": 47 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of invalid atomic ordering in atomic loads/stores/exchanges/updates and\n memory fences.\n\n **Why is this bad?** Using an invalid atomic ordering\n will cause a panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::sync::atomic::{self, AtomicU8, Ordering};\n\n let x = AtomicU8::new(0);\n\n // Bad: `Release` and `AcqRel` cannot be used for `load`.\n let _ = x.load(Ordering::Release);\n let _ = x.load(Ordering::AcqRel);\n\n // Bad: `Acquire` and `AcqRel` cannot be used for `store`.\n x.store(1, Ordering::Acquire);\n x.store(2, Ordering::AcqRel);\n\n // Bad: `Relaxed` cannot be used as a fence's ordering.\n atomic::fence(Ordering::Relaxed);\n atomic::compiler_fence(Ordering::Relaxed);\n\n // Bad: `Release` and `AcqRel` are both always invalid\n // for the failure ordering (the last arg).\n let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);\n let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);\n\n // Bad: The failure ordering is not allowed to be\n // stronger than the success order, and `SeqCst` is\n // stronger than `Relaxed`.\n let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_always", - "id_span": { - "path": "src/attrs.rs", - "line": 65 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.\n\n **Why is this bad?** While there are valid uses of this annotation (and once\n you know when to use it, by all means `allow` this lint), it's a common\n newbie-mistake to pepper one's code with it.\n\n As a rule of thumb, before slapping `#[inline(always)]` on a function,\n measure if that additional function call really affects your runtime profile\n sufficiently to make up for the increase in compile time.\n\n **Known problems:** False positives, big time. This lint is meant to be\n deactivated by everyone doing serious performance work. This means having\n done the measurement.\n\n **Example:**\n ```ignore\n #[inline(always)]\n fn not_quite_hot_code(..) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "useless_attribute", - "id_span": { - "path": "src/attrs.rs", - "line": 99 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `extern crate` and `use` items annotated with lint attributes.\n\n This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n `extern crate` items with a `#[macro_use]` attribute.\n\n **Why is this bad?** Lint attributes have no effect on crate imports. Most\n likely a `!` was forgotten.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n #[deny(dead_code)]\n extern crate foo;\n #[forbid(dead_code)]\n use foo::bar;\n\n // Ok\n #[allow(unused_imports)]\n use foo::baz;\n #[allow(unused_imports)]\n #[macro_use]\n extern crate baz;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "deprecated_semver", - "id_span": { - "path": "src/attrs.rs", - "line": 118 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `#[deprecated]` annotations with a `since` field that is not a valid semantic version.\n\n **Why is this bad?** For checking the version of the deprecation, it must be\n a valid semver. Failing that, the contained information is useless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n #[deprecated(since = \"forever\")]\n fn something_else() { /* ... */ }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "empty_line_after_outer_attr", - "id_span": { - "path": "src/attrs.rs", - "line": 153 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for empty lines after outer attributes\n **Why is this bad?**\n Most likely the attribute was meant to be an inner attribute using a '!'.\n If it was meant to be an outer attribute, then the following item\n should not be separated by empty lines.\n\n **Known problems:** Can cause false positives.\n\n From the clippy side it's difficult to detect empty lines between an attributes and the\n following item because empty lines and comments are not part of the AST. The parsing\n currently works for basic cases but is not perfect.\n\n **Example:**\n ```rust\n // Good (as inner attribute)\n #![allow(dead_code)]\n\n fn this_is_fine() { }\n\n // Bad\n #[allow(dead_code)]\n\n fn not_quite_good_code() { }\n\n // Good (as outer attribute)\n #[allow(dead_code)]\n fn this_is_fine_too() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "blanket_clippy_restriction_lints", - "id_span": { - "path": "src/attrs.rs", - "line": 176 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.\n **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.\n These lints should only be enabled on a lint-by-lint basis and with careful consideration.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n #![deny(clippy::restriction)]\n ```\n\n Good:\n ```rust\n #![deny(clippy::as_conversions)]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "deprecated_cfg_attr", - "id_span": { - "path": "src/attrs.rs", - "line": 205 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it with `#[rustfmt::skip]`.\n\n **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))\n are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.\n\n **Known problems:** This lint doesn't detect crate level inner attributes, because they get\n processed before the PreExpansionPass lints get executed. See\n [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg_attr(rustfmt, rustfmt_skip)]\n fn main() { }\n ```\n\n Good:\n ```rust\n #[rustfmt::skip]\n fn main() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mismatched_target_os", - "id_span": { - "path": "src/attrs.rs", - "line": 238 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for cfg attributes having operating systems used in target family position.\n **Why is this bad?** The configuration option will not be recognised and the related item will not be included\n by the conditional compilation engine.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg(linux)]\n fn conditional() { }\n ```\n\n Good:\n ```rust\n #[cfg(target_os = \"linux\")]\n fn conditional() { }\n ```\n\n Or:\n ```rust\n #[cfg(unix)]\n fn conditional() { }\n ```\n Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "await_holding_lock", - "id_span": { - "path": "src/await_holding_invalid.rs", - "line": 47 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to await while holding a non-async-aware MutexGuard.\n\n **Why is this bad?** The Mutex types found in std::sync and parking_lot\n are not designed to operate in an async context across await points.\n\n There are two potential solutions. One is to use an asynx-aware Mutex\n type. Many asynchronous foundation crates provide such a Mutex type. The\n other solution is to ensure the mutex is unlocked before calling await,\n either by introducing a scope or an explicit call to Drop::drop.\n\n **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).\n\n **Example:**\n\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n let guard = x.lock().unwrap();\n *guard += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n {\n let guard = x.lock().unwrap();\n *guard += 1;\n }\n bar.await;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "await_holding_refcell_ref", - "id_span": { - "path": "src/await_holding_invalid.rs", - "line": 86 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.\n\n **Why is this bad?** `RefCell` refs only check for exclusive mutable access\n at runtime. Holding onto a `RefCell` ref across an `await` suspension point\n risks panics from a mutable ref shared while other refs are outstanding.\n\n **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).\n\n **Example:**\n\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n let mut y = x.borrow_mut();\n *y += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n {\n let mut y = x.borrow_mut();\n *y += 1;\n }\n bar.await;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "bad_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 44 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for incompatible bit masks in comparisons.\n The formula for detecting if an expression of the type `_ m\n c` (where `` is one of {`&`, `|`} and `` is one of\n {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\n table:\n\n |Comparison |Bit Op|Example |is always|Formula |\n |------------|------|------------|---------|----------------------|\n |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |\n\n **Why is this bad?** If the bits that the comparison cares about are always\n set to zero or one by the bit mask, the comparison is constant `true` or\n `false` (depending on mask, compared value, and operators).\n\n So the code is actively misleading, and the only reason someone would write\n this intentionally is to win an underhanded Rust contest or create a\n test-case for this lint.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n if (x & 1 == 2) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ineffective_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 73 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for bit masks in comparisons which can be removed without changing the outcome. The basic structure can be seen in the\n following table:\n\n |Comparison| Bit Op |Example |equals |\n |----------|---------|-----------|-------|\n |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|\n\n **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),\n but still a bit misleading, because the bit mask is ineffective.\n\n **Known problems:** False negatives: This lint will only match instances\n where we have figured out the math (which is for a power-of-two compared\n value). This means things like `x | 1 >= 7` (which would be better written\n as `x >= 6`) will not be reported (but bit masks like this are fairly\n uncommon).\n\n **Example:**\n ```rust\n # let x = 1;\n if (x | 1 > 3) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "verbose_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 92 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for bit masks that can be replaced by a call to `trailing_zeros`\n\n **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15\n == 0`\n\n **Known problems:** llvm generates better code for `x & 15 == 0` on x86\n\n **Example:**\n ```rust\n # let x = 1;\n if x & 0b1111 == 0 { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "blacklisted_name", - "id_span": { - "path": "src/blacklisted_name.rs", - "line": 20 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of blacklisted names for variables, such as `foo`.\n\n **Why is this bad?** These names are usually placeholder names and should be\n avoided.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo = 3.14;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "blocks_in_if_conditions", - "id_span": { - "path": "src/blocks_in_if_conditions.rs", - "line": 42 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `if` conditions that use blocks containing an expression, statements or conditions that use closures with blocks.\n\n **Why is this bad?** Style, using blocks in the condition makes it hard to read.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n // Bad\n if { true } { /* ... */ }\n\n // Good\n if true { /* ... */ }\n ```\n\n // or\n\n ```rust\n # fn somefunc() -> bool { true };\n // Bad\n if { let x = somefunc(); x } { /* ... */ }\n\n // Good\n let res = { let x = somefunc(); x };\n if res { /* ... */ }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "nonminimal_bool", - "id_span": { - "path": "src/booleans.rs", - "line": 31 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for boolean expressions that can be written more concisely.\n\n **Why is this bad?** Readability of boolean expressions suffers from\n unnecessary duplication.\n\n **Known problems:** Ignores short circuiting behavior of `||` and\n `&&`. Ignores `|`, `&` and `^`.\n\n **Example:**\n ```ignore\n if a && true // should be: if a\n if !(a == b) // should be: if a != b\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "logic_bug", - "id_span": { - "path": "src/booleans.rs", - "line": 49 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for boolean expressions that contain terminals that can be eliminated.\n\n **Why is this bad?** This is most likely a logic bug.\n\n **Known problems:** Ignores short circuiting behavior.\n\n **Example:**\n ```ignore\n if a && b || a { ... }\n ```\n The `b` is unnecessary, the expression is equivalent to `if a`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "naive_bytecount", - "id_span": { - "path": "src/bytecount.rs", - "line": 30 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "cargo_common_metadata", - "id_span": { - "path": "src/cargo_common_metadata.rs", - "line": 49 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks to see if all common metadata is defined in `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata\n\n **Why is this bad?** It will be more difficult for users to discover the\n purpose of the crate, and key information related to it.\n\n **Known problems:** None.\n\n **Example:**\n ```toml\n # This `Cargo.toml` is missing an authors field:\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n\n Should include an authors field like:\n\n ```toml\n # This `Cargo.toml` includes all common metadata\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n authors = [\"Someone \"]\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "case_sensitive_file_extension_comparisons", - "id_span": { - "path": "src/case_sensitive_file_extension_comparisons.rs", - "line": 34 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to `ends_with` with possible file extensions\n and suggests to use a case-insensitive approach instead.\n\n **Why is this bad?**\n `ends_with` is case-sensitive and may not detect files with a valid extension.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.ends_with(\".rs\")\n }\n ```\n Use instead:\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case(\"rs\")) == Some(true)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "checked_conversions", - "id_span": { - "path": "src/checked_conversions.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit bounds checking when casting.\n **Why is this bad?** Reduces the readability of statements & is error prone.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let foo: u32 = 5;\n # let _ =\n foo <= i32::MAX as u32\n # ;\n ```\n\n Could be written:\n\n ```rust\n # use std::convert::TryFrom;\n # let foo = 1;\n # let _ =\n i32::try_from(foo).is_ok()\n # ;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cognitive_complexity", - "id_span": { - "path": "src/cognitive_complexity.rs", - "line": 24 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for methods with high cognitive complexity.\n **Why is this bad?** Methods of high cognitive complexity tend to be hard to\n both read and maintain. Also LLVM will tend to optimize small methods better.\n\n **Known problems:** Sometimes it's hard to find a way to reduce the\n complexity.\n\n **Example:** No. You'll see it when you get the warning.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "collapsible_if", - "id_span": { - "path": "src/collapsible_if.rs", - "line": 50 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for nested `if` statements which can be collapsed by `&&`-combining their conditions.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x {\n if y {\n …\n }\n }\n\n ```\n\n Should be written:\n\n ```rust.ignore\n if x && y {\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "collapsible_else_if", - "id_span": { - "path": "src/collapsible_if.rs", - "line": 85 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for collapsible `else { if ... }` expressions that can be collapsed to `else if ...`.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n\n if x {\n …\n } else {\n if y {\n …\n }\n }\n ```\n\n Should be written:\n\n ```rust.ignore\n if x {\n …\n } else if y {\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "collapsible_match", - "id_span": { - "path": "src/collapsible_match.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together without adding any branches.\n\n Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\n cases where merging would most likely make the code more readable.\n\n **Why is this bad?** It is unnecessarily verbose and complex.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(n) => match n {\n Ok(n) => n,\n _ => return,\n }\n None => return,\n };\n }\n ```\n Use instead:\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(Ok(n)) => n,\n _ => return,\n };\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "comparison_chain", - "id_span": { - "path": "src/comparison_chain.rs", - "line": 49 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks comparison chains written with `if` that can be rewritten with `match` and `cmp`.\n\n **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get\n repetitive\n\n **Known problems:** The match statement may be slower due to the compiler\n not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)\n\n **Example:**\n ```rust,ignore\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n if x > y {\n a()\n } else if x < y {\n b()\n } else {\n c()\n }\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n use std::cmp::Ordering;\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n match x.cmp(&y) {\n Ordering::Greater => a(),\n Ordering::Less => b(),\n Ordering::Equal => c()\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ifs_same_cond", - "id_span": { - "path": "src/copies.rs", - "line": 33 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for consecutive `if`s with the same condition.\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if a == b {\n …\n } else if a == b {\n …\n }\n ```\n\n Note that this lint ignores all conditions with a function call as it could\n have side effects:\n\n ```ignore\n if foo() {\n …\n } else if foo() { // not linted\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "same_functions_in_if_condition", - "id_span": { - "path": "src/copies.rs", - "line": 80 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for consecutive `if`s with the same function call.\n **Why is this bad?** This is probably a copy & paste error.\n Despite the fact that function can have side effects and `if` works as\n intended, such an approach is implicit and can be considered a \"code smell\".\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if foo() == bar {\n …\n } else if foo() == bar {\n …\n }\n ```\n\n This probably should be:\n ```ignore\n if foo() == bar {\n …\n } else if foo() == baz {\n …\n }\n ```\n\n or if the original code was not a typo and called function mutates a state,\n consider move the mutation out of the `if` condition to avoid similarity to\n a copy & paste error:\n\n ```ignore\n let first = foo();\n if first == bar {\n …\n } else {\n let second = foo();\n if second == bar {\n …\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_same_then_else", - "id_span": { - "path": "src/copies.rs", - "line": 101 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `if/else` with the same body as the *then* part and the *else* part.\n\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n let foo = if … {\n 42\n } else {\n 42\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "copy_iterator", - "id_span": { - "path": "src/copy_iterator.rs", - "line": 27 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for types that implement `Copy` as well as `Iterator`.\n\n **Why is this bad?** Implicit copies can be confusing when working with\n iterator combinators.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[derive(Copy, Clone)]\n struct Countdown(u8);\n\n impl Iterator for Countdown {\n // ...\n }\n\n let a: Vec<_> = my_iterator.take(1).collect();\n let b: Vec<_> = my_iterator.collect();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "create_dir", - "id_span": { - "path": "src/create_dir.rs", - "line": 24 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.\n **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n std::fs::create_dir(\"foo\");\n ```\n Use instead:\n ```rust\n std::fs::create_dir_all(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "dbg_macro", - "id_span": { - "path": "src/dbg_macro.rs", - "line": 25 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of dbg!() macro.\n **Why is this bad?** `dbg!` macro is intended as a debugging tool. It\n should not be in version control.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n dbg!(true)\n\n // Good\n true\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "default_trait_access", - "id_span": { - "path": "src/default.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for literal calls to `Default::default()`.\n **Why is this bad?** It's more clear to the reader to use the name of the type whose default is\n being gotten than the generic `Default`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let s: String = Default::default();\n\n // Good\n let s = String::default();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "field_reassign_with_default", - "id_span": { - "path": "src/default.rs", - "line": 63 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for immediate reassignment of fields initialized with Default::default().\n\n **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).\n\n **Known problems:** Assignments to patterns that are of tuple type are not linted.\n\n **Example:**\n Bad:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let mut a: A = Default::default();\n a.i = 42;\n ```\n Use instead:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let a = A {\n i: 42,\n .. Default::default()\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "default_numeric_fallback", - "id_span": { - "path": "src/default_numeric_fallback.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference.\n\n Default numeric fallback means that if numeric types have not yet been bound to concrete\n types at the end of type inference, then integer type is bound to `i32`, and similarly\n floating type is bound to `f64`.\n\n See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.\n\n **Why is this bad?** For those who are very careful about types, default numeric fallback\n can be a pitfall that cause unexpected runtime behavior.\n\n **Known problems:** This lint can only be allowed at the function level or above.\n\n **Example:**\n ```rust\n let i = 10;\n let f = 1.23;\n ```\n\n Use instead:\n ```rust\n let i = 10i32;\n let f = 1.23f64;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "explicit_deref_methods", - "id_span": { - "path": "src/dereference.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.\n **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise,\n when not part of a method chain.\n\n **Example:**\n ```rust\n use std::ops::Deref;\n let a: &mut String = &mut String::from(\"foo\");\n let b: &str = a.deref();\n ```\n Could be written as:\n ```rust\n let a: &mut String = &mut String::from(\"foo\");\n let b = &*a;\n ```\n\n This lint excludes\n ```rust,ignore\n let _ = d.unwrap().deref();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "derive_hash_xor_eq", - "id_span": { - "path": "src/derive.rs", - "line": 42 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for deriving `Hash` but implementing `PartialEq` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `HashMap`) so it’s probably a bad idea to use a\n default-generated `Hash` implementation with an explicitly defined\n `PartialEq`. In particular, the following must hold for any type:\n\n ```text\n k1 == k2 ⇒ hash(k1) == hash(k2)\n ```\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n #[derive(Hash)]\n struct Foo;\n\n impl PartialEq for Foo {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "derive_ord_xor_partial_ord", - "id_span": { - "path": "src/derive.rs", - "line": 93 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `sort`) so it’s probably a bad idea to use a\n default-generated `Ord` implementation with an explicitly defined\n `PartialOrd`. In particular, the following must hold for any type\n implementing `Ord`:\n\n ```text\n k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()\n ```\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n #[derive(Ord, PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n ...\n }\n ```\n Use instead:\n ```rust,ignore\n #[derive(PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n fn partial_cmp(&self, other: &Foo) -> Option {\n Some(self.cmp(other))\n }\n }\n\n impl Ord for Foo {\n ...\n }\n ```\n or, if you don't need a custom ordering:\n ```rust,ignore\n #[derive(Ord, PartialOrd, PartialEq, Eq)]\n struct Foo;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "expl_impl_clone_on_copy", - "id_span": { - "path": "src/derive.rs", - "line": 119 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit `Clone` implementations for `Copy` types.\n\n **Why is this bad?** To avoid surprising behaviour, these traits should\n agree and the behaviour of `Copy` cannot be overridden. In almost all\n situations a `Copy` type should have a `Clone` implementation that does\n nothing more than copy the object, which is what `#[derive(Copy, Clone)]`\n gets you.\n\n **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925\n\n **Example:**\n ```rust,ignore\n #[derive(Copy)]\n struct Foo;\n\n impl Clone for Foo {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unsafe_derive_deserialize", - "id_span": { - "path": "src/derive.rs", - "line": 153 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for deriving `serde::Deserialize` on a type that has methods using `unsafe`.\n\n **Why is this bad?** Deriving `serde::Deserialize` will create a constructor\n that may violate invariants hold by another constructor.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use serde::Deserialize;\n\n #[derive(Deserialize)]\n pub struct Foo {\n // ..\n }\n\n impl Foo {\n pub fn new() -> Self {\n // setup here ..\n }\n\n pub unsafe fn parts() -> (&str, &str) {\n // assumes invariants hold\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "disallowed_method", - "id_span": { - "path": "src/disallowed_method.rs", - "line": 46 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Denies the configured methods and functions in clippy.toml\n **Why is this bad?** Some methods are undesirable in certain contexts,\n and it's beneficial to lint for them as needed.\n\n **Known problems:** Currently, you must write each function as a\n fully-qualified path. This lint doesn't support aliases or reexported\n names; be aware that many types in `std` are actually reexports.\n\n For example, if you want to disallow `Duration::as_secs`, your clippy.toml\n configuration would look like\n `disallowed-methods = [\"core::time::Duration::as_secs\"]` and not\n `disallowed-methods = [\"std::time::Duration::as_secs\"]` as you might expect.\n\n **Example:**\n\n An example clippy.toml configuration:\n ```toml\n # clippy.toml\n disallowed-methods = [\"alloc::vec::Vec::leak\", \"std::time::Instant::now\"]\n ```\n\n ```rust,ignore\n // Example code where clippy issues a warning\n let xs = vec![1, 2, 3, 4];\n xs.leak(); // Vec::leak is disallowed in the config.\n\n let _now = Instant::now(); // Instant::now is disallowed in the config.\n ```\n\n Use instead:\n ```rust,ignore\n // Example code which does not raise clippy warning\n let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.\n xs.push(123); // Vec::push is _not_ disallowed in the config.\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "doc_markdown", - "id_span": { - "path": "src/doc.rs", - "line": 63 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the presence of `_`, `::` or camel-case words outside ticks in documentation.\n\n **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and\n camel-case probably indicates some code which should be included between\n ticks. `_` can also be used for emphasis in markdown, this lint tries to\n consider that.\n\n **Known problems:** Lots of bad docs won’t be fixed, what the lint checks\n for is limited, and there are still false positives.\n\n In addition, when writing documentation comments, including `[]` brackets\n inside a link text would trip the parser. Therfore, documenting link with\n `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec\n would fail.\n\n **Examples:**\n ```rust\n /// Do something with the foo_bar parameter. See also\n /// that::other::module::foo.\n // ^ `foo_bar` and `that::other::module::foo` should be ticked.\n fn doit(foo_bar: usize) {}\n ```\n\n ```rust\n // Link text with `[]` brackets should be written as following:\n /// Consume the array and return the inner\n /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].\n /// [SmallVec]: SmallVec\n fn main() {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_safety_doc", - "id_span": { - "path": "src/doc.rs", - "line": 97 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the doc comments of publicly visible unsafe functions and warns if there is no `# Safety` section.\n\n **Why is this bad?** Unsafe functions should document their safety\n preconditions, so that users can be sure they are using them safely.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n# type Universe = ();\n /// This function should really be documented\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n\n At least write a line about safety:\n\n ```rust\n# type Universe = ();\n /// # Safety\n ///\n /// This function should not be called before the horsemen are ready.\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_errors_doc", - "id_span": { - "path": "src/doc.rs", - "line": 126 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks the doc comments of publicly visible functions that return a `Result` type and warns if there is no `# Errors` section.\n\n **Why is this bad?** Documenting the type of errors that can be returned from a\n function can help callers write code to handle the errors appropriately.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function returns a `Result` it has an `# Errors` section in\n its doc comment:\n\n ```rust\n# use std::io;\n /// # Errors\n ///\n /// Will return `Err` if `filename` does not exist or the user does not have\n /// permission to read it.\n pub fn read(filename: String) -> io::Result {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_panics_doc", - "id_span": { - "path": "src/doc.rs", - "line": 157 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks the doc comments of publicly visible functions that may panic and warns if there is no `# Panics` section.\n\n **Why is this bad?** Documenting the scenarios in which panicking occurs\n can help callers who do not want to panic to avoid those situations.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function may panic it has a `# Panics` section in\n its doc comment:\n\n ```rust\n /// # Panics\n ///\n /// Will panic if y is 0\n pub fn divide_by(x: i32, y: i32) -> i32 {\n if y == 0 {\n panic!(\"Cannot divide by 0\")\n } else {\n x / y\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_doctest_main", - "id_span": { - "path": "src/doc.rs", - "line": 185 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `fn main() { .. }` in doctests\n **Why is this bad?** The test can be shorter (and likely more readable)\n if the `fn main()` is left implicit.\n\n **Known problems:** None.\n\n **Examples:**\n ``````rust\n /// An example of a doctest with a `main()` function\n ///\n /// # Examples\n ///\n /// ```\n /// fn main() {\n /// // this needs not be in an `fn`\n /// }\n /// ```\n fn needless_main() {\n unimplemented!();\n }\n ``````\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "double_comparisons", - "id_span": { - "path": "src/double_comparison.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for double comparisons that could be simplified to a single expression.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 2;\n if x == y || x < y {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 2;\n if x <= y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "double_parens", - "id_span": { - "path": "src/double_parens.rs", - "line": 35 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for unnecessary double parentheses.\n **Why is this bad?** This makes code harder to read and might indicate a\n mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn simple_double_parens() -> i32 {\n ((0))\n }\n\n // Good\n fn simple_no_parens() -> i32 {\n 0\n }\n\n // or\n\n # fn foo(bar: usize) {}\n // Bad\n foo((0));\n\n // Good\n foo(0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "drop_ref", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 26 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::drop` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `drop` on a reference will only drop the\n reference itself, which is a no-op. It will not call the `drop` method (from\n the `Drop` trait implementation) on the underlying referenced value, which\n is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n let mut lock_guard = mutex.lock();\n std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex\n // still locked\n operation_that_requires_mutex_to_be_unlocked();\n ```\n", - "applicability": null - }, - { - "id": "forget_ref", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 47 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::forget` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `forget` on a reference will only forget the\n reference itself, which is a no-op. It will not forget the underlying\n referenced\n value, which is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = Box::new(1);\n std::mem::forget(&x) // Should have been forget(x), x will still be dropped\n ```\n", - "applicability": null - }, - { - "id": "drop_copy", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 68 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::drop` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::drop` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the\n value will be copied and moved into the function on invocation.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::drop(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", - "applicability": null - }, - { - "id": "forget_copy", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 95 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::forget` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::forget` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the\n value will be copied and moved into the function on invocation.\n\n An alternative, but also valid, explanation is that Copy types do not\n implement\n the Drop trait, which means they have no destructors. Without a destructor,\n there\n is nothing for `std::mem::forget` to ignore.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::forget(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", - "applicability": null - }, - { - "id": "duration_subsec", - "id_span": { - "path": "src/duration_subsec.rs", - "line": 34 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calculation of subsecond microseconds or milliseconds from other `Duration` methods.\n\n **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or\n `Duration::subsec_millis()` than to calculate them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::time::Duration;\n let dur = Duration::new(5, 0);\n\n // Bad\n let _micros = dur.subsec_nanos() / 1_000;\n let _millis = dur.subsec_nanos() / 1_000_000;\n\n // Good\n let _micros = dur.subsec_micros();\n let _millis = dur.subsec_millis();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "else_if_without_else", - "id_span": { - "path": "src/else_if_without_else.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of if expressions with an `else if` branch, but without a final `else` branch.\n\n **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n }\n ```\n\n Could be written:\n\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n } else {\n // We don't care about zero.\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "empty_enum", - "id_span": { - "path": "src/empty_enum.rs", - "line": 38 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `enum`s with no variants.\n As of this writing, the `never_type` is still a\n nightly-only experimental API. Therefore, this lint is only triggered\n if the `never_type` is enabled.\n\n **Why is this bad?** If you want to introduce a type which\n can't be instantiated, you should use `!` (the primitive type \"never\"),\n or a wrapper around it, because `!` has more extensive\n compiler support (type inference, etc...) and wrappers\n around it are the conventional way to define an uninhabited type.\n For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)\n\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n enum Test {}\n ```\n\n Good:\n ```rust\n #![feature(never_type)]\n\n struct Test(!);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_entry", - "id_span": { - "path": "src/entry.rs", - "line": 48 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` or `BTreeMap`.\n\n **Why is this bad?** Using `entry` is more efficient.\n\n **Known problems:** Some false negatives, eg.:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let v = 1;\n # let k = 1;\n if !map.contains_key(&k) {\n map.insert(k.clone(), v);\n }\n ```\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n if !map.contains_key(&k) {\n map.insert(k, v);\n }\n ```\n can both be rewritten as:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n map.entry(k).or_insert(v);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "enum_clike_unportable_variant", - "id_span": { - "path": "src/enum_clike.rs", - "line": 31 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for C-like enumerations that are `repr(isize/usize)` and have values that don't fit into an `i32`.\n\n **Why is this bad?** This will truncate the variant value on 32 bit\n architectures, but works fine on 64 bit.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # #[cfg(target_pointer_width = \"64\")]\n #[repr(usize)]\n enum NonPortable {\n X = 0x1_0000_0000,\n Y = 0,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "enum_variant_names", - "id_span": { - "path": "src/enum_variants.rs", - "line": 36 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", - "applicability": null - }, - { - "id": "pub_enum_variant_names", - "id_span": { - "path": "src/enum_variants.rs", - "line": 66 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Detects public enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Public enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n pub enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", - "applicability": null - }, - { - "id": "module_name_repetitions", - "id_span": { - "path": "src/enum_variants.rs", - "line": 91 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Detects type names that are prefixed or suffixed by the containing module's name.\n\n **Why is this bad?** It requires the user to type the module name twice.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n mod cake {\n struct BlackForestCake;\n }\n ```\n Could be written as:\n ```rust\n mod cake {\n struct BlackForest;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "module_inception", - "id_span": { - "path": "src/enum_variants.rs", - "line": 121 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for modules that have the same name as their parent module\n\n **Why is this bad?** A typical beginner mistake is to have `mod foo;` and\n again `mod foo { ..\n }` in `foo.rs`.\n The expectation is that items inside the inner `mod foo { .. }` are then\n available\n through `foo::x`, but they are only available through\n `foo::foo::x`.\n If this is done on purpose, it would be better to choose a more\n representative module name.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // lib.rs\n mod foo;\n // foo.rs\n mod foo {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "eq_op", - "id_span": { - "path": "src/eq_op.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for equal operands to comparison, logical and bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n `||`, `&`, `|`, `^`, `-` and `/`).\n\n **Why is this bad?** This is usually just a typo or a copy and paste error.\n\n **Known problems:** False negatives: We had some false positives regarding\n calls (notably [racer](https://github.com/phildawes/racer) had one instance\n of `x.pop() && x.pop()`), so we removed matching any function or method\n calls. We may introduce a list of known pure functions in the future.\n\n **Example:**\n ```rust\n # let x = 1;\n if x + 1 == x + 1 {}\n ```\n or\n ```rust\n # let a = 3;\n # let b = 4;\n assert_eq!(a, a);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "op_ref", - "id_span": { - "path": "src/eq_op.rs", - "line": 56 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound\n and suggests to dereference the other argument instead\n\n **Why is this bad?** It is more idiomatic to dereference the other argument.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n // Bad\n &x == y\n\n // Good\n x == *y\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "erasing_op", - "id_span": { - "path": "src/erasing_op.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for erasing operations, e.g., `x * 0`.\n **Why is this bad?** The whole expression can be replaced by zero.\n This is most likely not the intended outcome and should probably be\n corrected\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1;\n 0 / x;\n 0 * x;\n x & 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "boxed_local", - "id_span": { - "path": "src/escape.rs", - "line": 43 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usage of `Box` where an unboxed `T` would work fine.\n\n **Why is this bad?** This is an unnecessary allocation, and bad for\n performance. It is only necessary to allocate if you wish to move the box\n into something.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(bar: usize) {}\n // Bad\n let x = Box::new(1);\n foo(*x);\n println!(\"{}\", *x);\n\n // Good\n let x = 1;\n foo(x);\n println!(\"{}\", x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_closure", - "id_span": { - "path": "src/eta_reduction.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for closures which just call another function where the function can be called directly. `unsafe` functions or calls where types\n get adjusted are ignored.\n\n **Why is this bad?** Needlessly creating a closure adds code for no benefit\n and gives the optimizer more work.\n\n **Known problems:** If creating the closure inside the closure has a side-\n effect then moving the closure creation out will change when that side-\n effect runs.\n See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.\n\n **Example:**\n ```rust,ignore\n // Bad\n xs.map(|x| foo(x))\n\n // Good\n xs.map(foo)\n ```\n where `foo(_)` is a plain function that takes the exact argument type of\n `x`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_closure_for_method_calls", - "id_span": { - "path": "src/eta_reduction.rs", - "line": 61 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for closures which only invoke a method on the closure argument and can be replaced by referencing the method directly.\n\n **Why is this bad?** It's unnecessary to create the closure.\n\n **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),\n [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),\n [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)\n\n\n **Example:**\n ```rust,ignore\n Some('a').map(|s| s.to_uppercase());\n ```\n may be rewritten as\n ```rust,ignore\n Some('a').map(char::to_uppercase);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "eval_order_dependence", - "id_span": { - "path": "src/eval_order_dependence.rs", - "line": 38 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation\n order of sub-expressions.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Code which intentionally depends on the evaluation\n order, or which is correct for any evaluation order.\n\n **Example:**\n ```rust\n let mut x = 0;\n\n // Bad\n let a = {\n x = 1;\n 1\n } + x;\n // Unclear whether a is 1 or 2.\n\n // Good\n let tmp = {\n x = 1;\n 1\n };\n let a = tmp + x;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "diverging_sub_expression", - "id_span": { - "path": "src/eval_order_dependence.rs", - "line": 62 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for diverging calls that are not match arms or statements.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Someone might want to use `some_bool || panic!()` as a\n shorthand.\n\n **Example:**\n ```rust,no_run\n # fn b() -> bool { true }\n # fn c() -> bool { true }\n let a = b() || panic!() || c();\n // `c()` is dead, `panic!()` is only called if `b()` returns `false`\n let x = (a, b, c, panic!());\n // can simply be replaced by `panic!()`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "struct_excessive_bools", - "id_span": { - "path": "src/excessive_bools.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for excessive use of bools in structs.\n\n **Why is this bad?** Excessive bools in a struct\n is often a sign that it's used as a state machine,\n which is much better implemented as an enum.\n If it's not the case, excessive bools usually benefit\n from refactoring into two-variant enums for better\n readability and API.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n struct S {\n is_pending: bool,\n is_processing: bool,\n is_finished: bool,\n }\n ```\n\n Good:\n ```rust\n enum S {\n Pending,\n Processing,\n Finished,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_params_excessive_bools", - "id_span": { - "path": "src/excessive_bools.rs", - "line": 78 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for excessive use of bools in function definitions.\n\n **Why is this bad?** Calls to such functions\n are confusing and error prone, because it's\n hard to remember argument order and you have\n no type system support to back you up. Using\n two-variant enums instead of bools often makes\n API easier to use.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust,ignore\n fn f(is_round: bool, is_hot: bool) { ... }\n ```\n\n Good:\n ```rust,ignore\n enum Shape {\n Round,\n Spiky,\n }\n\n enum Temperature {\n Hot,\n IceCold,\n }\n\n fn f(shape: Shape, temperature: Temperature) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "exhaustive_enums", - "id_span": { - "path": "src/exhaustive_items.rs", - "line": 34 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "exhaustive_structs", - "id_span": { - "path": "src/exhaustive_items.rs", - "line": 64 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "exit", - "id_span": { - "path": "src/exit.rs", - "line": 20 - }, - "group": "clippy::restriction", - "docs": " **What it does:** `exit()` terminates the program and doesn't provide a stack trace.\n\n **Why is this bad?** Ideally a program is terminated by finishing\n the main function.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n std::process::exit(0)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "explicit_write", - "id_span": { - "path": "src/explicit_write.rs", - "line": 25 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `write!()` / `writeln()!` which can be replaced with `(e)print!()` / `(e)println!()`\n\n **Why is this bad?** Using `(e)println! is clearer and more concise\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::io::Write;\n # let bar = \"furchtbar\";\n // this would be clearer as `eprintln!(\"foo: {:?}\", bar);`\n writeln!(&mut std::io::stderr(), \"foo: {:?}\", bar).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fallible_impl_from", - "id_span": { - "path": "src/fallible_impl_from.rs", - "line": 45 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`\n **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo(i32);\n\n // Bad\n impl From for Foo {\n fn from(s: String) -> Self {\n Foo(s.parse().unwrap())\n }\n }\n ```\n\n ```rust\n // Good\n struct Foo(i32);\n\n use std::convert::TryFrom;\n impl TryFrom for Foo {\n type Error = ();\n fn try_from(s: String) -> Result {\n if let Ok(parsed) = s.parse() {\n Ok(Foo(parsed))\n } else {\n Err(())\n }\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_equality_without_abs", - "id_span": { - "path": "src/float_equality_without_abs.rs", - "line": 37 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.\n\n **Why is this bad?** The code without `.abs()` is more likely to have a bug.\n\n **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is\n technically unneccessary. However, it will make the code more robust and doesn't have any\n large performance implications. If the abs call was deliberately left out for performance\n reasons, it is probably better to state this explicitly in the code, which then can be done\n with an allow.\n\n **Example:**\n\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b) < f32::EPSILON\n }\n ```\n Use instead:\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b).abs() < f32::EPSILON\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "excessive_precision", - "id_span": { - "path": "src/float_literal.rs", - "line": 30 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for float literals with a precision greater than that supported by the underlying type.\n\n **Why is this bad?** Rust will truncate the literal silently.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let v: f32 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789\n\n // Good\n let v: f64 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789_9\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "lossy_float_literal", - "id_span": { - "path": "src/float_literal.rs", - "line": 54 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for whole number float literals that cannot be represented as the underlying type without loss.\n\n **Why is this bad?** Rust will silently lose precision during\n conversion to a float.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _: f32 = 16_777_217.0; // 16_777_216.0\n\n // Good\n let _: f32 = 16_777_216.0;\n let _: f64 = 16_777_217.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "imprecise_flops", - "id_span": { - "path": "src/floating_point_arithmetic.rs", - "line": 45 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy\n at the cost of performance.\n\n **Why is this bad?** Negatively impacts accuracy.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let a = 3f32;\n let _ = a.powf(1.0 / 3.0);\n let _ = (1.0 + a).ln();\n let _ = a.exp() - 1.0;\n ```\n\n is better expressed as\n\n ```rust\n let a = 3f32;\n let _ = a.cbrt();\n let _ = a.ln_1p();\n let _ = a.exp_m1();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suboptimal_flops", - "id_span": { - "path": "src/floating_point_arithmetic.rs", - "line": 102 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve both\n accuracy and performance.\n\n **Why is this bad?** Negatively impacts accuracy and performance.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = (2f32).powf(a);\n let _ = E.powf(a);\n let _ = a.powf(1.0 / 2.0);\n let _ = a.log(2.0);\n let _ = a.log(10.0);\n let _ = a.log(E);\n let _ = a.powf(2.0);\n let _ = a * 2.0 + 4.0;\n let _ = if a < 0.0 {\n -a\n } else {\n a\n };\n let _ = if a < 0.0 {\n a\n } else {\n -a\n };\n ```\n\n is better expressed as\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = a.exp2();\n let _ = a.exp();\n let _ = a.sqrt();\n let _ = a.log2();\n let _ = a.log10();\n let _ = a.ln();\n let _ = a.powi(2);\n let _ = a.mul_add(2.0, 4.0);\n let _ = a.abs();\n let _ = -a.abs();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_format", - "id_span": { - "path": "src/format.rs", - "line": 37 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of `format!(\"string literal with no argument\")` and `format!(\"{}\", foo)` where `foo` is a string.\n\n **Why is this bad?** There is no point of doing that. `format!(\"foo\")` can\n be replaced by `\"foo\".to_owned()` if you really need a `String`. The even\n worse `&format!(\"foo\")` is often encountered in the wild. `format!(\"{}\",\n foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`\n if `foo: &str`.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n\n // Bad\n # let foo = \"foo\";\n format!(\"{}\", foo);\n\n // Good\n format!(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suspicious_assignment_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 22 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.\n\n **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or\n confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n a =- 42; // confusing, should it be `a -= 42` or `a = -42`?\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_unary_op_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks the formatting of a unary operator on the right hand side of a binary operator. It lints if there is no space between the binary and unary operators,\n but there is a space between the unary and its operand.\n\n **Why is this bad?** This is either a typo in the binary operator or confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo <- 30 { // this should be `foo < -30` but looks like a different operator\n }\n\n if foo &&! bar { // this should be `foo && !bar` but looks like a different operator\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_else_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 80 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for formatting of `else`. It lints if the `else` is followed immediately by a newline or the `else` seems to be missing.\n\n **Why is this bad?** This is probably some refactoring remnant, even if the\n code is correct, it might look confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo {\n } { // looks like an `else` is missing here\n }\n\n if foo {\n } if bar { // looks like an `else` is missing here\n }\n\n if foo {\n } else\n\n { // this is the `else` block of the previous `if`, but should it be?\n }\n\n if foo {\n } else\n\n if bar { // this is the `else` block of the previous `if`, but should it be?\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "possible_missing_comma", - "id_span": { - "path": "src/formatting.rs", - "line": 100 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines.\n\n **Why is this bad?** This could lead to unexpected results.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = &[\n -1, -2, -3 // <= no comma here\n -4, -5, -6\n ];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "from_over_into", - "id_span": { - "path": "src/from_over_into.rs", - "line": 39 - }, - "group": "clippy::style", - "docs": " **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.\n **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct StringWrapper(String);\n\n impl Into for String {\n fn into(self) -> StringWrapper {\n StringWrapper(self)\n }\n }\n ```\n Use instead:\n ```rust\n struct StringWrapper(String);\n\n impl From for StringWrapper {\n fn from(s: String) -> StringWrapper {\n StringWrapper(s)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "from_str_radix_10", - "id_span": { - "path": "src/from_str_radix_10.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function invocations of the form `primitive::from_str_radix(s, 10)`\n\n **Why is this bad?**\n This specific common use case can be rewritten as `s.parse::()`\n (and in most cases, the turbofish can be removed), which reduces code length\n and complexity.\n\n **Known problems:**\n This lint may suggest using (&).parse() instead of .parse() directly\n in some cases, which is correct but adds unnecessary complexity to the code.\n\n **Example:**\n\n ```ignore\n let input: &str = get_input();\n let num = u16::from_str_radix(input, 10)?;\n ```\n Use instead:\n ```ignore\n let input: &str = get_input();\n let num: u16 = input.parse()?;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "too_many_arguments", - "id_span": { - "path": "src/functions.rs", - "line": 39 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for functions with too many parameters.\n **Why is this bad?** Functions with lots of parameters are considered bad\n style and reduce readability (“what does the 5th parameter mean?”). Consider\n grouping some parameters into a new type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Color;\n fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "too_many_lines", - "id_span": { - "path": "src/functions.rs", - "line": 62 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions with a large amount of lines.\n **Why is this bad?** Functions with a lot of lines are harder to understand\n due to having to look at a larger amount of code to understand what the\n function is doing. Consider splitting the body of the function into\n multiple functions.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn im_too_long() {\n println!(\"\");\n // ... 100 more LoC\n println!(\"\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "not_unsafe_ptr_arg_deref", - "id_span": { - "path": "src/functions.rs", - "line": 96 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for public functions that dereference raw pointer arguments but are not marked unsafe.\n\n **Why is this bad?** The function should probably be marked `unsafe`, since\n for an arbitrary raw pointer, there is no way of telling for sure if it is\n valid.\n\n **Known problems:**\n\n * It does not check functions recursively so if the pointer is passed to a\n private non-`unsafe` function which does the dereferencing, the lint won't\n trigger.\n * It only checks for arguments whose type are raw pointers, not raw pointers\n got from an argument in some other way (`fn foo(bar: &[*const u8])` or\n `some_argument.get_raw_ptr()`).\n\n **Example:**\n ```rust,ignore\n // Bad\n pub fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n\n // Good\n pub unsafe fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "must_use_unit", - "id_span": { - "path": "src/functions.rs", - "line": 117 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for a [`#[must_use]`] attribute on unit-returning functions and methods.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Unit values are useless. The attribute is likely\n a remnant of a refactoring that removed the return type.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn useless() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "double_must_use", - "id_span": { - "path": "src/functions.rs", - "line": 142 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for a [`#[must_use]`] attribute without further information on functions and methods that return a type already\n marked as `#[must_use]`.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** The attribute isn't needed. Not using the result\n will already be reported. Alternatively, one can add some text to the\n attribute to improve the lint message.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn double_must_use() -> Result<(), ()> {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "must_use_candidate", - "id_span": { - "path": "src/functions.rs", - "line": 170 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for public functions that have no [`#[must_use]`] attribute, but return something not already marked\n must-use, have no mutable arg and mutate no statics.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Not bad at all, this lint just shows places where\n you could add the attribute.\n\n **Known problems:** The lint only checks the arguments for mutable\n types without looking if they are actually changed. On the other hand,\n it also ignores a broad range of potentially interesting side effects,\n because we cannot decide whether the programmer intends the function to\n be called for the side effect or the result. Expect many false\n positives. At least we don't lint if the result type is unit or already\n `#[must_use]`.\n\n **Examples:**\n ```rust\n // this could be annotated with `#[must_use]`.\n fn id(t: T) -> T { t }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_unit_err", - "id_span": { - "path": "src/functions.rs", - "line": 216 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for public functions that return a `Result` with an `Err` type of `()`. It suggests using a custom type that\n implements [`std::error::Error`].\n\n **Why is this bad?** Unit does not implement `Error` and carries no\n further information about what went wrong.\n\n **Known problems:** Of course, this lint assumes that `Result` is used\n for a fallible operation (which is after all the intended use). However\n code may opt to (mis)use it as a basic two-variant-enum. In that case,\n the suggestion is misguided, and the code should use a custom enum\n instead.\n\n **Examples:**\n ```rust\n pub fn read_u8() -> Result { Err(()) }\n ```\n should become\n ```rust,should_panic\n use std::fmt;\n\n #[derive(Debug)]\n pub struct EndOfStream;\n\n impl fmt::Display for EndOfStream {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n write!(f, \"End of Stream\")\n }\n }\n\n impl std::error::Error for EndOfStream { }\n\n pub fn read_u8() -> Result { Err(EndOfStream) }\n# fn main() {\n# read_u8().unwrap();\n# }\n ```\n\n Note that there are crates that simplify creating the error type, e.g.\n [`thiserror`](https://docs.rs/thiserror).\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "future_not_send", - "id_span": { - "path": "src/future_not_send.rs", - "line": 44 - }, - "group": "clippy::nursery", - "docs": " **What it does:** This lint requires Future implementations returned from functions and methods to implement the `Send` marker trait. It is mostly\n used by library authors (public and internal) that target an audience where\n multithreaded executors are likely to be used for running these Futures.\n\n **Why is this bad?** A Future implementation captures some state that it\n needs to eventually produce its final value. When targeting a multithreaded\n executor (which is the norm on non-embedded devices) this means that this\n state may need to be transported to other threads, in other words the\n whole Future needs to implement the `Send` marker trait. If it does not,\n then the resulting Future cannot be submitted to a thread pool in the\n end user’s code.\n\n Especially for generic functions it can be confusing to leave the\n discovery of this problem to the end user: the reported error location\n will be far from its cause and can in many cases not even be fixed without\n modifying the library where the offending Future implementation is\n produced.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn not_send(bytes: std::rc::Rc<[u8]>) {}\n ```\n Use instead:\n ```rust\n async fn is_send(bytes: std::sync::Arc<[u8]>) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "get_last_with_len", - "id_span": { - "path": "src/get_last_with_len.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.\n\n **Why is this bad?** Using `x.last()` is easier to read and has the same\n result.\n\n Note that using `x[x.len() - 1]` is semantically different from\n `x.last()`. Indexing into the array will panic on out-of-bounds\n accesses, while `x.get()` and `x.last()` will return `None`.\n\n There is another lint (get_unwrap) that covers the case of using\n `x.get(index).unwrap()` instead of `x[index]`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x = vec![2, 3, 5];\n let last_element = x.get(x.len() - 1);\n\n // Good\n let x = vec![2, 3, 5];\n let last_element = x.last();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "identity_op", - "id_span": { - "path": "src/identity_op.rs", - "line": 24 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for identity operations, e.g., `x + 0`.\n **Why is this bad?** This code can be removed without changing the\n meaning. So it just obscures what's going on. Delete it mercilessly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n x / 1 + 0 * 1 - 0 | 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_let_mutex", - "id_span": { - "path": "src/if_let_mutex.rs", - "line": 36 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `Mutex::lock` calls in `if let` expression with lock calls in any of the else blocks.\n\n **Why is this bad?** The Mutex lock remains held for the whole\n `if let ... else` block and deadlocks.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n if let Ok(thing) = mutex.lock() {\n do_thing();\n } else {\n mutex.lock();\n }\n ```\n Should be written\n ```rust,ignore\n let locked = mutex.lock();\n if let Ok(thing) = locked {\n do_thing(thing);\n } else {\n use_locked(locked);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_let_some_result", - "id_span": { - "path": "src/if_let_some_result.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:*** Checks for unnecessary `ok()` in if let.\n **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match\n on `Ok(pat)`\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for i in iter {\n if let Some(value) = i.parse().ok() {\n vec.push(value)\n }\n }\n ```\n Could be written:\n\n ```ignore\n for i in iter {\n if let Ok(value) = i.parse() {\n vec.push(value)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "if_not_else", - "id_span": { - "path": "src/if_not_else.rs", - "line": 43 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `!` or `!=` in an if condition with an else branch.\n\n **Why is this bad?** Negations reduce the readability of statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if !v.is_empty() {\n a()\n } else {\n b()\n }\n ```\n\n Could be written:\n\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if v.is_empty() {\n b()\n } else {\n a()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "implicit_return", - "id_span": { - "path": "src/implicit_return.rs", - "line": 33 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for missing return statements at the end of a block.\n **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers\n coming from other languages might prefer the expressiveness of `return`. It's possible to miss\n the last returning statement because the only difference is a missing `;`. Especially in bigger\n code with multiple return paths having a `return` keyword makes it easier to find the\n corresponding statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n add return\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "implicit_saturating_sub", - "id_span": { - "path": "src/implicit_saturating_sub.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for implicit saturating subtraction.\n **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let end: u32 = 10;\n let start: u32 = 5;\n\n let mut i: u32 = end - start;\n\n // Bad\n if i != 0 {\n i -= 1;\n }\n\n // Good\n i = i.saturating_sub(1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "inconsistent_struct_constructor", - "id_span": { - "path": "src/inconsistent_struct_constructor.rs", - "line": 58 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for struct constructors where the order of the field init shorthand in the constructor is inconsistent with the order in the struct definition.\n\n **Why is this bad?** Since the order of fields in a constructor doesn't affect the\n resulted instance as the below example indicates,\n\n ```rust\n #[derive(Debug, PartialEq, Eq)]\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n\n // This assertion never fails.\n assert_eq!(Foo { x, y }, Foo { y, x });\n ```\n\n inconsistent order means nothing and just decreases readability and consistency.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n Foo { y, x };\n ```\n\n Use instead:\n ```rust\n # struct Foo {\n # x: i32,\n # y: i32,\n # }\n # let x = 1;\n # let y = 2;\n Foo { x, y };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "out_of_bounds_indexing", - "id_span": { - "path": "src/indexing_slicing.rs", - "line": 32 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for out of bounds array indexing with a constant index.\n\n **Why is this bad?** This will always panic at runtime.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```no_run\n # #![allow(const_err)]\n let x = [1, 2, 3, 4];\n\n // Bad\n x[9];\n &x[2..9];\n\n // Good\n x[0];\n x[3];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "indexing_slicing", - "id_span": { - "path": "src/indexing_slicing.rs", - "line": 81 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint does report on arrays if we can tell that slicing operations are in bounds and does not\n lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.\n\n **Why is this bad?** Indexing and slicing can panic at runtime and there are\n safe alternatives.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```rust,no_run\n // Vector\n let x = vec![0; 5];\n\n // Bad\n x[2];\n &x[2..100];\n &x[2..];\n &x[..100];\n\n // Good\n x.get(2);\n x.get(2..100);\n x.get(2..);\n x.get(..100);\n\n // Array\n let y = [0, 1, 2, 3];\n\n // Bad\n &y[10..100];\n &y[10..];\n &y[..100];\n\n // Good\n &y[2..];\n &y[..2];\n &y[0..3];\n y.get(10);\n y.get(10..100);\n y.get(10..);\n y.get(..100);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "infinite_iter", - "id_span": { - "path": "src/infinite_iter.rs", - "line": 21 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "maybe_infinite_iter", - "id_span": { - "path": "src/infinite_iter.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "multiple_inherent_impl", - "id_span": { - "path": "src/inherent_impl.rs", - "line": 37 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for multiple inherent implementations of a struct\n **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn one() {}\n }\n impl X {\n fn other() {}\n }\n ```\n\n Could be written:\n\n ```rust\n struct X;\n impl X {\n fn one() {}\n fn other() {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inherent_to_string", - "id_span": { - "path": "src/inherent_to_string.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inherent_to_string_shadow_display", - "id_span": { - "path": "src/inherent_to_string.rs", - "line": 89 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n use std::fmt;\n\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A, too\")\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_fn_without_body", - "id_span": { - "path": "src/inline_fn_without_body.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `#[inline]` on trait methods without bodies\n **Why is this bad?** Only implementations of trait methods may be inlined.\n The inline attribute is ignored for trait methods without bodies.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n trait Animal {\n #[inline]\n fn name(&self) -> &'static str;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "int_plus_one", - "id_span": { - "path": "src/int_plus_one.rs", - "line": 31 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block\n **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 1;\n if x >= y + 1 {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 1;\n if x > y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "integer_division", - "id_span": { - "path": "src/integer_division.rs", - "line": 26 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for division of integers\n **Why is this bad?** When outside of some very specific algorithms,\n integer division is very often a mistake because it discards the\n remainder.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x = 3 / 2;\n println!(\"{}\", x);\n\n // Good\n let x = 3f32 / 2f32;\n println!(\"{}\", x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "items_after_statements", - "id_span": { - "path": "src/items_after_statements.rs", - "line": 48 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for items declared after some statement in a block.\n **Why is this bad?** Items live for the entire scope they are declared\n in. But statements are processed in order. This might cause confusion as\n it's hard to figure out which item is meant in a statement.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n foo(); // prints \"foo\"\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n }\n ```\n\n ```rust\n // Good\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n foo(); // prints \"foo\"\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "large_const_arrays", - "id_span": { - "path": "src/large_const_arrays.rs", - "line": 30 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for large `const` arrays that should be defined as `static` instead.\n\n **Why is this bad?** Performance: const variables are inlined upon use.\n Static items result in only one instance and has a fixed location in memory.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n pub const a = [0u32; 1_000_000];\n\n // Good\n pub static a = [0u32; 1_000_000];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "large_enum_variant", - "id_span": { - "path": "src/large_enum_variant.rs", - "line": 39 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for large size differences between variants on `enum`s.\n\n **Why is this bad?** Enum size is bounded by the largest variant. Having a\n large variant can penalize the memory layout of that enum.\n\n **Known problems:** This lint obviously cannot take the distribution of\n variants in your running program into account. It is possible that the\n smaller variants make up less than 1% of all instances, in which case\n the overhead is negligible and the boxing is counter-productive. Always\n measure the change this lint suggests.\n\n **Example:**\n\n ```rust\n // Bad\n enum Test {\n A(i32),\n B([i32; 8000]),\n }\n\n // Possibly better\n enum Test2 {\n A(i32),\n B(Box<[i32; 8000]>),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "large_stack_arrays", - "id_span": { - "path": "src/large_stack_arrays.rs", - "line": 23 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for local arrays that may be too large.\n **Why is this bad?** Large local arrays may cause stack overflow.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = [0u32; 1_000_000];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "len_zero", - "id_span": { - "path": "src/len_zero.rs", - "line": 41 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than calculating their length. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n if x.len() == 0 {\n ..\n }\n if y.len() != 0 {\n ..\n }\n ```\n instead use\n ```ignore\n if x.is_empty() {\n ..\n }\n if !y.is_empty() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "len_without_is_empty", - "id_span": { - "path": "src/len_zero.rs", - "line": 66 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for items that implement `.len()` but not `.is_empty()`.\n\n **Why is this bad?** It is good custom to have both methods, because for\n some data structures, asking about the length will be a costly operation,\n whereas `.is_empty()` can usually answer in constant time. Also it used to\n lead to false positives on the [`len_zero`](#len_zero) lint – currently that\n lint will ignore such entities.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl X {\n pub fn len(&self) -> usize {\n ..\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "comparison_to_empty", - "id_span": { - "path": "src/len_zero.rs", - "line": 103 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for comparing to an empty slice such as `\"\"` or `[]`, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than checking for equality. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n if s == \"\" {\n ..\n }\n\n if arr == [] {\n ..\n }\n ```\n Use instead:\n ```ignore\n if s.is_empty() {\n ..\n }\n\n if arr.is_empty() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_let_if_seq", - "id_span": { - "path": "src/let_if_seq.rs", - "line": 49 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for variable declarations immediately followed by a conditional affectation.\n\n **Why is this bad?** This is not idiomatic Rust.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let foo;\n\n if bar() {\n foo = 42;\n } else {\n foo = 0;\n }\n\n let mut baz = None;\n\n if bar() {\n baz = Some(42);\n }\n ```\n\n should be written\n\n ```rust,ignore\n let foo = if bar() {\n 42\n } else {\n 0\n };\n\n let baz = if bar() {\n Some(42)\n } else {\n None\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "let_underscore_must_use", - "id_span": { - "path": "src/let_underscore.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `let _ = ` where expr is #[must_use]\n\n **Why is this bad?** It's better to explicitly\n handle the value of a #[must_use] expr\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn f() -> Result {\n Ok(0)\n }\n\n let _ = f();\n // is_ok() is marked #[must_use]\n let _ = f().is_ok();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "let_underscore_lock", - "id_span": { - "path": "src/let_underscore.rs", - "line": 56 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `let _ = sync_lock`\n **Why is this bad?** This statement immediately drops the lock instead of\n extending its lifetime to the end of the scope, which is often not intended.\n To extend lock lifetime to the end of the scope, use an underscore-prefixed\n name instead (i.e. _lock). If you want to explicitly drop the lock,\n `std::mem::drop` conveys your intention better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n let _ = mutex.lock();\n ```\n\n Good:\n ```rust,ignore\n let _lock = mutex.lock();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "let_underscore_drop", - "id_span": { - "path": "src/let_underscore.rs", - "line": 97 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `let _ = ` where expr has a type that implements `Drop`\n\n **Why is this bad?** This statement immediately drops the initializer\n expression instead of extending its lifetime to the end of the scope, which\n is often not intended. To extend the expression's lifetime to the end of the\n scope, use an underscore-prefixed name instead (i.e. _var). If you want to\n explicitly drop the expression, `std::mem::drop` conveys your intention\n better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n struct Droppable;\n impl Drop for Droppable {\n fn drop(&mut self) {}\n }\n {\n let _ = Droppable;\n // ^ dropped here\n /* more code */\n }\n ```\n\n Good:\n ```rust,ignore\n {\n let _droppable = Droppable;\n /* more code */\n // dropped at end of scope\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_lifetimes", - "id_span": { - "path": "src/lifetimes.rs", - "line": 46 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for lifetime annotations which can be removed by relying on lifetime elision.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:**\n - We bail out if the function has a `where` clause where lifetimes\n are mentioned due to potenial false positives.\n - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the\n placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetime annotations\n fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {\n x\n }\n\n // Good\n fn elided(x: &u8, y: u8) -> &u8 {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "extra_unused_lifetimes", - "id_span": { - "path": "src/lifetimes.rs", - "line": 74 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for lifetimes in generics that are never used anywhere else.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetimes\n fn unused_lifetime<'a>(x: u8) {\n // ..\n }\n\n // Good\n fn no_lifetime(x: u8) {\n // ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unreadable_literal", - "id_span": { - "path": "src/literal_representation.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if a long integral or floating-point constant does not contain underscores.\n\n **Why is this bad?** Reading long numbers is difficult without separators.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 61864918973511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mistyped_literal_suffixes", - "id_span": { - "path": "src/literal_representation.rs", - "line": 57 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Warns for mistyped suffix in literals\n **Why is this bad?** This is most probably a typo\n\n **Known problems:**\n - Recommends a signed suffix, even though the number might be too big and an unsigned\n suffix is required\n - Does not match on `_127` since that is a valid grouping for decimal and octal numbers\n\n **Example:**\n\n ```rust\n // Probably mistyped\n 2_32;\n\n // Good\n 2_i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inconsistent_digit_grouping", - "id_span": { - "path": "src/literal_representation.rs", - "line": 80 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if an integral or floating-point constant is grouped inconsistently with underscores.\n\n **Why is this bad?** Readers may incorrectly interpret inconsistently\n grouped digits.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 618_64_9189_73_511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unusual_byte_groupings", - "id_span": { - "path": "src/literal_representation.rs", - "line": 99 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if hexadecimal or binary literals are not grouped by nibble or byte.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u32 = 0xFFF_FFF;\n let y: u8 = 0b01_011_101;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "large_digit_groups", - "id_span": { - "path": "src/literal_representation.rs", - "line": 118 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if the digits of an integral or floating-point constant are grouped into groups that\n are too large.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u64 = 6186491_8973511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "decimal_literal_representation", - "id_span": { - "path": "src/literal_representation.rs", - "line": 136 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns if there is a better representation for a numeric literal.\n **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more\n readable than a decimal representation.\n\n **Known problems:** None.\n\n **Example:**\n\n `255` => `0xFF`\n `65_535` => `0xFFFF`\n `4_042_322_160` => `0xF0F0_F0F0`\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_memcpy", - "id_span": { - "path": "src/loops/mod.rs", - "line": 50 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "needless_range_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 78 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "explicit_iter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 107 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "explicit_into_iter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 135 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_next_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 158 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "for_loops_over_fallibles", - "id_span": { - "path": "src/loops/mod.rs", - "line": 201 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_let_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 230 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "needless_collect", - "id_span": { - "path": "src/loops/mod.rs", - "line": 252 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "explicit_counter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 282 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "empty_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 315 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_let_on_iterator", - "id_span": { - "path": "src/loops/mod.rs", - "line": 334 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "for_kv_map", - "id_span": { - "path": "src/loops/mod.rs", - "line": 362 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "never_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 383 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_range_bound", - "id_span": { - "path": "src/loops/mod.rs", - "line": 403 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_immutable_condition", - "id_span": { - "path": "src/loops/mod.rs", - "line": 426 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "same_item_push", - "id_span": { - "path": "src/loops/mod.rs", - "line": 459 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_element_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 484 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_flatten", - "id_span": { - "path": "src/loops/mod.rs", - "line": 515 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "macro_use_imports", - "id_span": { - "path": "src/macro_use.rs", - "line": 25 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `#[macro_use] use...`.\n **Why is this bad?** Since the Rust 2018 edition you can import\n macro's directly, this is considered idiomatic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[macro_use]\n use some_macro;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "main_recursion", - "id_span": { - "path": "src/main_recursion.rs", - "line": 22 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for recursion using the entrypoint.\n **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),\n recursing into main() seems like an unintuitive antipattern we should be able to detect.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n fn main() {\n main();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_async_fn", - "id_span": { - "path": "src/manual_async_fn.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** It checks for manual implementations of `async` functions.\n **Why is this bad?** It's more idiomatic to use the dedicated syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::future::Future;\n\n fn foo() -> impl Future { async { 42 } }\n ```\n Use instead:\n ```rust\n async fn foo() -> i32 { 42 }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_map", - "id_span": { - "path": "src/manual_map.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_non_exhaustive", - "id_span": { - "path": "src/manual_non_exhaustive.rs", - "line": 56 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for manual implementations of the non-exhaustive pattern.\n **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent\n and allows possible optimizations when applied to enums.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct S {\n pub a: i32,\n pub b: i32,\n _c: (),\n }\n\n enum E {\n A,\n B,\n #[doc(hidden)]\n _C,\n }\n\n struct T(pub i32, pub i32, ());\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct S {\n pub a: i32,\n pub b: i32,\n }\n\n #[non_exhaustive]\n enum E {\n A,\n B,\n }\n\n #[non_exhaustive]\n struct T(pub i32, pub i32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "manual_ok_or", - "id_span": { - "path": "src/manual_ok_or.rs", - "line": 34 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Finds patterns that reimplement `Option::ok_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n let foo: Option = None;\n foo.map_or(Err(\"error\"), |v| Ok(v));\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.ok_or(\"error\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_strip", - "id_span": { - "path": "src/manual_strip.rs", - "line": 52 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\n the pattern's length.\n\n **Why is this bad?**\n Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no\n slicing which may panic and the compiler does not need to insert this panic code. It is\n also sometimes more readable as it removes the need for duplicating or storing the pattern\n used by `str::{starts,ends}_with` and in the slicing.\n\n **Known problems:**\n None.\n\n **Example:**\n\n ```rust\n let s = \"hello, world!\";\n if s.starts_with(\"hello, \") {\n assert_eq!(s[\"hello, \".len()..].to_uppercase(), \"WORLD!\");\n }\n ```\n Use instead:\n ```rust\n let s = \"hello, world!\";\n if let Some(end) = s.strip_prefix(\"hello, \") {\n assert_eq!(end.to_uppercase(), \"WORLD!\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_unwrap_or", - "id_span": { - "path": "src/manual_unwrap_or.rs", - "line": 36 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo: Option = None;\n match foo {\n Some(v) => v,\n None => 1,\n };\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.unwrap_or(1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_clone", - "id_span": { - "path": "src/map_clone.rs", - "line": 40 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `map(|x| x.clone())` or dereferencing closures for `Copy` types, on `Iterator` or `Option`,\n and suggests `cloned()` or `copied()` instead\n\n **Why is this bad?** Readability, this can be written more concisely\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.map(|i| *i);\n ```\n\n The correct use would be:\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.cloned();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_err_ignore", - "id_span": { - "path": "src/map_err_ignore.rs", - "line": 101 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for instances of `map_err(|_| Some::Enum)`\n **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\n\n **Known problems:** None.\n\n **Example:**\n Before:\n ```rust\n use std::fmt;\n\n #[derive(Debug)]\n enum Error {\n Indivisible,\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {}\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(|_| Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n\n After:\n ```rust\n use std::{fmt, num::ParseIntError};\n\n #[derive(Debug)]\n enum Error {\n Indivisible(ParseIntError),\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible(_) => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {\n fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n match self {\n Error::Indivisible(source) => Some(source),\n _ => None,\n }\n }\n }\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_identity", - "id_span": { - "path": "src/map_identity.rs", - "line": 30 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for instances of `map(f)` where `f` is the identity function.\n **Why is this bad?** It can be written more concisely without the call to `map`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();\n ```\n Use instead:\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| 2*x).collect();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "option_map_unit_fn", - "id_span": { - "path": "src/map_unit_fn.rs", - "line": 48 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_map_unit_fn", - "id_span": { - "path": "src/map_unit_fn.rs", - "line": 89 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "match_on_vec_items", - "id_span": { - "path": "src/match_on_vec_items.rs", - "line": 41 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.\n **Why is this bad?** This can panic at runtime.\n\n **Known problems:** None.\n\n **Example:**\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Bad\n match arr[idx] {\n 0 => println!(\"{}\", 0),\n 1 => println!(\"{}\", 3),\n _ => {},\n }\n ```\n Use instead:\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Good\n match arr.get(idx) {\n Some(0) => println!(\"{}\", 0),\n Some(1) => println!(\"{}\", 3),\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "single_match", - "id_span": { - "path": "src/matches.rs", - "line": 55 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "single_match_else", - "id_span": { - "path": "src/matches.rs", - "line": 94 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "match_ref_pats", - "id_span": { - "path": "src/matches.rs", - "line": 125 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches where all arms match a reference, suggesting to remove the reference and deref the matched expression\n instead. It also checks for `if let &foo = bar` blocks.\n\n **Why is this bad?** It just makes the code less readable. That reference\n destructuring adds nothing to the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n match x {\n &A(ref y) => foo(y),\n &B => bar(),\n _ => frob(&x),\n }\n\n // Good\n match *x {\n A(ref y) => foo(y),\n B => bar(),\n _ => frob(x),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_bool", - "id_span": { - "path": "src/matches.rs", - "line": 159 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for matches where match expression is a `bool`. It suggests to replace the expression with an `if...else` block.\n\n **Why is this bad?** It makes the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n match condition {\n true => foo(),\n false => bar(),\n }\n ```\n Use if/else instead:\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n if condition {\n foo();\n } else {\n bar();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "match_overlapping_arm", - "id_span": { - "path": "src/matches.rs", - "line": 181 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for overlapping match arms.\n **Why is this bad?** It is likely to be an error and if not, makes the code\n less obvious.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 5;\n match x {\n 1...10 => println!(\"1 ... 10\"),\n 5...15 => println!(\"5 ... 15\"),\n _ => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_wild_err_arm", - "id_span": { - "path": "src/matches.rs", - "line": 203 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for arm which matches all errors with `Err(_)` and take drastic actions like `panic!`.\n\n **Why is this bad?** It is generally a bad practice, similar to\n catching all exceptions in java with `catch(Exception)`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Result = Ok(3);\n match x {\n Ok(_) => println!(\"ok\"),\n Err(_) => panic!(\"err\"),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_as_ref", - "id_span": { - "path": "src/matches.rs", - "line": 229 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for match which is used to add a reference to an `Option` value.\n\n **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Option<()> = None;\n\n // Bad\n let r: Option<&()> = match x {\n None => None,\n Some(ref v) => Some(v),\n };\n\n // Good\n let r: Option<&()> = x.as_ref();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "wildcard_enum_match_arm", - "id_span": { - "path": "src/matches.rs", - "line": 258 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for wildcard enum matches using `_`.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some\n variants, and also may not use correct path to enum if it's not present in the current scope.\n\n **Example:**\n ```rust\n # enum Foo { A(usize), B(usize) }\n # let x = Foo::B(1);\n // Bad\n match x {\n Foo::A(_) => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A(_) => {},\n Foo::B(_) => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_wildcard_for_single_variants", - "id_span": { - "path": "src/matches.rs", - "line": 290 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for wildcard enum matches for a single variant.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may not use correct path to enum\n if it's not present in the current scope.\n\n **Example:**\n\n ```rust\n # enum Foo { A, B, C }\n # let x = Foo::B;\n // Bad\n match x {\n Foo::A => {},\n Foo::B => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A => {},\n Foo::B => {},\n Foo::C => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "wildcard_in_or_patterns", - "id_span": { - "path": "src/matches.rs", - "line": 317 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for wildcard pattern used with others patterns in same match arm.\n **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.\n It makes the code less readable, especially to spot wildcard pattern use in match arm.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n match \"foo\" {\n \"a\" => {},\n \"bar\" | _ => {},\n }\n\n // Good\n match \"foo\" {\n \"a\" => {},\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "infallible_destructuring_match", - "id_span": { - "path": "src/matches.rs", - "line": 352 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches being used to destructure a single-variant enum or tuple struct where a `let` will suffice.\n\n **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n\n let data = match wrapper {\n Wrapper::Data(i) => i,\n };\n ```\n\n The correct use would be:\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n let Wrapper::Data(data) = wrapper;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "match_single_binding", - "id_span": { - "path": "src/matches.rs", - "line": 380 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for useless match that binds to only one value.\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** Suggested replacements may be incorrect when `match`\n is actually binding temporary value, bringing a 'dropped while borrowed' error.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n\n // Bad\n match (a, b) {\n (c, d) => {\n // useless match\n }\n }\n\n // Good\n let (c, d) = (a, b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "rest_pat_in_fully_bound_structs", - "id_span": { - "path": "src/matches.rs", - "line": 410 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.\n **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after\n matching all enum variants explicitly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct A { a: i32 }\n let a = A { a: 5 };\n\n // Bad\n match a {\n A { a: 5, .. } => {},\n _ => {},\n }\n\n // Good\n match a {\n A { a: 5 } => {},\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_pattern_matching", - "id_span": { - "path": "src/matches.rs", - "line": 458 - }, - "group": "clippy::style", - "docs": " **What it does:** Lint for redundant pattern matching over `Result`, `Option`, `std::task::Poll` or `std::net::IpAddr`\n\n **Why is this bad?** It's more concise and clear to just use the proper\n utility function\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if let Ok(_) = Ok::(42) {}\n if let Err(_) = Err::(42) {}\n if let None = None::<()> {}\n if let Some(_) = Some(42) {}\n if let Poll::Pending = Poll::Pending::<()> {}\n if let Poll::Ready(_) = Poll::Ready(42) {}\n if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}\n if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}\n match Ok::(42) {\n Ok(_) => true,\n Err(_) => false,\n };\n ```\n\n The more idiomatic use would be:\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if Ok::(42).is_ok() {}\n if Err::(42).is_err() {}\n if None::<()>.is_none() {}\n if Some(42).is_some() {}\n if Poll::Pending::<()>.is_pending() {}\n if Poll::Ready(42).is_ready() {}\n if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}\n if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}\n Ok::(42).is_ok();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_like_matches_macro", - "id_span": { - "path": "src/matches.rs", - "line": 491 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `match` or `if let` expressions producing a `bool` that could be written using `matches!`\n\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** This lint falsely triggers, if there are arms with\n `cfg` attributes that remove an arm evaluating to `false`.\n\n **Example:**\n ```rust\n let x = Some(5);\n\n // Bad\n let a = match x {\n Some(0) => true,\n _ => false,\n };\n\n let a = if let Some(0) = x {\n true\n } else {\n false\n };\n\n // Good\n let a = matches!(x, Some(0));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_same_arms", - "id_span": { - "path": "src/matches.rs", - "line": 532 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `match` with identical arm bodies.\n **Why is this bad?** This is probably a copy & paste error. If arm bodies\n are the same on purpose, you can factor them\n [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).\n\n **Known problems:** False positive possible with order dependent `match`\n (see issue\n [#860](https://github.com/rust-lang/rust-clippy/issues/860)).\n\n **Example:**\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => bar(), // <= oops\n }\n ```\n\n This should probably be\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => baz(), // <= fixed\n }\n ```\n\n or if the original code was not a typo:\n ```rust,ignore\n match foo {\n Bar | Baz => bar(), // <= shows the intent better\n Quz => quz(),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_discriminant_non_enum", - "id_span": { - "path": "src/mem_discriminant.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.\n **Why is this bad?** The value of `mem::discriminant()` on non-enum types\n is unspecified.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n mem::discriminant(&\"hello\");\n mem::discriminant(&&Some(2));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mem_forget", - "id_span": { - "path": "src/mem_forget.rs", - "line": 21 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is `Drop`.\n\n **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its\n destructor, possibly causing leaks.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::mem;\n # use std::rc::Rc;\n mem::forget(Rc::new(55))\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_replace_option_with_none", - "id_span": { - "path": "src/mem_replace.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `mem::replace()` on an `Option` with `None`.\n\n **Why is this bad?** `Option` already has the method `take()` for\n taking its current value (Some(..) or None) and replacing it with\n `None`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n let mut an_option = Some(0);\n let replaced = mem::replace(&mut an_option, None);\n ```\n Is better expressed with:\n ```rust\n let mut an_option = Some(0);\n let taken = an_option.take();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mem_replace_with_uninit", - "id_span": { - "path": "src/mem_replace.rs", - "line": 69 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())` and `mem::replace(&mut _, mem::zeroed())`.\n\n **Why is this bad?** This will lead to undefined behavior even if the\n value is overwritten later, because the uninitialized value may be\n observed in the case of a panic.\n\n **Known problems:** None.\n\n **Example:**\n\n ```\n use std::mem;\n# fn may_panic(v: Vec) -> Vec { v }\n\n #[allow(deprecated, invalid_value)]\n fn myfunc (v: &mut Vec) {\n let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };\n let new_v = may_panic(taken_v); // undefined behavior on panic\n mem::forget(mem::replace(v, new_v));\n }\n ```\n\n The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,\n at the cost of either lazily creating a replacement value or aborting\n on panic, to ensure that the uninitialized value cannot be observed.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_replace_with_default", - "id_span": { - "path": "src/mem_replace.rs", - "line": 93 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `std::mem::replace` on a value of type `T` with `T::default()`.\n\n **Why is this bad?** `std::mem` module already has the method `take` to\n take the current value and replace it with the default value of that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut text = String::from(\"foo\");\n let replaced = std::mem::replace(&mut text, String::default());\n ```\n Is better expressed with:\n ```rust\n let mut text = String::from(\"foo\");\n let taken = std::mem::take(&mut text);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unwrap_used", - "id_span": { - "path": "src/methods/mod.rs", - "line": 84 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", - "applicability": null - }, - { - "id": "expect_used", - "id_span": { - "path": "src/methods/mod.rs", - "line": 126 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", - "applicability": null - }, - { - "id": "should_implement_trait", - "id_span": { - "path": "src/methods/mod.rs", - "line": 155 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_self_convention", - "id_span": { - "path": "src/methods/mod.rs", - "line": 188 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_pub_self_convention", - "id_span": { - "path": "src/methods/mod.rs", - "line": 212 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ok_expect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 235 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_unwrap_or", - "id_span": { - "path": "src/methods/mod.rs", - "line": 272 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_map_or_none", - "id_span": { - "path": "src/methods/mod.rs", - "line": 295 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_map_or_into_option", - "id_span": { - "path": "src/methods/mod.rs", - "line": 321 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "bind_instead_of_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 354 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 377 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "skip_while_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 400 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_flatten", - "id_span": { - "path": "src/methods/mod.rs", - "line": 423 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 452 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 478 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_find_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 504 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_map_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 526 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "flat_map_identity", - "id_span": { - "path": "src/methods/mod.rs", - "line": 548 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "search_is_some", - "id_span": { - "path": "src/methods/mod.rs", - "line": 572 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "chars_next_cmp", - "id_span": { - "path": "src/methods/mod.rs", - "line": 596 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", - "applicability": null - }, - { - "id": "or_fun_call", - "id_span": { - "path": "src/methods/mod.rs", - "line": 627 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "expect_fun_call", - "id_span": { - "path": "src/methods/mod.rs", - "line": 662 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "clone_on_copy", - "id_span": { - "path": "src/methods/mod.rs", - "line": 679 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "clone_on_ref_ptr", - "id_span": { - "path": "src/methods/mod.rs", - "line": 704 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "clone_double_ref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 726 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inefficient_to_string", - "id_span": { - "path": "src/methods/mod.rs", - "line": 749 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "new_ret_no_self", - "id_span": { - "path": "src/methods/mod.rs", - "line": 810 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_char_pattern", - "id_span": { - "path": "src/methods/mod.rs", - "line": 831 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iterator_step_by_zero", - "id_span": { - "path": "src/methods/mod.rs", - "line": 850 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "iter_nth_zero", - "id_span": { - "path": "src/methods/mod.rs", - "line": 878 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_nth", - "id_span": { - "path": "src/methods/mod.rs", - "line": 904 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "iter_skip_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 928 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "get_unwrap", - "id_span": { - "path": "src/methods/mod.rs", - "line": 961 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_extend_chars", - "id_span": { - "path": "src/methods/mod.rs", - "line": 990 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_cloned_collect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1013 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "chars_last_cmp", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1037 - }, - "group": "clippy::style", - "docs": " **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.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", - "applicability": null - }, - { - "id": "useless_asref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1062 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_fold", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1084 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1113 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "into_iter_on_ref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1137 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suspicious_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1156 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "uninit_assumed_init", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1188 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_saturating_arithmetic", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1215 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zst_offset", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1232 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "filetype_is_file", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1272 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_as_ref_deref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1297 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_next_slice", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1323 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "single_char_add_str", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1348 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_lazy_evaluations", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1384 - }, - "group": "clippy::style", - "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_collect_result_unit", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1405 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "from_iter_instead_of_collect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1438 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inspect_for_each", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1468 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "filter_map_identity", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1491 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "bytes_nth", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1513 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "implicit_clone", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1539 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.\n **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as\n to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = vec![1, 2, 3];\n let b = a.to_vec();\n let c = a.to_owned();\n ```\n Use instead:\n ```rust\n let a = vec![1, 2, 3];\n let b = a.clone();\n let c = a.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_count", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1565 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of `.iter().count()`.\n **Why is this bad?** `.len()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.iter().count();\n let _ = &some_vec[..].iter().count();\n\n // Good\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.len();\n let _ = &some_vec[..].len();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "min_max", - "id_span": { - "path": "src/minmax.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant.\n\n **Why is this bad?** This is in all probability not the intended outcome. At\n the least it hurts readability of the code.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n min(0, max(100, x))\n ```\n or\n ```ignore\n x.max(100).min(0)\n ```\n It will always be equal to `0`. Probably the author meant to clamp the value\n between 0 and 100, but has erroneously swapped `min` and `max`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "toplevel_ref_arg", - "id_span": { - "path": "src/misc.rs", - "line": 53 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function arguments and let bindings denoted as `ref`.\n\n **Why is this bad?** The `ref` declaration makes the function take an owned\n value, but turns the argument into a reference (which means that the value\n is destroyed when exiting the function). This adds not much value: either\n take a reference type, or take an owned value and create references in the\n body.\n\n For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The\n type of `x` is more obvious with the former.\n\n **Known problems:** If the argument is dereferenced within the function,\n removing the `ref` will lead to errors. This can be fixed by removing the\n dereferences, e.g., changing `*x` to `x` within the function.\n\n **Example:**\n ```rust,ignore\n // Bad\n fn foo(ref x: u8) -> bool {\n true\n }\n\n // Good\n fn foo(x: &u8) -> bool {\n true\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cmp_nan", - "id_span": { - "path": "src/misc.rs", - "line": 76 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons to NaN.\n **Why is this bad?** NaN does not compare meaningfully to anything – not\n even itself – so those comparisons are simply wrong.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1.0;\n\n // Bad\n if x == f32::NAN { }\n\n // Good\n if x.is_nan() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_cmp", - "id_span": { - "path": "src/misc.rs", - "line": 109 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1.2331f64;\n let y = 1.2332f64;\n\n // Bad\n if y == 1.23f64 { }\n if y != x {} // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (y - 1.23f64).abs() < error_margin { }\n if (y - x).abs() > error_margin { }\n ```\n", - "applicability": null - }, - { - "id": "cmp_owned", - "id_span": { - "path": "src/misc.rs", - "line": 136 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for conversions to owned values just for the sake of a comparison.\n\n **Why is this bad?** The comparison can operate on a reference, so creating\n an owned value effectively throws it away directly afterwards, which is\n needlessly consuming code and heap space.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x.to_owned() == y {}\n ```\n Could be written as\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x == y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "modulo_one", - "id_span": { - "path": "src/misc.rs", - "line": 159 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for getting the remainder of a division by one or minus one.\n\n **Why is this bad?** The result for a divisor of one can only ever be zero; for\n minus one it can cause panic/overflow (if the left operand is the minimal value of\n the respective integer type) or results in zero. No one will write such code\n deliberately, unless trying to win an Underhanded Rust Contest. Even for that\n contest, it's probably a bad idea. Use something more underhanded.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n let a = x % 1;\n let a = x % -1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "used_underscore_binding", - "id_span": { - "path": "src/misc.rs", - "line": 181 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the use of bindings with a single leading underscore.\n\n **Why is this bad?** A single leading underscore is usually used to indicate\n that a binding will not be used. Using such a binding breaks this\n expectation.\n\n **Known problems:** The lint does not work properly with desugaring and\n macro, it has been allowed in the mean time.\n\n **Example:**\n ```rust\n let _x = 0;\n let y = _x + 1; // Here we are using `_x`, even though it has a leading\n // underscore. We should rename `_x` to `x`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "short_circuit_statement", - "id_span": { - "path": "src/misc.rs", - "line": 201 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of short circuit boolean conditions as a\n statement.\n\n **Why is this bad?** Using a short circuit boolean condition as a statement\n may hide the fact that the second part is executed or not depending on the\n outcome of the first part.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n f() && g(); // We should write `if f() { g(); }`.\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zero_ptr", - "id_span": { - "path": "src/misc.rs", - "line": 223 - }, - "group": "clippy::style", - "docs": " **What it does:** Catch casts from `0` to some pointer type\n **Why is this bad?** This generally means `null` and is better expressed as\n {`std`, `core`}`::ptr::`{`null`, `null_mut`}.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let a = 0 as *const u32;\n\n // Good\n let a = std::ptr::null::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "float_cmp_const", - "id_span": { - "path": "src/misc.rs", - "line": 254 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point value and constant, except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: f64 = 1.0;\n const ONE: f64 = 1.00;\n\n // Bad\n if x == ONE { } // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (x - ONE).abs() < error_margin { }\n ```\n", - "applicability": null - }, - { - "id": "unneeded_field_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for structure field patterns bound to wildcards.\n **Why is this bad?** Using `..` instead is shorter and leaves the focus on\n the fields that are actually bound.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Foo {\n # a: i32,\n # b: i32,\n # c: i32,\n # }\n let f = Foo { a: 0, b: 0, c: 0 };\n\n // Bad\n match f {\n Foo { a: _, b: 0, .. } => {},\n Foo { a: _, b: _, c: _ } => {},\n }\n\n // Good\n match f {\n Foo { b: 0, .. } => {},\n Foo { .. } => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "duplicate_underscore_argument", - "id_span": { - "path": "src/misc_early.rs", - "line": 65 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function arguments having the similar names differing by an underscore.\n\n **Why is this bad?** It affects code readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo(a: i32, _a: i32) {}\n\n // Good\n fn bar(a: i32, _b: i32) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "double_neg", - "id_span": { - "path": "src/misc_early.rs", - "line": 83 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects expressions of the form `--x`.\n **Why is this bad?** It can mislead C/C++ programmers to think `x` was\n decremented.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut x = 3;\n --x;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mixed_case_hex_literals", - "id_span": { - "path": "src/misc_early.rs", - "line": 104 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns on hexadecimal literals with mixed-case letter digits.\n\n **Why is this bad?** It looks confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 0x1a9BAcD;\n\n // Good\n let y = 0x1A9BACD;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unseparated_literal_suffix", - "id_span": { - "path": "src/misc_early.rs", - "line": 125 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if literal suffixes are not separated by an underscore.\n\n **Why is this bad?** It is much less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 123832i32;\n\n // Good\n let y = 123832_i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zero_prefixed_literal", - "id_span": { - "path": "src/misc_early.rs", - "line": 163 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Warns if an integral constant literal starts with `0`.\n **Why is this bad?** In some languages (including the infamous C language\n and most of its\n family), this marks an octal constant. In Rust however, this is a decimal\n constant. This could\n be confusing for both the writer and a reader of the constant.\n\n **Known problems:** None.\n\n **Example:**\n\n In Rust:\n ```rust\n fn main() {\n let a = 0123;\n println!(\"{}\", a);\n }\n ```\n\n prints `123`, while in C:\n\n ```c\n #include \n\n int main() {\n int a = 0123;\n printf(\"%d\\n\", a);\n }\n ```\n\n prints `83` (as `83 == 0o123` while `123 == 0o173`).\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "builtin_type_shadow", - "id_span": { - "path": "src/misc_early.rs", - "line": 184 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if a generic shadows a built-in type.\n **Why is this bad?** This gives surprising type errors.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n impl Foo {\n fn impl_func(&self) -> u32 {\n 42\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 213 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for patterns in the form `name @ _`.\n **Why is this bad?** It's almost always more readable to just use direct\n bindings.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = Some(\"abc\");\n\n // Bad\n match v {\n Some(x) => (),\n y @ _ => (),\n }\n\n // Good\n match v {\n Some(x) => (),\n y => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unneeded_wildcard_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 247 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`).\n\n _NOTE_: While `_, ..` means there is at least one element left, `..`\n means there are 0 or more elements left. This can make a difference\n when refactoring, but shouldn't result in errors in the refactored code,\n since the wildcard pattern isn't used anyway.\n **Why is this bad?** The wildcard pattern is unneeded as the rest pattern\n can match that element as well.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct TupleStruct(u32, u32, u32);\n # let t = TupleStruct(1, 2, 3);\n // Bad\n match t {\n TupleStruct(0, .., _) => (),\n _ => (),\n }\n\n // Good\n match t {\n TupleStruct(0, ..) => (),\n _ => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "missing_const_for_fn", - "id_span": { - "path": "src/missing_const_for_fn.rs", - "line": 72 - }, - "group": "clippy::nursery", - "docs": " **What it does:**\n Suggests the use of `const` in functions and methods where possible.\n\n **Why is this bad?**\n\n Not having the function const prevents callers of the function from being const as well.\n\n **Known problems:**\n\n Const functions are currently still being worked on, with some features only being available\n on nightly. This lint does not consider all edge cases currently and the suggestions may be\n incorrect if you are using this lint on stable.\n\n Also, the lint only runs one pass over the code. Consider these two non-const functions:\n\n ```rust\n fn a() -> i32 {\n 0\n }\n fn b() -> i32 {\n a()\n }\n ```\n\n When running Clippy, the lint will only suggest to make `a` const, because `b` at this time\n can't be const as it calls a non-const function. Making `a` const and running Clippy again,\n will suggest to make `b` const, too.\n\n **Example:**\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n\n Could be a const fn:\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n const fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_docs_in_private_items", - "id_span": { - "path": "src/missing_doc.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns if there is missing doc for any documentable item (public or private).\n\n **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`\n allowed-by-default lint for\n public members, but has no way to enforce documentation of private items.\n This lint fixes that.\n\n **Known problems:** None.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_inline_in_public_items", - "id_span": { - "path": "src/missing_inline.rs", - "line": 55 - }, - "group": "clippy::restriction", - "docs": " **What it does:** it lints if an exported function, method, trait method with default impl, or trait method impl is not `#[inline]`.\n\n **Why is this bad?** In general, it is not. Functions can be inlined across\n crates when that's profitable as long as any form of LTO is used. When LTO is disabled,\n functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates\n might intend for most of the methods in their public API to be able to be inlined across\n crates even when LTO is disabled. For these types of crates, enabling this lint might make\n sense. It allows the crate to require all exported methods to be `#[inline]` by default, and\n then opt out for specific methods where this might not make sense.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo() {} // missing #[inline]\n fn ok() {} // ok\n #[inline] pub fn bar() {} // ok\n #[inline(always)] pub fn baz() {} // ok\n\n pub trait Bar {\n fn bar(); // ok\n fn def_bar() {} // missing #[inline]\n }\n\n struct Baz;\n impl Baz {\n fn private() {} // ok\n }\n\n impl Bar for Baz {\n fn bar() {} // ok - Baz is not exported\n }\n\n pub struct PubBaz;\n impl PubBaz {\n fn private() {} // ok\n pub fn not_ptrivate() {} // missing #[inline]\n }\n\n impl Bar for PubBaz {\n fn bar() {} // missing #[inline]\n fn def_bar() {} // missing #[inline]\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "modulo_arithmetic", - "id_span": { - "path": "src/modulo_arithmetic.rs", - "line": 26 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for modulo arithmetic.\n **Why is this bad?** The results of modulo (%) operation might differ\n depending on the language, when negative numbers are involved.\n If you interop with different languages it might be beneficial\n to double check all places that use modulo arithmetic.\n\n For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = -17 % 3;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "multiple_crate_versions", - "id_span": { - "path": "src/multiple_crate_versions.rs", - "line": 32 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks to see if multiple versions of a crate are being used.\n\n **Why is this bad?** This bloats the size of targets, and can lead to\n confusing error messages when structs or traits are used interchangeably\n between different versions of a crate.\n\n **Known problems:** Because this can be caused purely by the dependencies\n themselves, it's not always possible to fix this issue.\n\n **Example:**\n ```toml\n # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.\n [dependencies]\n ctrlc = \"=3.1.0\"\n ansi_term = \"=0.11.0\"\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutable_key_type", - "id_span": { - "path": "src/mut_key.rs", - "line": 50 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for sets/maps with mutable key types.\n **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and\n `BtreeSet` rely on either the hash or the order of keys be unchanging,\n so having types with interior mutability is a bad idea.\n\n **Known problems:** It's correct to use a struct, that contains interior mutability\n as a key, when its `Hash` implementation doesn't access any of the interior mutable types.\n However, this lint is unable to recognize this, so it causes a false positive in theses cases.\n The `bytes` crate is a great example of this.\n\n **Example:**\n ```rust\n use std::cmp::{PartialEq, Eq};\n use std::collections::HashSet;\n use std::hash::{Hash, Hasher};\n use std::sync::atomic::AtomicUsize;\n# #[allow(unused)]\n\n struct Bad(AtomicUsize);\n impl PartialEq for Bad {\n fn eq(&self, rhs: &Self) -> bool {\n ..\n ; unimplemented!();\n }\n }\n\n impl Eq for Bad {}\n\n impl Hash for Bad {\n fn hash(&self, h: &mut H) {\n ..\n ; unimplemented!();\n }\n }\n\n fn main() {\n let _: HashSet = HashSet::new();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_mut", - "id_span": { - "path": "src/mut_mut.rs", - "line": 24 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for instances of `mut mut` references.\n **Why is this bad?** Multiple `mut`s don't add anything meaningful to the\n source. This is either a copy'n'paste error, or it shows a fundamental\n misunderstanding of references.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut y = 1;\n let x = &mut &mut y;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_mutex_lock", - "id_span": { - "path": "src/mut_mutex_lock.rs", - "line": 40 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `&mut Mutex::lock` calls\n **Why is this bad?** `Mutex::lock` is less efficient than\n calling `Mutex::get_mut`. In addition you also have a statically\n guarantee that the mutex isn't locked, instead of just a runtime\n guarantee.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let mut value = value_mutex.lock().unwrap();\n *value += 1;\n ```\n Use instead:\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let value = value_mutex.get_mut().unwrap();\n *value += 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "unnecessary_mut_passed", - "id_span": { - "path": "src/mut_reference.rs", - "line": 25 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects passing a mutable reference to a function that only requires an immutable reference.\n\n **Why is this bad?** The mutable reference rules out all other references to\n the value. Also the code misleads about the intent of the call site.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n my_vec.push(&mut value)\n\n // Good\n my_vec.push(&value)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "debug_assert_with_mut_call", - "id_span": { - "path": "src/mutable_debug_assertion.rs", - "line": 28 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for function/method calls with a mutable parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.\n\n **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the\n compiler.\n Therefore mutating something in a `debug_assert!` macro results in different behaviour\n between a release and debug build.\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n debug_assert_eq!(vec![3].pop(), Some(3));\n // or\n fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }\n debug_assert!(take_a_mut_parameter(&mut 5));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutex_atomic", - "id_span": { - "path": "src/mutex_atomic.rs", - "line": 34 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usages of `Mutex` where an atomic will do.\n **Why is this bad?** Using a mutex just to make access to a plain bool or\n reference sequential is shooting flies with cannons.\n `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and\n faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # let y = true;\n\n // Bad\n # use std::sync::Mutex;\n let x = Mutex::new(&y);\n\n // Good\n # use std::sync::atomic::AtomicBool;\n let x = AtomicBool::new(y);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutex_integer", - "id_span": { - "path": "src/mutex_atomic.rs", - "line": 59 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for usages of `Mutex` where `X` is an integral type.\n\n **Why is this bad?** Using a mutex just to make access to a plain integer\n sequential is\n shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # use std::sync::Mutex;\n let x = Mutex::new(0usize);\n\n // Good\n # use std::sync::atomic::AtomicUsize;\n let x = AtomicUsize::new(0usize);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_arbitrary_self_type", - "id_span": { - "path": "src/needless_arbitrary_self_type.rs", - "line": 55 - }, - "group": "clippy::complexity", - "docs": " **What it does:** The lint checks for `self` in fn parameters that specify the `Self`-type explicitly\n **Why is this bad?** Increases the amount and decreases the readability of code\n\n **Known problems:** None\n\n **Example:**\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self: Self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n\n Could be rewritten as\n\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_bool", - "id_span": { - "path": "src/needless_bool.rs", - "line": 38 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions of the form `if c { true } else { false }` (or vice versa) and suggests using the condition directly.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** Maybe false positives: Sometimes, the two branches are\n painstakingly documented (which we, of course, do not detect), so they *may*\n have some value. Even then, the documentation can be rewritten to match the\n shorter code.\n\n **Example:**\n ```rust,ignore\n if x {\n false\n } else {\n true\n }\n ```\n Could be written as\n ```rust,ignore\n !x\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "bool_comparison", - "id_span": { - "path": "src/needless_bool.rs", - "line": 62 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions of the form `x == true`, `x != true` and order comparisons such as `x < true` (or vice versa) and\n suggest using the variable directly.\n\n **Why is this bad?** Unnecessary code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x == true {}\n if y == false {}\n ```\n use `x` directly:\n ```rust,ignore\n if x {}\n if !y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_borrow", - "id_span": { - "path": "src/needless_borrow.rs", - "line": 32 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for address of operations (`&`) that are going to be dereferenced immediately by the compiler.\n\n **Why is this bad?** Suggests that the receiver of the expression borrows\n the expression.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x: &i32 = &&&&&&5;\n\n // Good\n let x: &i32 = &5;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_borrowed_reference", - "id_span": { - "path": "src/needless_borrowed_ref.rs", - "line": 48 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for useless borrowed references.\n **Why is this bad?** It is mostly useless and make the code look more\n complex than it\n actually is.\n\n **Known problems:** It seems that the `&ref` pattern is sometimes useful.\n For instance in the following snippet:\n ```rust,ignore\n enum Animal {\n Cat(u64),\n Dog(u64),\n }\n\n fn foo(a: &Animal, b: &Animal) {\n match (a, b) {\n (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error\n (&Animal::Dog(ref c), &Animal::Dog(_)) => ()\n }\n }\n ```\n There is a lifetime mismatch error for `k` (indeed a and b have distinct\n lifetime).\n This can be fixed by using the `&ref` pattern.\n However, the code can also be fixed by much cleaner ways\n\n **Example:**\n ```rust\n let mut v = Vec::::new();\n let _ = v.iter_mut().filter(|&ref a| a.is_empty());\n ```\n This closure takes a reference on something that has been matched as a\n reference and\n de-referenced.\n As such, it could just be |a| a.is_empty()\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_continue", - "id_span": { - "path": "src/needless_continue.rs", - "line": 114 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** The lint checks for `if`-statements appearing in loops that contain a `continue` statement in either their main blocks or their\n `else`-blocks, when omitting the `else`-block possibly with some\n rearrangement of code can make the code easier to understand.\n\n **Why is this bad?** Having explicit `else` blocks for `if` statements\n containing `continue` in their THEN branch adds unnecessary branching and\n nesting to the code. Having an else block containing just `continue` can\n also be better written by grouping the statements following the whole `if`\n statement within the THEN block and omitting the else block completely.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n } else {\n continue;\n }\n println!(\"Hello, world\");\n }\n ```\n\n Could be rewritten as\n\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n println!(\"Hello, world\");\n }\n }\n ```\n\n As another example, the following code\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n } else {\n // Do something useful\n }\n # break;\n }\n ```\n Could be rewritten as\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n }\n // Do something useful\n # break;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_pass_by_value", - "id_span": { - "path": "src/needless_pass_by_value.rs", - "line": 50 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by value, but not consuming them in its\n body.\n\n **Why is this bad?** Taking arguments by reference is more flexible and can\n sometimes avoid\n unnecessary allocations.\n\n **Known problems:**\n * This lint suggests taking an argument by reference,\n however sometimes it is better to let users decide the argument type\n (by using `Borrow` trait, for example), depending on how the function is used.\n\n **Example:**\n ```rust\n fn foo(v: Vec) {\n assert_eq!(v.len(), 42);\n }\n ```\n should be\n ```rust\n fn foo(v: &[i32]) {\n assert_eq!(v.len(), 42);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": null - } - }, - { - "id": "needless_question_mark", - "id_span": { - "path": "src/needless_question_mark.rs", - "line": 57 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Suggests alternatives for useless applications of `?` in terminating expressions\n\n **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n Some(to.magic?)\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| Ok(t.magic?))\n }\n\n ```\n Use instead:\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n to.magic\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| t.magic)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_update", - "id_span": { - "path": "src/needless_update.rs", - "line": 43 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for needlessly including a base struct on update when all fields are changed anyway.\n\n This lint is not applied to structs marked with\n [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).\n\n **Why is this bad?** This will cost resources (because the base has to be\n somewhere), and make the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Point {\n # x: i32,\n # y: i32,\n # z: i32,\n # }\n # let zero_point = Point { x: 0, y: 0, z: 0 };\n\n // Bad\n Point {\n x: 1,\n y: 1,\n z: 1,\n ..zero_point\n };\n\n // Ok\n Point {\n x: 1,\n y: 1,\n ..zero_point\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "neg_cmp_op_on_partial_ord", - "id_span": { - "path": "src/neg_cmp_op_on_partial_ord.rs", - "line": 41 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the usage of negated comparison operators on types which only implement\n `PartialOrd` (e.g., `f64`).\n\n **Why is this bad?**\n These operators make it easy to forget that the underlying types actually allow not only three\n potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is\n especially easy to miss if the operator based comparison result is negated.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::cmp::Ordering;\n\n // Bad\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = !(a <= b);\n\n // Good\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = match a.partial_cmp(&b) {\n None | Some(Ordering::Greater) => true,\n _ => false,\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "neg_multiply", - "id_span": { - "path": "src/neg_multiply.rs", - "line": 21 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for multiplication by -1 as a form of negation.\n **Why is this bad?** It's more readable to just negate.\n\n **Known problems:** This only catches integers (for now).\n\n **Example:**\n ```ignore\n x * -1\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "new_without_default", - "id_span": { - "path": "src/new_without_default.rs", - "line": 48 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for types with a `fn new() -> Self` method and no implementation of\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).\n\n **Why is this bad?** The user might expect to be able to use\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the\n type can be constructed without arguments.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n\n ```ignore\n struct Foo(Bar);\n\n impl Foo {\n fn new() -> Self {\n Foo(Bar::new())\n }\n }\n ```\n\n To fix the lint, add a `Default` implementation that delegates to `new`:\n\n ```ignore\n struct Foo(Bar);\n\n impl Default for Foo {\n fn default() -> Self {\n Foo::new()\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "no_effect", - "id_span": { - "path": "src/no_effect.rs", - "line": 22 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for statements which have no effect.\n **Why is this bad?** Similar to dead code, these statements are actually\n executed. However, as they have no effect, all they do is make the code less\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unnecessary_operation", - "id_span": { - "path": "src/no_effect.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expression statements that can be reduced to a sub-expression.\n\n **Why is this bad?** Expressions by themselves often have no side-effects.\n Having such expressions reduces readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n compute_array()[0];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "declare_interior_mutable_const", - "id_span": { - "path": "src/non_copy_const.rs", - "line": 69 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for declaration of `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` should better be replaced by a `static` item if a global\n variable is wanted, or replaced by a `const fn` if a constructor is wanted.\n\n **Known problems:** A \"non-constant\" const item is a legacy way to supply an\n initialized value to downstream `static` items (e.g., the\n `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,\n and this lint should be suppressed.\n\n Even though the lint avoids triggering on a constant whose type has enums that have variants\n with interior mutability, and its value uses non interior mutable variants (see\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);\n it complains about associated constants without default values only based on its types;\n which might not be preferable.\n There're other enums plus associated constants cases that the lint cannot handle.\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n\n // Bad.\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", - "applicability": null - }, - { - "id": "borrow_interior_mutable_const", - "id_span": { - "path": "src/non_copy_const.rs", - "line": 110 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks if `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` value should be stored inside a `static` item.\n\n **Known problems:** When an enum has variants with interior mutability, use of its non\n interior mutable variants can generate false positives. See issue\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n\n // Bad.\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = CONST_ATOM;\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", - "applicability": null - }, - { - "id": "similar_names", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 27 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for names that are very similar and thus confusing.\n **Why is this bad?** It's hard to distinguish between names that differ only\n by a single character.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let checked_exp = something;\n let checked_expr = something_else;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "many_single_char_names", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 45 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for too many variables whose name consists of a single character.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let (a, b, c, d, e, f, g) = (...);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "just_underscores_and_digits", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 65 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks if you have variables whose name consists of just underscores and digits.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```rust\n let _1 = 1;\n let ___1 = 1;\n let __1___2 = 11;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "nonsensical_open_options", - "id_span": { - "path": "src/open_options.rs", - "line": 23 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for duplicate open options as well as combinations that make no sense.\n\n **Why is this bad?** In the best case, the code will be harder to read than\n necessary. I don't know the worst case.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::fs::OpenOptions;\n\n OpenOptions::new().read(true).truncate(true);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_env_unwrap", - "id_span": { - "path": "src/option_env_unwrap.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of `option_env!(...).unwrap()` and suggests usage of the `env!` macro.\n\n **Why is this bad?** Unwrapping the result of `option_env!` will panic\n at run-time if the environment variable doesn't exist, whereas `env!`\n catches it at compile-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n let _ = option_env!(\"HOME\").unwrap();\n ```\n\n Is better expressed as:\n\n ```rust,no_run\n let _ = env!(\"HOME\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_if_let_else", - "id_span": { - "path": "src/option_if_let_else.rs", - "line": 59 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Lints usage of `if let Some(v) = ... { y } else { x }` which is more\n idiomatically done with `Option::map_or` (if the else bit is a pure\n expression) or `Option::map_or_else` (if the else bit is an impure\n expression).\n\n **Why is this bad?**\n Using the dedicated functions of the Option type is clearer and\n more concise than an `if let` expression.\n\n **Known problems:**\n This lint uses a deliberately conservative metric for checking\n if the inside of either body contains breaks or continues which will\n cause it to not suggest a fix if either block contains a loop with\n continues or breaks contained within the loop.\n\n **Example:**\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n 5\n };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n let y = do_complicated_function();\n y*y\n };\n ```\n\n should be\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = optional.map_or(5, |foo| foo);\n let _ = optional.map_or_else(||{\n let y = do_complicated_function();\n y*y\n }, |foo| foo);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "overflow_check_conditional", - "id_span": { - "path": "src/overflow_check_conditional.rs", - "line": 21 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects classic underflow/overflow checks.\n **Why is this bad?** Most classic C underflow/overflow checks will fail in\n Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n a + b < a;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panic_in_result_fn", - "id_span": { - "path": "src/panic_in_result_fn.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.\n **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.\n\n **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.\n\n **Example:**\n\n ```rust\n fn result_with_panic() -> Result\n {\n panic!(\"error\");\n }\n ```\n Use instead:\n ```rust\n fn result_without_panic() -> Result {\n Err(String::from(\"error\"))\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panic", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 19 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `panic!`.\n **Why is this bad?** `panic!` will stop the execution of the executable\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n panic!(\"even with a good reason\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unimplemented", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 35 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `unimplemented!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unimplemented!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "todo", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 51 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `todo!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n todo!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unreachable", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 67 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `unreachable!`.\n **Why is this bad?** This macro can cause code to panic\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unreachable!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "partialeq_ne_impl", - "id_span": { - "path": "src/partialeq_ne_impl.rs", - "line": 27 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for manual re-implementations of `PartialEq::ne`.\n **Why is this bad?** `PartialEq::ne` is required to always return the\n negated result of `PartialEq::eq`, which is exactly what the default\n implementation does. Therefore, there should never be any need to\n re-implement it.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo;\n\n impl PartialEq for Foo {\n fn eq(&self, other: &Foo) -> bool { true }\n fn ne(&self, other: &Foo) -> bool { !(self == other) }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trivially_copy_pass_by_ref", - "id_span": { - "path": "src/pass_by_ref_or_value.rs", - "line": 52 - }, - "group": "clippy::pedantic", - "docs": " **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\n pass by value.\n\n **Why is this bad?** In many calling conventions instances of structs will\n be passed through registers if they fit into two or less general purpose\n registers.\n\n **Known problems:** This lint is target register size dependent, it is\n limited to 32-bit to try and reduce portability problems between 32 and\n 64-bit, but if you are compiling for 8 or 16-bit targets then the limit\n will be different.\n\n The configuration option `trivial_copy_size_limit` can be set to override\n this limit for a project.\n\n This lint attempts to allow passing arguments by reference if a reference\n to that argument is returned. This is implemented by comparing the lifetime\n of the argument and return value for equality. However, this can cause\n false positives in cases involving multiple lifetimes that are bounded by\n each other.\n\n **Example:**\n\n ```rust\n // Bad\n fn foo(v: &u32) {}\n ```\n\n ```rust\n // Better\n fn foo(v: u32) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "large_types_passed_by_value", - "id_span": { - "path": "src/pass_by_ref_or_value.rs", - "line": 84 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by value, where the argument type is `Copy` and large enough to be worth considering\n passing by reference. Does not trigger if the function is being exported,\n because that might induce API breakage, if the parameter is declared as mutable,\n or if the argument is a `self`.\n\n **Why is this bad?** Arguments passed by value might result in an unnecessary\n shallow copy, taking up more space in the stack and requiring a call to\n `memcpy`, which can be expensive.\n\n **Example:**\n\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Bad\n fn foo(v: TooLarge) {}\n ```\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Good\n fn foo(v: &TooLarge) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "path_buf_push_overwrite", - "id_span": { - "path": "src/path_buf_push_overwrite.rs", - "line": 36 - }, - "group": "clippy::nursery", - "docs": " **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) calls on `PathBuf` that can cause overwrites.\n\n **Why is this bad?** Calling `push` with a root path at the start can overwrite the\n previous defined path.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"/bar\");\n assert_eq!(x, PathBuf::from(\"/bar\"));\n ```\n Could be written:\n\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"bar\");\n assert_eq!(x, PathBuf::from(\"/foo/bar\"));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "pattern_type_mismatch", - "id_span": { - "path": "src/pattern_type_mismatch.rs", - "line": 79 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for patterns that aren't exact representations of the types they are applied to.\n\n To satisfy this lint, you will have to adjust either the expression that is matched\n against or the pattern itself, as well as the bindings that are introduced by the\n adjusted patterns. For matching you will have to either dereference the expression\n with the `*` operator, or amend the patterns to explicitly match against `&`\n or `&mut ` depending on the reference mutability. For the bindings you need\n to use the inverse. You can leave them as plain bindings if you wish for the value\n to be copied, but you must use `ref mut ` or `ref ` to construct\n a reference into the matched structure.\n\n If you are looking for a way to learn about ownership semantics in more detail, it\n is recommended to look at IDE options available to you to highlight types, lifetimes\n and reference semantics in your code. The available tooling would expose these things\n in a general way even outside of the various pattern matching mechanics. Of course\n this lint can still be used to highlight areas of interest and ensure a good understanding\n of ownership semantics.\n\n **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable\n because it increases ownership hints in the code, and will guard against some changes\n in ownership.\n\n **Known problems:** None.\n\n **Example:**\n\n This example shows the basic adjustments necessary to satisfy the lint. Note how\n the matched expression is explicitly dereferenced with `*` and the `inner` variable\n is bound to a shared borrow via `ref inner`.\n\n ```rust,ignore\n // Bad\n let value = &Some(Box::new(23));\n match value {\n Some(inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n\n // Good\n let value = &Some(Box::new(23));\n match *value {\n Some(ref inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n ```\n\n The following example demonstrates one of the advantages of the more verbose style.\n Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable\n borrow, while `b` is simply taken by value. This ensures that the loop body cannot\n accidentally modify the wrong part of the structure.\n\n ```rust,ignore\n // Bad\n let mut values = vec![(2, 3), (3, 4)];\n for (a, b) in &mut values {\n *a += *b;\n }\n\n // Good\n let mut values = vec![(2, 3), (3, 4)];\n for &mut (ref mut a, b) in &mut values {\n *a += b;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "precedence", - "id_span": { - "path": "src/precedence.rs", - "line": 44 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:\n * mixed usage of arithmetic and bit shifting/combining operators without\n parentheses\n * a \"negative\" numeric literal (which is really a unary `-` followed by a\n numeric literal)\n followed by a method call\n\n **Why is this bad?** Not everyone knows the precedence of those operators by\n heart, so expressions like these may trip others trying to reason about the\n code.\n\n **Known problems:** None.\n\n **Example:**\n * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7\n * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ptr_arg", - "id_span": { - "path": "src/ptr.rs", - "line": 69 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless the references are mutable. It will also suggest you\n replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\n calls.\n\n **Why is this bad?** Requiring the argument to be of the specific size\n makes the function less useful for no benefit; slices in the form of `&[T]`\n or `&str` usually suffice and can be obtained from other types, too.\n\n **Known problems:** The lint does not follow data. So if you have an\n argument `x` and write `let y = x; y.clone()` the lint will not suggest\n changing that `.clone()` to `.to_owned()`.\n\n Other functions called from this function taking a `&String` or `&Vec`\n argument may also fail to compile if you change the argument. Applying\n this lint on them will fix the problem, but they may be in other crates.\n\n One notable example of a function that may cause issues, and which cannot\n easily be changed due to being in the standard library is `Vec::contains`.\n when called on a `Vec>`. If a `&Vec` is passed to that method then\n it will compile, but if a `&[T]` is passed then it will not compile.\n\n ```ignore\n fn cannot_take_a_slice(v: &Vec) -> bool {\n let vec_of_vecs: Vec> = some_other_fn();\n\n vec_of_vecs.contains(v)\n }\n ```\n\n Also there may be `fn(&Vec)`-typed references pointing to your function.\n If you have them, you will get a compiler error after applying this lint's\n suggestions. You then have the choice to undo your changes or change the\n type of the reference.\n\n Note that if the function is part of your public interface, there may be\n other crates referencing it, of which you may not be aware. Carefully\n deprecate the function before applying the lint suggestions in this case.\n\n **Example:**\n ```ignore\n // Bad\n fn foo(&Vec) { .. }\n\n // Good\n fn foo(&[u32]) { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "cmp_null", - "id_span": { - "path": "src/ptr.rs", - "line": 95 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint checks for equality comparisons with `ptr::null`\n **Why is this bad?** It's easier and more readable to use the inherent\n `.is_null()`\n method instead\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n if x == ptr::null {\n ..\n }\n\n // Good\n if x.is_null() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_from_ref", - "id_span": { - "path": "src/ptr.rs", - "line": 117 - }, - "group": "clippy::correctness", - "docs": " **What it does:** This lint checks for functions that take immutable references and return mutable ones.\n\n **Why is this bad?** This is trivially unsound, as one can create two\n mutable references from the same (immutable!) source.\n This [error](https://github.com/rust-lang/rust/issues/39465)\n actually lead to an interim Rust release 1.15.1.\n\n **Known problems:** To be on the conservative side, if there's at least one\n mutable reference with the output lifetime, this lint will not trigger.\n In practice, this case is unlikely anyway.\n\n **Example:**\n ```ignore\n fn foo(&Foo) -> &mut Bar { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ptr_eq", - "id_span": { - "path": "src/ptr_eq.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** Use `std::ptr::eq` when applicable\n **Why is this bad?** `ptr::eq` can be used to compare `&T` references\n (which coerce to `*const T` implicitly) by their address rather than\n comparing the values they point to.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(a as *const _ as usize == b as *const _ as usize);\n ```\n Use instead:\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(std::ptr::eq(a, b));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ptr_offset_with_cast", - "id_span": { - "path": "src/ptr_offset_with_cast.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an `isize`.\n\n **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric\n cast by using the `add` method instead.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.offset(offset as isize);\n }\n ```\n\n Could be written:\n\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.add(offset);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "question_mark", - "id_span": { - "path": "src/question_mark.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for expressions that could be replaced by the question mark operator.\n **Why is this bad?** Question mark usage is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n if option.is_none() {\n return None;\n }\n ```\n\n Could be written:\n\n ```ignore\n option?;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "range_zip_with_len", - "id_span": { - "path": "src/ranges.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for zipping a collection with the range of `0.._.len()`.\n\n **Why is this bad?** The code is better expressed with `.enumerate()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = vec![1];\n x.iter().zip(0..x.len());\n ```\n Could be written as\n ```rust\n # let x = vec![1];\n x.iter().enumerate();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "range_plus_one", - "id_span": { - "path": "src/ranges.rs", - "line": 74 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for exclusive ranges where 1 is added to the upper bound, e.g., `x..(y+1)`.\n\n **Why is this bad?** The code is more readable with an inclusive range\n like `x..=y`.\n\n **Known problems:** Will add unnecessary pair of parentheses when the\n expression is not wrapped in a pair but starts with a opening parenthesis\n and ends with a closing one.\n I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.\n\n Also in many cases, inclusive ranges are still slower to run than\n exclusive ranges, because they essentially add an extra branch that\n LLVM may fail to hoist out of the loop.\n\n This will cause a warning that cannot be fixed if the consumer of the\n range only accepts a specific range type, instead of the generic\n `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..(y+1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..=y { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "range_minus_one", - "id_span": { - "path": "src/ranges.rs", - "line": 99 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., `x..=(y-1)`.\n\n **Why is this bad?** The code is more readable with an exclusive range\n like `x..y`.\n\n **Known problems:** This will cause a warning that cannot be fixed if\n the consumer of the range only accepts a specific range type, instead of\n the generic `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..=(y-1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..y { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "reversed_empty_ranges", - "id_span": { - "path": "src/ranges.rs", - "line": 132 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for range expressions `x..y` where both `x` and `y` are constant and `x` is greater or equal to `y`.\n\n **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.\n Moreover, trying to use a reversed range to index a slice will panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n fn main() {\n (10..=0).for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[3..1];\n }\n ```\n Use instead:\n ```rust\n fn main() {\n (0..=10).rev().for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[1..3];\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "manual_range_contains", - "id_span": { - "path": "src/ranges.rs", - "line": 159 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for expressions like `x >= 3 && x < 8` that could be more readably expressed as `(3..8).contains(x)`.\n\n **Why is this bad?** `contains` expresses the intent better and has less\n failure modes (such as fencepost errors or using `||` instead of `&&`).\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // given\n let x = 6;\n\n assert!(x >= 3 && x < 8);\n ```\n Use instead:\n ```rust\n# let x = 6;\n assert!((3..8).contains(&x));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_clone", - "id_span": { - "path": "src/redundant_clone.rs", - "line": 63 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned value that is going to be dropped without further use.\n\n **Why is this bad?** It is not always possible for the compiler to eliminate useless\n allocations and deallocations generated by redundant `clone()`s.\n\n **Known problems:**\n\n False-negatives: analysis performed by this lint is conservative and limited.\n\n **Example:**\n ```rust\n # use std::path::Path;\n # #[derive(Clone)]\n # struct Foo;\n # impl Foo {\n # fn new() -> Self { Foo {} }\n # }\n # fn call(x: Foo) {}\n {\n let x = Foo::new();\n call(x.clone());\n call(x.clone()); // this can just pass `x`\n }\n\n [\"lorem\", \"ipsum\"].join(\" \").to_string();\n\n Path::new(\"/a/b\").join(\"c\").to_path_buf();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_closure_call", - "id_span": { - "path": "src/redundant_closure_call.rs", - "line": 32 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects closures called in the same expression where they are defined.\n\n **Why is this bad?** It is unnecessarily adding to the expression's\n complexity.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = (|| 42)()\n\n // Good\n let a = 42\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_else", - "id_span": { - "path": "src/redundant_else.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `else` blocks that can be removed without changing semantics.\n **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.\n\n **Known problems:** Some may prefer to keep the `else` block for clarity.\n\n **Example:**\n\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n } else {\n print!(\"Moving on...\");\n }\n }\n ```\n Use instead:\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n }\n print!(\"Moving on...\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_field_names", - "id_span": { - "path": "src/redundant_field_names.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for fields in struct literals where shorthands could be used.\n\n **Why is this bad?** If the field and variable names are the same,\n the field name is redundant.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let bar: u8 = 123;\n\n struct Foo {\n bar: u8,\n }\n\n let foo = Foo { bar: bar };\n ```\n the last line can be simplified to\n ```ignore\n let foo = Foo { bar };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_pub_crate", - "id_span": { - "path": "src/redundant_pub_crate.rs", - "line": 30 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they are inside a private module.\n\n **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent\n module's visibility.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n mod internal {\n pub(crate) fn internal_fn() { }\n }\n ```\n This function is not visible outside the module and it can be declared with `pub` or\n private visibility\n ```rust\n mod internal {\n pub fn internal_fn() { }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_slicing", - "id_span": { - "path": "src/redundant_slicing.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for redundant slicing expressions which use the full range, and do not change the type.\n\n **Why is this bad?** It unnecessarily adds complexity to the expression.\n\n **Known problems:** If the type being sliced has an implementation of `Index`\n that actually changes anything then it can't be removed. However, this would be surprising\n to people reading the code and should have a note with it.\n\n **Example:**\n\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n &x[..]\n }\n ```\n Use instead:\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_static_lifetimes", - "id_span": { - "path": "src/redundant_static_lifetimes.rs", - "line": 30 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for constants and statics with an explicit `'static` lifetime.\n **Why is this bad?** Adding `'static` to every reference can create very\n complicated types.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n ```\n This code can be rewritten as\n ```ignore\n const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ref_option_ref", - "id_span": { - "path": "src/ref_option_ref.rs", - "line": 28 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `&Option<&T>`.\n **Why is this bad?** Since `&` is Copy, it's useless to have a\n reference on `Option<&T>`.\n\n **Known problems:** It may be irrelevant to use this lint on\n public API code as it will make a breaking change to apply it.\n\n **Example:**\n\n ```rust,ignore\n let x: &Option<&u32> = &Some(&0u32);\n ```\n Use instead:\n ```rust,ignore\n let x: Option<&u32> = Some(&0u32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "deref_addrof", - "id_span": { - "path": "src/reference.rs", - "line": 29 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `*&` and `*&mut` in expressions.\n **Why is this bad?** Immediately dereferencing a reference is no-op and\n makes the code less clear.\n\n **Known problems:** Multiple dereference/addrof pairs are not handled so\n the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = f(*&mut b);\n let c = *&d;\n\n // Good\n let a = f(b);\n let c = d;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ref_in_deref", - "id_span": { - "path": "src/reference.rs", - "line": 120 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for references in expressions that use auto dereference.\n\n **Why is this bad?** The reference is a no-op and is automatically\n dereferenced by the compiler and makes the code less clear.\n\n **Example:**\n ```rust\n struct Point(u32, u32);\n let point = Point(30, 20);\n let x = (&point).0;\n ```\n Use instead:\n ```rust\n # struct Point(u32, u32);\n # let point = Point(30, 20);\n let x = point.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "invalid_regex", - "id_span": { - "path": "src/regex.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\n regex syntax.\n\n **Why is this bad?** This will lead to a runtime panic.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n Regex::new(\"|\")\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trivial_regex", - "id_span": { - "path": "src/regex.rs", - "line": 46 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).\n\n **Why is this bad?** Matching the regex can likely be replaced by `==` or\n `str::starts_with`, `str::ends_with` or `std::contains` or other `str`\n methods.\n\n **Known problems:** If the same regex is going to be applied to multiple\n inputs, the precomputations done by `Regex` construction can give\n significantly better performance than any of the `str`-based methods.\n\n **Example:**\n ```ignore\n Regex::new(\"^foobar\")\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "repeat_once", - "id_span": { - "path": "src/repeat_once.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. - `.to_string()` for `str`\n - `.clone()` for `String`\n - `.to_vec()` for `slice`\n\n **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.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n let x = String::from(\"hello world\").repeat(1);\n }\n ```\n Use instead:\n ```rust\n fn main() {\n let x = String::from(\"hello world\").clone();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "let_and_return", - "id_span": { - "path": "src/returns.rs", - "line": 38 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `let`-bindings, which are subsequently returned.\n\n **Why is this bad?** It is just extraneous code. Remove it to make your code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo() -> String {\n let x = String::new();\n x\n }\n ```\n instead, use\n ```\n fn foo() -> String {\n String::new()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_return", - "id_span": { - "path": "src/returns.rs", - "line": 63 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for return statements at the end of a block.\n **Why is this bad?** Removing the `return` and semicolon will make the code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n simplify to\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "self_assignment", - "id_span": { - "path": "src/self_assignment.rs", - "line": 29 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for explicit self-assignments.\n **Why is this bad?** Self-assignments are redundant and unlikely to be\n intentional.\n\n **Known problems:** If expression contains any deref coercions or\n indexing operations they are assumed not to have any side effects.\n\n **Example:**\n\n ```rust\n struct Event {\n id: usize,\n x: i32,\n y: i32,\n }\n\n fn copy_position(a: &mut Event, b: &Event) {\n a.x = b.x;\n a.y = a.y;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "semicolon_if_nothing_returned", - "id_span": { - "path": "src/semicolon_if_nothing_returned.rs", - "line": 30 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Looks for blocks of expressions and fires if the last expression returns `()` but is not followed by a semicolon.\n\n **Why is this bad?** The semicolon might be optional but when\n extending the block with new code, it doesn't require a change in previous last line.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n println!(\"Hello world\")\n }\n ```\n Use instead:\n ```rust\n fn main() {\n println!(\"Hello world\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "serde_api_misuse", - "id_span": { - "path": "src/serde_api.rs", - "line": 16 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for mis-uses of the serde API.\n **Why is this bad?** Serde is very finnicky about how its API should be\n used, but the type system can't be used to enforce it (yet?).\n\n **Known problems:** None.\n\n **Example:** Implementing `Visitor::visit_string` but not\n `Visitor::visit_str`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_same", - "id_span": { - "path": "src/shadow.rs", - "line": 34 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.\n\n **Why is this bad?** Not much, in fact it's a very common pattern in Rust\n code. Still, some may opt to avoid it in their code base, they can set this\n lint to `Warn`.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n # let x = 1;\n // Bad\n let x = &x;\n\n // Good\n let y = &x; // use different variable name\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_reuse", - "id_span": { - "path": "src/shadow.rs", - "line": 61 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while reusing the original value.\n\n **Why is this bad?** Not too much, in fact it's a common pattern in Rust\n code. Still, some argue that name shadowing like this hurts readability,\n because a value may be bound to different things depending on position in\n the code.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n let x = 2;\n let x = x + 1;\n ```\n use different variable name:\n ```rust\n let x = 2;\n let y = x + 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_unrelated", - "id_span": { - "path": "src/shadow.rs", - "line": 93 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use\n the original value.\n\n **Why is this bad?** Name shadowing can hurt readability, especially in\n large code bases, because it is easy to lose track of the active binding at\n any place in the code. This can be alleviated by either giving more specific\n names to bindings or introducing more scopes to contain the bindings.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns. Note that\n `allow`/`warn`/`deny`/`forbid` attributes only work on the function level\n for this lint.\n\n **Example:**\n ```rust\n # let y = 1;\n # let z = 2;\n let x = y;\n\n // Bad\n let x = z; // shadows the earlier binding\n\n // Good\n let w = z; // use different variable name\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_component_path_imports", - "id_span": { - "path": "src/single_component_path_imports.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** Checking for imports with single component use path.\n **Why is this bad?** Import with single component use path such as `use cratename;`\n is not necessary, and thus should be removed.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use regex;\n\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n Better as\n ```rust,ignore\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "size_of_in_element_count", - "id_span": { - "path": "src/size_of_in_element_count.rs", - "line": 31 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Detects expressions where `size_of::` or `size_of_val::` is used as a\n count of elements of type `T`\n\n **Why is this bad?** These functions expect a count\n of `T` and not a number of bytes\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::ptr::copy_nonoverlapping;\n # use std::mem::size_of;\n const SIZE: usize = 128;\n let x = [2u8; SIZE];\n let mut y = [2u8; SIZE];\n unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "slow_vector_initialization", - "id_span": { - "path": "src/slow_vector_initialization.rs", - "line": 37 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks slow zero-filled vector initialization\n **Why is this bad?** These structures are non-idiomatic and less efficient than simply using\n `vec![0; len]`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use core::iter::repeat;\n # let len = 4;\n\n // Bad\n let mut vec1 = Vec::with_capacity(len);\n vec1.resize(len, 0);\n\n let mut vec2 = Vec::with_capacity(len);\n vec2.extend(repeat(0).take(len));\n\n // Good\n let mut vec1 = vec![0; len];\n let mut vec2 = vec![0; len];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "stable_sort_primitive", - "id_span": { - "path": "src/stable_sort_primitive.rs", - "line": 36 - }, - "group": "clippy::perf", - "docs": " **What it does:** When sorting primitive values (integers, bools, chars, as well\n as arrays, slices, and tuples of such items), it is better to\n use an unstable sort than a stable sort.\n\n **Why is this bad?**\n Using a stable sort consumes more memory and cpu cycles. Because\n values which compare equal are identical, preserving their\n relative order (the guarantee that a stable sort provides) means\n nothing, while the extra costs still apply.\n\n **Known problems:**\n None\n\n **Example:**\n\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort();\n ```\n Use instead:\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort_unstable();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_add_assign", - "id_span": { - "path": "src/strings.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for string appends of the form `x = x + y` (without `let`!).\n\n **Why is this bad?** It's not really bad, but some people think that the\n `.push_str(_)` method is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut x = \"Hello\".to_owned();\n x = x + \", World\";\n\n // More readable\n x += \", World\";\n x.push_str(\", World\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_add", - "id_span": { - "path": "src/strings.rs", - "line": 65 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for all instances of `x + _` where `x` is of type `String`, but only if [`string_add_assign`](#string_add_assign) does *not*\n match.\n\n **Why is this bad?** It's not bad in and of itself. However, this particular\n `Add` implementation is asymmetric (the other operand need not be `String`,\n but `x` does), while addition as mathematically defined is symmetric, also\n the `String::push_str(_)` function is a perfectly good replacement.\n Therefore, some dislike it and wish not to have it in their code.\n\n That said, other people think that string addition, having a long tradition\n in other languages is actually fine, which is why we decided to make this\n particular lint `allow` by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = \"Hello\".to_owned();\n x + \", World\";\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_lit_as_bytes", - "id_span": { - "path": "src/strings.rs", - "line": 107 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for the `as_bytes` method called on string literals that contain only ASCII characters.\n\n **Why is this bad?** Byte string literals (e.g., `b\"foo\"`) can be used\n instead. They are shorter but less discoverable than `as_bytes()`.\n\n **Known Problems:**\n `\"str\".as_bytes()` and the suggested replacement of `b\"str\"` are not\n equivalent because they have different types. The former is `&[u8]`\n while the latter is `&[u8; 3]`. That means in general they will have a\n different set of methods and different trait implementations.\n\n ```compile_fail\n fn f(v: Vec) {}\n\n f(\"...\".as_bytes().to_owned()); // works\n f(b\"...\".to_owned()); // does not work, because arg is [u8; 3] not Vec\n\n fn g(r: impl std::io::Read) {}\n\n g(\"...\".as_bytes()); // works\n g(b\"...\"); // does not work\n ```\n\n The actual equivalent of `\"str\".as_bytes()` with the same type is not\n `b\"str\"` but `&b\"str\"[..]`, which is a great deal of punctuation and not\n more readable than a function call.\n\n **Example:**\n ```rust\n // Bad\n let bs = \"a byte string\".as_bytes();\n\n // Good\n let bs = b\"a byte string\";\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_from_utf8_as_bytes", - "id_span": { - "path": "src/strings.rs", - "line": 196 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Check if the string is transformed to byte array and casted back to string.\n **Why is this bad?** It's unnecessary, the string can be used directly.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = std::str::from_utf8(&\"Hello World!\".as_bytes()[6..11]).unwrap();\n ```\n could be written as\n ```rust\n let _ = &\"Hello World!\"[6..11];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "str_to_string", - "id_span": { - "path": "src/strings.rs", - "line": 313 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better\n expressed with `.to_owned()`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let _ = \"str\".to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let _ = \"str\".to_owned();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_to_string", - "id_span": { - "path": "src/strings.rs", - "line": 362 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `String`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let msg = String::from(\"Hello World\");\n let _ = msg.to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let msg = String::from(\"Hello World\");\n let _ = msg.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_operation_groupings", - "id_span": { - "path": "src/suspicious_operation_groupings.rs", - "line": 60 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for unlikely usages of binary operators that are almost\n certainly typos and/or copy/paste errors, given the other usages\n of binary operators nearby.\n **Why is this bad?**\n They are probably bugs and if they aren't then they look like bugs\n and you should add a comment explaining why you are doing such an\n odd set of operations.\n **Known problems:**\n There may be some false positives if you are trying to do something\n unusual that happens to look like a typo.\n\n **Example:**\n\n ```rust\n struct Vec3 {\n x: f64,\n y: f64,\n z: f64,\n }\n\n impl Eq for Vec3 {}\n\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // This should trigger the lint because `self.x` is compared to `other.y`\n self.x == other.y && self.y == other.y && self.z == other.z\n }\n }\n ```\n Use instead:\n ```rust\n # struct Vec3 {\n # x: f64,\n # y: f64,\n # z: f64,\n # }\n // same as above except:\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // Note we now compare other.x to self.x\n self.x == other.x && self.y == other.y && self.z == other.z\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_arithmetic_impl", - "id_span": { - "path": "src/suspicious_trait_impl.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. subtracting elements in an Add impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl Add for Foo {\n type Output = Foo;\n\n fn add(self, other: Foo) -> Foo {\n Foo(self.0 - other.0)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_op_assign_impl", - "id_span": { - "path": "src/suspicious_trait_impl.rs", - "line": 48 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. subtracting elements in an AddAssign impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl AddAssign for Foo {\n fn add_assign(&mut self, other: Foo) {\n *self = *self - other;\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_swap", - "id_span": { - "path": "src/swap.rs", - "line": 36 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for manual swapping.\n **Why is this bad?** The `std::mem::swap` function exposes the intent better\n without deinitializing or copying either variable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut a = 42;\n let mut b = 1337;\n\n let t = b;\n b = a;\n a = t;\n ```\n Use std::mem::swap():\n ```rust\n let mut a = 1;\n let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "almost_swapped", - "id_span": { - "path": "src/swap.rs", - "line": 61 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `foo = bar; bar = foo` sequences.\n **Why is this bad?** This looks like a failed attempt to swap.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n a = b;\n b = a;\n ```\n If swapping is intended, use `swap()` instead:\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "tabs_in_doc_comments", - "id_span": { - "path": "src/tabs_in_doc_comments.rs", - "line": 54 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks doc comments for usage of tab characters.\n **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.\n To keep a consistent view on the source, also doc comments should not have tabs.\n Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the\n display settings of the author and reader differ.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n ///\n /// Struct to hold two strings:\n /// \t- first\t\tone\n /// \t- second\tone\n pub struct DoubleString {\n ///\n /// \t- First String:\n /// \t\t- needs to be inside here\n first_string: String,\n ///\n /// \t- Second String:\n /// \t\t- needs to be inside here\n second_string: String,\n}\n ```\n\n Will be converted to:\n ```rust\n ///\n /// Struct to hold two strings:\n /// - first one\n /// - second one\n pub struct DoubleString {\n ///\n /// - First String:\n /// - needs to be inside here\n first_string: String,\n ///\n /// - Second String:\n /// - needs to be inside here\n second_string: String,\n}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "temporary_assignment", - "id_span": { - "path": "src/temporary_assignment.rs", - "line": 19 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for construction of a structure or tuple just to assign a value in it.\n\n **Why is this bad?** Readability. If the structure is only created to be\n updated, why not write the structure you want in the first place?\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n (0, 0).0 = 1\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "to_digit_is_some", - "id_span": { - "path": "src/to_digit_is_some.rs", - "line": 27 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.\n **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's\n more straight forward to use the dedicated `is_digit` method.\n\n **Example:**\n ```rust\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.to_digit(radix).is_some();\n ```\n can be written as:\n ```\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.is_digit(radix);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "to_string_in_display", - "id_span": { - "path": "src/to_string_in_display.rs", - "line": 40 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for uses of `to_string()` in `Display` traits.\n **Why is this bad?** Usually `to_string` is implemented indirectly\n via `Display`. Hence using it while implementing `Display` would\n lead to infinite recursion.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.to_string())\n }\n }\n\n ```\n Use instead:\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.0)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "type_repetition_in_bounds", - "id_span": { - "path": "src/trait_bounds.rs", - "line": 28 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** This lint warns about unnecessary type repetitions in trait bounds\n **Why is this bad?** Repeating the type for every bound makes the code\n less readable than combining the bounds\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo(t: T) where T: Copy, T: Clone {}\n ```\n\n Could be written as:\n\n ```rust\n pub fn foo(t: T) where T: Copy + Clone {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trait_duplication_in_bounds", - "id_span": { - "path": "src/trait_bounds.rs", - "line": 57 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for cases where generics are being used and multiple syntax specifications for trait bounds are used simultaneously.\n\n **Why is this bad?** Duplicate bounds makes the code\n less readable than specifing them only once.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n\n Could be written as:\n\n ```rust\n fn func(arg: T) {}\n ```\n or\n\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "useless_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 53 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmutes_expressible_as_ptr_casts", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 78 - }, - "group": "clippy::complexity", - "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "crosspointer_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 96 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "transmute_ptr_to_ref", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 121 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_char", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 152 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_bytes_to_str", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 183 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_bool", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 205 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_float", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 227 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_float_to_int", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 249 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_ptr_to_ptr", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 276 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "unsound_collection_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 304 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "transmuting_null", - "id_span": { - "path": "src/transmuting_null.rs", - "line": 23 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmute calls which would receive a null pointer.\n **Why is this bad?** Transmuting a null pointer is undefined behavior.\n\n **Known problems:** Not all cases can be detected at the moment of this writing.\n For example, variables which hold a null pointer and are then fed to a `transmute`\n call, aren't detectable yet.\n\n **Example:**\n ```rust\n let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "try_err", - "id_span": { - "path": "src/try_err.rs", - "line": 43 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usages of `Err(x)?`.\n **Why is this bad?** The `?` operator is designed to allow calls that\n can fail to be easily chained. For example, `foo()?.bar()` or\n `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will\n always return), it is more clear to write `return Err(x)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n Err(\"failed\")?;\n }\n Ok(0)\n }\n ```\n Could be written:\n\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n return Err(\"failed\".into());\n }\n Ok(0)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "box_vec", - "id_span": { - "path": "src/types.rs", - "line": 66 - }, - "group": "clippy::perf", - "docs": " **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.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` it, you just add another level of indirection\n without any benefit whatsoever.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct X {\n values: Box>,\n }\n ```\n\n Better:\n\n ```rust,ignore\n struct X {\n values: Vec,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "vec_box", - "id_span": { - "path": "src/types.rs", - "line": 95 - }, - "group": "clippy::complexity", - "docs": " **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.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` its contents, you just add another level of indirection.\n\n **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),\n 1st comment).\n\n **Example:**\n ```rust\n struct X {\n values: Vec>,\n }\n ```\n\n Better:\n\n ```rust\n struct X {\n values: Vec,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "option_option", - "id_span": { - "path": "src/types.rs", - "line": 133 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for use of `Option>` in function signatures and type definitions\n\n **Why is this bad?** `Option<_>` represents an optional value. `Option>`\n represents an optional optional value which is logically the same thing as an optional\n value but has an unneeded extra level of wrapping.\n\n If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,\n consider a custom `enum` instead, with clear names for each case.\n\n **Known problems:** None.\n\n **Example**\n ```rust\n fn get_data() -> Option> {\n None\n }\n ```\n\n Better:\n\n ```rust\n pub enum Contents {\n Data(Vec), // Was Some(Some(Vec))\n NotYetFetched, // Was Some(None)\n None, // Was None\n }\n\n fn get_data() -> Contents {\n Contents::None\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "linkedlist", - "id_span": { - "path": "src/types.rs", - "line": 169 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`).\n\n **Why is this bad?** Gankro says:\n\n > The TL;DR of `LinkedList` is that it's built on a massive amount of\n pointers and indirection.\n > It wastes memory, it has terrible cache locality, and is all-around slow.\n `RingBuf`, while\n > \"only\" amortized for push/pop, should be faster in the general case for\n almost every possible\n > workload, and isn't even amortized at all if you can predict the capacity\n you need.\n >\n > `LinkedList`s are only really good if you're doing a lot of merging or\n splitting of lists.\n > This is because they can just mangle some pointers instead of actually\n copying the data. Even\n > if you're doing a lot of insertion in the middle of the list, `RingBuf`\n can still be better\n > because of how expensive it is to seek to the middle of a `LinkedList`.\n\n **Known problems:** False positives – the instances where using a\n `LinkedList` makes sense are few and far between, but they can still happen.\n\n **Example:**\n ```rust\n # use std::collections::LinkedList;\n let x: LinkedList = LinkedList::new();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "borrowed_box", - "id_span": { - "path": "src/types.rs", - "line": 193 - }, - "group": "clippy::complexity", - "docs": " **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.\n\n **Why is this bad?** Any `&Box` can also be a `&T`, which is more\n general.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn foo(bar: &Box) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(bar: &T) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "redundant_allocation", - "id_span": { - "path": "src/types.rs", - "line": 217 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of redundant allocations anywhere in the code.\n **Why is this bad?** Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Box<&T>`\n add an unnecessary level of indirection.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n fn foo(bar: Rc<&usize>) {}\n ```\n\n Better:\n\n ```rust\n fn foo(bar: &usize) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "rc_buffer", - "id_span": { - "path": "src/types.rs", - "line": 248 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`.\n **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since\n it is larger and involves an extra level of indirection, and doesn't implement `Borrow`.\n\n While mutating a buffer type would still be possible with `Rc::get_mut()`, it only\n works if there are no additional references yet, which usually defeats the purpose of\n enclosing it in a shared ownership type. Instead, additionally wrapping the inner\n type with an interior mutable container (such as `RefCell` or `Mutex`) would normally\n be used.\n\n **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for\n cases where mutation only happens before there are any additional references.\n\n **Example:**\n ```rust,ignore\n # use std::rc::Rc;\n fn foo(interned: Rc) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(interned: Rc) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "let_unit_value", - "id_span": { - "path": "src/types.rs", - "line": 768 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for binding a unit value.\n **Why is this bad?** A unit value cannot usefully be used anywhere. So\n binding one is kind of pointless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = {\n 1;\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unit_cmp", - "id_span": { - "path": "src/types.rs", - "line": 849 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons to unit. This includes all binary comparisons (like `==` and `<`) and asserts.\n\n **Why is this bad?** Unit is always equal to itself, and thus is just a\n clumsily written constant. Mostly this happens when someone accidentally\n adds semicolons at the end of the operands.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n if {\n foo();\n } == {\n bar();\n } {\n baz();\n }\n ```\n is equal to\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n {\n foo();\n bar();\n baz();\n }\n ```\n\n For asserts:\n ```rust\n # fn foo() {};\n # fn bar() {};\n assert_eq!({ foo(); }, { bar(); });\n ```\n will always succeed\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unit_arg", - "id_span": { - "path": "src/types.rs", - "line": 922 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for passing a unit value as an argument to a function without using a unit literal (`()`).\n\n **Why is this bad?** This is likely the result of an accidental semicolon.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n foo({\n let a = bar();\n baz(a);\n })\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "cast_precision_loss", - "id_span": { - "path": "src/types.rs", - "line": 1158 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts from any numerical to a float type where the receiving type cannot store all values from the original type without\n rounding errors. This possible rounding is to be expected, so this lint is\n `Allow` by default.\n\n Basically, this warns on casting any integer with 32 or more bits to `f32`\n or any 64-bit integer to `f64`.\n\n **Why is this bad?** It's not bad at all. But in some applications it can be\n helpful to know where precision loss can take place. This lint can help find\n those places in the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = u64::MAX;\n x as f64;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_sign_loss", - "id_span": { - "path": "src/types.rs", - "line": 1179 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts from a signed to an unsigned numerical type. In this case, negative values wrap around to large positive values,\n which can be quite surprising in practice. However, as the cast works as\n defined, this lint is `Allow` by default.\n\n **Why is this bad?** Possibly surprising results. You can activate this lint\n as a one-time check to see where numerical wrapping can arise.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let y: i8 = -1;\n y as u128; // will return 18446744073709551615\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_possible_truncation", - "id_span": { - "path": "src/types.rs", - "line": 1201 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by\n default.\n\n **Why is this bad?** In some problem domains, it is good practice to avoid\n truncation. This lint can be activated to help assess where additional\n checks could be beneficial.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u8(x: u64) -> u8 {\n x as u8\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_possible_wrap", - "id_span": { - "path": "src/types.rs", - "line": 1224 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts from an unsigned type to a signed type of the same size. Performing such a cast is a 'no-op' for the compiler,\n i.e., nothing is changed at the bit level, and the binary representation of\n the value is reinterpreted. This can cause wrapping if the value is too big\n for the target signed type. However, the cast works as defined, so this lint\n is `Allow` by default.\n\n **Why is this bad?** While such a cast is not bad in itself, the results can\n be surprising when this is not the intended behavior, as demonstrated by the\n example below.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n u32::MAX as i32; // will yield a value of `-1`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_lossless", - "id_span": { - "path": "src/types.rs", - "line": 1256 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts between numerical types that may be replaced by safe conversion functions.\n\n **Why is this bad?** Rust's `as` keyword will perform many kinds of\n conversions, including silently lossy conversions. Conversion functions such\n as `i32::from` will only perform lossless conversions. Using the conversion\n functions prevents conversions from turning into silent lossy conversions if\n the types of the input expressions ever change, and make it easier for\n people reading the code to know that the conversion is lossless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u64(x: u8) -> u64 {\n x as u64\n }\n ```\n\n Using `::from` would look like this:\n\n ```rust\n fn as_u64(x: u8) -> u64 {\n u64::from(x)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_cast", - "id_span": { - "path": "src/types.rs", - "line": 1281 - }, - "group": "clippy::complexity", - "docs": " **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.\n\n **Why is this bad?** It's just unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = 2i32 as i32;\n let _ = 0.5 as f32;\n ```\n\n Better:\n\n ```rust\n let _ = 2_i32;\n let _ = 0.5_f32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cast_ptr_alignment", - "id_span": { - "path": "src/types.rs", - "line": 1305 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts, using `as` or `pointer::cast`, from a less-strictly-aligned pointer to a more-strictly-aligned pointer\n\n **Why is this bad?** Dereferencing the resulting pointer may be undefined\n behavior.\n\n **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar\n on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like\n u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.\n\n **Example:**\n ```rust\n let _ = (&1u8 as *const u8) as *const u16;\n let _ = (&mut 1u8 as *mut u8) as *mut u16;\n\n (&1u8 as *const u8).cast::();\n (&mut 1u8 as *mut u8).cast::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_to_numeric_cast", - "id_span": { - "path": "src/types.rs", - "line": 1332 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for casts of function pointers to something other than usize\n **Why is this bad?**\n Casting a function pointer to anything other than usize/isize is not portable across\n architectures, because you end up losing bits if the target type is too small or end up with a\n bunch of extra bits that waste space and add more instructions to the final binary than\n strictly necessary for the problem\n\n Casting to isize also doesn't make sense since there are no signed addresses.\n\n **Example**\n\n ```rust\n // Bad\n fn fun() -> i32 { 1 }\n let a = fun as i64;\n\n // Good\n fn fun2() -> i32 { 1 }\n let a = fun2 as usize;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "fn_to_numeric_cast_with_truncation", - "id_span": { - "path": "src/types.rs", - "line": 1362 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to store address.\n\n **Why is this bad?**\n Such a cast discards some bits of the function's address. If this is intended, it would be more\n clearly expressed by casting to usize first, then casting the usize to the intended type (with\n a comment) to perform the truncation.\n\n **Example**\n\n ```rust\n // Bad\n fn fn1() -> i16 {\n 1\n };\n let _ = fn1 as i32;\n\n // Better: Cast to usize first, then comment with the reason for the truncation\n fn fn2() -> i16 {\n 1\n };\n let fn_ptr = fn2 as usize;\n let fn_ptr_truncated = fn_ptr as i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "type_complexity", - "id_span": { - "path": "src/types.rs", - "line": 1897 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for types used in structs, parameters and `let` declarations above a certain complexity threshold.\n\n **Why is this bad?** Too complex types make the code less readable. Consider\n using a `type` definition to simplify them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n struct Foo {\n inner: Rc>>>,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "char_lit_as_u8", - "id_span": { - "path": "src/types.rs", - "line": 2068 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions where a character literal is cast to `u8` and suggests using a byte literal instead.\n\n **Why is this bad?** In general, casting values to smaller types is\n error-prone and should be avoided where possible. In the particular case of\n converting a character literal to u8, it is easy to avoid by just using a\n byte literal instead. As an added bonus, `b'a'` is even slightly shorter\n than `'a' as u8`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n 'x' as u8\n ```\n\n A better version, using the byte literal:\n\n ```rust,ignore\n b'x'\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "absurd_extreme_comparisons", - "id_span": { - "path": "src/types.rs", - "line": 2133 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a\n case that is always true or always false. Only integer and boolean types are\n checked.\n\n **Why is this bad?** An expression like `min <= x` may misleadingly imply\n that it is possible for `x` to be less than the minimum. Expressions like\n `max < x` are probably mistakes.\n\n **Known problems:** For `usize` the size of the current compile target will\n be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such\n a comparison to detect target pointer width will trigger this lint. One can\n use `mem::sizeof` and compare its value or conditional compilation\n attributes\n like `#[cfg(target_pointer_width = \"64\")] ..` instead.\n\n **Example:**\n\n ```rust\n let vec: Vec = Vec::new();\n if vec.len() <= 0 {}\n if 100 > i32::MAX {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "invalid_upcast_comparisons", - "id_span": { - "path": "src/types.rs", - "line": 2294 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for comparisons where the relation is always either true or false, but where one side has been upcast so that the comparison is\n necessary. Only integer types are checked.\n\n **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`\n will mistakenly imply that it is possible for `x` to be outside the range of\n `u8`.\n\n **Known problems:**\n https://github.com/rust-lang/rust-clippy/issues/886\n\n **Example:**\n ```rust\n let x: u8 = 1;\n (x as u32) > 300;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "implicit_hasher", - "id_span": { - "path": "src/types.rs", - "line": 2515 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for public `impl` or `fn` missing generalization over different hashers and implicitly defaulting to the default hashing\n algorithm (`SipHash`).\n\n **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be\n used with them.\n\n **Known problems:** Suggestions for replacing constructors can contain\n false-positives. Also applying suggestions can require modification of other\n pieces of code, possibly including external crates.\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n could be rewritten as\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_ref_to_mut", - "id_span": { - "path": "src/types.rs", - "line": 2866 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.\n **Why is this bad?** It’s basically guaranteed to be undefined behaviour.\n `UnsafeCell` is the only way to obtain aliasable data that is considered\n mutable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn x(r: &i32) {\n unsafe {\n *(r as *const _ as *mut _) += 1;\n }\n }\n ```\n\n Instead consider using interior mutability types.\n\n ```rust\n use std::cell::UnsafeCell;\n\n fn x(r: &UnsafeCell) {\n unsafe {\n *r.get() += 1;\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ptr_as_ptr", - "id_span": { - "path": "src/types.rs", - "line": 2922 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `as` casts between raw pointers without changing its mutability,\n namely `*const T` to `*const U` and `*mut T` to `*mut U`.\n\n **Why is this bad?**\n Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because\n it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr as *const i32;\n let _ = mut_ptr as *mut i32;\n ```\n Use instead:\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr.cast::();\n let _ = mut_ptr.cast::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "undropped_manually_drops", - "id_span": { - "path": "src/undropped_manually_drops.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.\n **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.\n\n **Known problems:** Does not catch cases if the user binds `std::mem::drop`\n to a different name and calls it that way.\n\n **Example:**\n\n ```rust\n struct S;\n drop(std::mem::ManuallyDrop::new(S));\n ```\n Use instead:\n ```rust\n struct S;\n unsafe {\n std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "invisible_characters", - "id_span": { - "path": "src/unicode.rs", - "line": 20 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for invisible Unicode characters in the code.\n **Why is this bad?** Having an invisible character in the code makes for all\n sorts of April fools, but otherwise is very much frowned upon.\n\n **Known problems:** None.\n\n **Example:** You don't see it, but there may be a zero-width space or soft hyphen\n some­where in this text.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "non_ascii_literal", - "id_span": { - "path": "src/unicode.rs", - "line": 44 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for non-ASCII characters in string literals.\n **Why is this bad?** Yeah, we know, the 90's called and wanted their charset\n back. Even so, there still are editors and other programs out there that\n don't work well with Unicode. So if the code is meant to be used\n internationally, on multiple operating systems, or has other portability\n requirements, activating this lint could be useful.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = String::from(\"€\");\n ```\n Could be written as:\n ```rust\n let x = String::from(\"\\u{20ac}\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unicode_not_nfc", - "id_span": { - "path": "src/unicode.rs", - "line": 61 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for string literals that contain Unicode in a form that is not equal to its\n [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).\n\n **Why is this bad?** If such a string is compared to another, the results\n may be surprising.\n\n **Known problems** None.\n\n **Example:** You may not see it, but \"à\"\" and \"à\"\" aren't the same string. The\n former when escaped is actually `\"a\\u{300}\"` while the latter is `\"\\u{e0}\"`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unit_return_expecting_ord", - "id_span": { - "path": "src/unit_return_expecting_ord.rs", - "line": 30 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for functions that expect closures of type Fn(...) -> Ord where the implemented closure returns the unit type.\n The lint also suggests to remove the semi-colon at the end of the statement if present.\n\n **Why is this bad?** Likely, returning the unit type is unintentional, and\n could simply be caused by an extra semi-colon. Since () implements Ord\n it doesn't cause a compilation error.\n This is the same reasoning behind the unit_cmp lint.\n\n **Known problems:** If returning unit is intentional, then there is no\n way of specifying this without triggering needless_return lint\n\n **Example:**\n\n ```rust\n let mut twins = vec!((1, 1), (2, 2));\n twins.sort_by_key(|x| { x.1; });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_address_comparisons", - "id_span": { - "path": "src/unnamed_address.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons with an address of a function item.\n **Why is this bad?** Function item address is not guaranteed to be unique and could vary\n between different code generation units. Furthermore different function items could have\n the same address after being merged together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n type F = fn();\n fn a() {}\n let f: F = a;\n if f == a {\n // ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "vtable_address_comparisons", - "id_span": { - "path": "src/unnamed_address.rs", - "line": 51 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons with an address of a trait vtable.\n **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which\n are not guaranteed to be unique and could vary between different code generation units.\n Furthermore vtables for different types could have the same address after being merged\n together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n let a: Rc = ...\n let b: Rc = ...\n if Rc::ptr_eq(&a, &b) {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unnecessary_sort_by", - "id_span": { - "path": "src/unnecessary_sort_by.rs", - "line": 41 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects uses of `Vec::sort_by` passing in a closure\n which compares the two arguments, either directly or indirectly.\n\n **Why is this bad?**\n It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if\n possible) than to use `Vec::sort_by` and a more complicated\n closure.\n\n **Known problems:**\n If the suggested `Vec::sort_by_key` uses Reverse and it isn't already\n imported by a use statement, then it will need to be added manually.\n\n **Example:**\n\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by(|a, b| a.foo().cmp(&b.foo()));\n ```\n Use instead:\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by_key(|a| a.foo());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_wraps", - "id_span": { - "path": "src/unnecessary_wraps.rs", - "line": 50 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for private functions that only return `Ok` or `Some`.\n **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.\n\n **Known problems:** There can be false positives if the function signature is designed to\n fit some external requirement.\n\n **Example:**\n\n ```rust\n fn get_cool_number(a: bool, b: bool) -> Option {\n if a && b {\n return Some(50);\n }\n if a {\n Some(0)\n } else {\n Some(10)\n }\n }\n ```\n Use instead:\n ```rust\n fn get_cool_number(a: bool, b: bool) -> i32 {\n if a && b {\n return 50;\n }\n if a {\n 0\n } else {\n 10\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "unnested_or_patterns", - "id_span": { - "path": "src/unnested_or_patterns.rs", - "line": 47 - }, - "group": "clippy::pedantic", - "docs": " **What it does:**\n Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\n suggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\n Another way to think of this is that it rewrites patterns in\n *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.\n\n **Why is this bad?**\n\n In the example above, `Some` is repeated, which unncessarily complicates the pattern.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n if let Some(0) | Some(2) = Some(0) {}\n }\n ```\n Use instead:\n ```rust\n #![feature(or_patterns)]\n\n fn main() {\n if let Some(0 | 2) = Some(0) {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unsafe_removed_from_name", - "id_span": { - "path": "src/unsafe_removed_from_name.rs", - "line": 24 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for imports that remove \"unsafe\" from an item's name.\n\n **Why is this bad?** Renaming makes it less clear which traits and\n structures are unsafe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n use std::cell::{UnsafeCell as TotallySafeCell};\n\n extern crate crossbeam;\n use crossbeam::{spawn_unsafe as spawn};\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_io_amount", - "id_span": { - "path": "src/unused_io_amount.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for unused written/read amount.\n **Why is this bad?** `io::Write::write(_vectored)` and\n `io::Read::read(_vectored)` are not guaranteed to\n process the entire buffer. They return how many bytes were processed, which\n might be smaller\n than a given buffer's length. If you don't need to deal with\n partial-write/read, use\n `write_all`/`read_exact` instead.\n\n **Known problems:** Detects only common patterns.\n\n **Example:**\n ```rust,ignore\n use std::io;\n fn foo(w: &mut W) -> io::Result<()> {\n // must be `w.write_all(b\"foo\")?;`\n w.write(b\"foo\")?;\n Ok(())\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_self", - "id_span": { - "path": "src/unused_self.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks methods that contain a `self` argument but don't use it\n **Why is this bad?** It may be clearer to define the method as an associated function instead\n of an instance method if it doesn't require `self`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct A;\n impl A {\n fn method(&self) {}\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n struct A;\n impl A {\n fn method() {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_unit", - "id_span": { - "path": "src/unused_unit.rs", - "line": 27 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for unit (`()`) expressions that can be removed.\n **Why is this bad?** Such expressions add no value, but can make the code\n less readable. Depending on formatting they can make a `break` or `return`\n statement look like a function call.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn return_unit() -> () {\n ()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_unwrap", - "id_span": { - "path": "src/unwrap.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.\n **Why is this bad?** Using `if let` or `match` is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_some() {\n do_something_with(option.unwrap())\n }\n ```\n\n Could be written:\n\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if let Some(value) = option {\n do_something_with(value)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panicking_unwrap", - "id_span": { - "path": "src/unwrap.rs", - "line": 63 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.\n **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.\n\n **Known problems:** This lint only checks `if` conditions not assignments.\n So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_none() {\n do_something_with(option.unwrap())\n }\n ```\n\n This code will always panic. The if condition should probably be inverted.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unwrap_in_result", - "id_span": { - "path": "src/unwrap_in_result.rs", - "line": 47 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`\n **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.\n\n **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.\n\n **Example:**\n Before:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .expect(\"cannot divide the input by three\");\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n\n After:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .map_err(|e| format!(\"cannot divide the input by three: {}\", e))?;\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "upper_case_acronyms", - "id_span": { - "path": "src/upper_case_acronyms.rs", - "line": 35 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for fully capitalized names and optionally names containing a capitalized acronym.\n **Why is this bad?** In CamelCase, acronyms count as one word.\n See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)\n for more.\n\n By default, the lint only triggers on fully-capitalized names.\n You can use the `upper-case-acronyms-aggressive: true` config option to enable linting\n on all camel case names\n\n **Known problems:** When two acronyms are contiguous, the lint can't tell where\n the first acronym ends and the second starts, so it suggests to lowercase all of\n the letters in the second acronym.\n\n **Example:**\n\n ```rust\n struct HTTPResponse;\n ```\n Use instead:\n ```rust\n struct HttpResponse;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "use_self", - "id_span": { - "path": "src/use_self.rs", - "line": 53 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for unnecessary repetition of structure name when a replacement with `Self` is applicable.\n\n **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct\n name\n feels inconsistent.\n\n **Known problems:**\n - Unaddressed false negative in fn bodies of trait implementations\n - False positive with assotiated types in traits (#4140)\n\n **Example:**\n\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Foo {\n Foo {}\n }\n }\n ```\n could be\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Self {\n Self {}\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_conversion", - "id_span": { - "path": "src/useless_conversion.rs", - "line": 32 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls which uselessly convert to the same type.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n // format!() returns a `String`\n let s: String = format!(\"hello\").into();\n\n // Good\n let s: String = format!(\"hello\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_vec", - "id_span": { - "path": "src/vec.rs", - "line": 36 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would be possible.\n\n **Why is this bad?** This is less efficient.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(my_vec: &[u8]) {}\n\n // Bad\n foo(&vec![1, 2]);\n\n // Good\n foo(&[1, 2]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "vec_init_then_push", - "id_span": { - "path": "src/vec_init_then_push.rs", - "line": 32 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.\n **Why is this bad?** The `vec![]` macro is both more performant and easier to read than\n multiple `push` calls.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut v = Vec::new();\n v.push(0);\n ```\n Use instead:\n ```rust\n let v = vec![0];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "vec_resize_to_zero", - "id_span": { - "path": "src/vec_resize_to_zero.rs", - "line": 24 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Finds occurrences of `Vec::resize(0, an_int)`\n **Why is this bad?** This is probably an argument inversion mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n vec!(1, 2, 3, 4, 5).resize(0, 5)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "verbose_file_reads", - "id_span": { - "path": "src/verbose_file_reads.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of File::read_to_end and File::read_to_string.\n **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.\n 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)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # use std::io::Read;\n # use std::fs::File;\n let mut f = File::open(\"foo.txt\").unwrap();\n let mut bytes = Vec::new();\n f.read_to_end(&mut bytes).unwrap();\n ```\n Can be written more concisely as\n ```rust,no_run\n # use std::fs;\n let mut bytes = fs::read(\"foo.txt\").unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wildcard_dependencies", - "id_span": { - "path": "src/wildcard_dependencies.rs", - "line": 24 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.\n **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),\n it is highly unlikely that you work with any possible version of your dependency,\n and wildcard dependencies would cause unnecessary breakage in the ecosystem.\n\n **Known problems:** None.\n\n **Example:**\n\n ```toml\n [dependencies]\n regex = \"*\"\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "enum_glob_use", - "id_span": { - "path": "src/wildcard_imports.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "wildcard_imports", - "id_span": { - "path": "src/wildcard_imports.rs", - "line": 83 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "println_empty_string", - "id_span": { - "path": "src/write.rs", - "line": 33 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `println!(\"\")` to print a newline.\n\n **Why is this bad?** You should use `println!()`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n println!(\"\");\n\n // Good\n println!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "print_with_newline", - "id_span": { - "path": "src/write.rs", - "line": 57 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `print!()` with a format string that ends in a newline.\n\n **Why is this bad?** You should use `println!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"World\";\n print!(\"Hello {}!\\n\", name);\n ```\n use println!() instead\n ```rust\n # let name = \"World\";\n println!(\"Hello {}!\", name);\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "print_stdout", - "id_span": { - "path": "src/write.rs", - "line": 75 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for printing on *stdout*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stdout* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `print!` and `println!` calls.\n\n **Example:**\n ```rust\n println!(\"Hello world!\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "print_stderr", - "id_span": { - "path": "src/write.rs", - "line": 93 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for printing on *stderr*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stderr* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `eprint!` and `eprintln!` calls.\n\n **Example:**\n ```rust\n eprintln!(\"Hello world!\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "use_debug", - "id_span": { - "path": "src/write.rs", - "line": 110 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of `Debug` formatting. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** The purpose of the `Debug` trait is to facilitate\n debugging Rust code. It should not be used in user-facing output.\n\n **Example:**\n ```rust\n # let foo = \"bar\";\n println!(\"{:?}\", foo);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "print_literal", - "id_span": { - "path": "src/write.rs", - "line": 133 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "writeln_empty_string", - "id_span": { - "path": "src/write.rs", - "line": 156 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `writeln!(buf, \"\")` to print a newline.\n\n **Why is this bad?** You should use `writeln!(buf)`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"\");\n\n // Good\n writeln!(buf);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "write_with_newline", - "id_span": { - "path": "src/write.rs", - "line": 182 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `write!()` with a format string that\n ends in a newline.\n\n **Why is this bad?** You should use `writeln!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n # let name = \"World\";\n // Bad\n write!(buf, \"Hello {}!\\n\", name);\n\n // Good\n writeln!(buf, \"Hello {}!\", name);\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "write_literal", - "id_span": { - "path": "src/write.rs", - "line": 207 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "zero_divided_by_zero", - "id_span": { - "path": "src/zero_div_zero.rs", - "line": 23 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `0.0 / 0.0`.\n **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let nan = 0.0f32 / 0.0;\n\n // Good\n let nan = f32::NAN;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "zero_sized_map_values", - "id_span": { - "path": "src/zero_sized_map_values.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for maps with zero-sized value types anywhere in the code.\n **Why is this bad?** Since there is only a single value for a zero-sized type, a map\n containing zero sized values is effectively a set. Using a set in that case improves\n readability and communicates intent more clearly.\n\n **Known problems:**\n * A zero-sized type cannot be recovered later if it contains private fields.\n * This lints the signature of public items\n\n **Example:**\n\n ```rust\n # use std::collections::HashMap;\n fn unique_words(text: &str) -> HashMap<&str, ()> {\n todo!();\n }\n ```\n Use instead:\n ```rust\n # use std::collections::HashSet;\n fn unique_words(text: &str) -> HashSet<&str> {\n todo!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - } -] diff --git a/tests/dogfood.rs b/tests/dogfood.rs index b0da0bfcc35..604968ad8b0 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -41,10 +41,6 @@ fn dogfood_clippy() { command.args(&["-D", "clippy::internal"]); } - if cfg!(feature = "metadata-collector-lint") { - command.args(&["-D", "clippy::internal"]); - } - let output = command.output().unwrap(); println!("status: {}", output.status); diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs deleted file mode 100644 index b0f59e5cf8a..00000000000 --- a/tests/ui-internal/metadata-collector/track_applicability_value.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -fn producer_fn() -> Applicability { - Applicability::MachineApplicable -} - -fn modifier_fn(applicability: &mut Applicability) { - if let Applicability::MaybeIncorrect = applicability { - *applicability = Applicability::HasPlaceholders; - } -} - -fn consumer_fn(_applicability: Applicability) {} - -struct Muh; - -impl Muh { - fn producer_method() -> Applicability { - Applicability::MachineApplicable - } -} - -fn main() { - let mut applicability = producer_fn(); - applicability = Applicability::MachineApplicable; - applicability = Muh::producer_method(); - - applicability = if true { - Applicability::HasPlaceholders - } else { - Applicability::MaybeIncorrect - }; - - modifier_fn(&mut applicability); - - consumer_fn(applicability); -} -- cgit 1.4.1-3-g733a5 From c1fa1102d484b74cb7bc425994fa4246dd690ad3 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 12 Mar 2021 21:52:01 +0100 Subject: ENABLE_METADATA_COLLECTION env-value to disable default metadata collection --- clippy_lints/src/lib.rs | 6 +++++- .../src/utils/internal_lints/metadata_collector.rs | 18 ------------------ tests/dogfood.rs | 2 ++ 3 files changed, 7 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7668d4dd0b1..1a74f641554 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1005,7 +1005,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } #[cfg(feature = "metadata-collector-lint")] - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + { + if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + } + } store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 37e90bf8686..07abae31386 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -148,24 +148,6 @@ struct LintMetadata { applicability: Option, } -// impl Ord for LintMetadata { -// fn cmp(&self, other: &Self) -> Ordering { -// self.id.cmp(&other.id) -// } -// } -// -// impl PartialOrd for LintMetadata { -// fn partial_cmp(&self, other: &Self) -> Option { -// Some(self.cmp(other)) -// } -// } -// -// impl PartialEq for LintMetadata { -// fn eq(&self, other: &Self) -> bool { -// self.id == other.id -// } -// } - impl LintMetadata { fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { Self { diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 604968ad8b0..6524fd4706c 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -22,12 +22,14 @@ fn dogfood_clippy() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string()); let mut command = Command::new(&*CLIPPY_PATH); command .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") + .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection) .arg("clippy") .arg("--all-targets") .arg("--all-features") -- cgit 1.4.1-3-g733a5 From 62cafe2c02440cb1cc7cc9cbf1abe63942a19e51 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 12 Mar 2021 20:59:41 +0100 Subject: Applying PR suggestions --- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 07abae31386..b7bb557f957 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -32,7 +32,7 @@ use crate::utils::{ }; /// This is the output file of the lint collector. -const OUTPUT_FILE: &str = "../metadata_collection.json"; +const OUTPUT_FILE: &str = "../util/gh-pages/metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like @@ -62,7 +62,7 @@ const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [ ("span_suggestions", true), ]; const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ - &["clippy_utils", "diagnostics", "mutispan_sugg"], + &["clippy_utils", "diagnostics", "multispan_sugg"], &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], ]; @@ -226,11 +226,8 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { if_chain! { // item validation - if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; + if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind; if is_lint_ref_type(cx, ty); - let expr = &cx.tcx.hir().body(body_id).value; - if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; - if let ExprKind::Struct(_, _, _) = inner_exp.kind; // blacklist check let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); -- cgit 1.4.1-3-g733a5 From e0eb29c936602553f7952a4d12c7d193bc3a7513 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 31 Mar 2021 20:03:38 +0200 Subject: Applying PR suggestions (mostly typos) Co-authored-by: flip1995 Co-authored-by: phansch --- Cargo.toml | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 19 ++++++++++--------- clippy_utils/src/diagnostics.rs | 8 ++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/Cargo.toml b/Cargo.toml index bdee8e40821..23a035f9715 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } deny-warnings = [] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] -metadata-collector-lint = ["clippy_lints/metadata-collector-lint"] +metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b7bb557f957..e85637ca758 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -27,8 +27,9 @@ use std::io::prelude::*; use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; -use crate::utils::{ - last_path_segment, match_function_call, match_path, match_type, paths, span_lint, walk_ptrs_ty_depth, +use clippy_utils::{ + diagnostics::span_lint, last_path_segment, match_function_call, match_path, paths, ty::match_type, + ty::walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -107,7 +108,7 @@ pub struct MetadataCollector { /// /// We use a Heap here to have the lints added in alphabetic order in the export lints: BinaryHeap, - applicability_into: FxHashMap, + applicability_info: FxHashMap, } impl Drop for MetadataCollector { @@ -120,7 +121,7 @@ impl Drop for MetadataCollector { return; } - let mut applicability_info = std::mem::take(&mut self.applicability_into); + let mut applicability_info = std::mem::take(&mut self.applicability_info); // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); @@ -272,7 +273,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { } for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { - let app_info = self.applicability_into.entry(lint_name).or_default(); + let app_info = self.applicability_info.entry(lint_name).or_default(); app_info.applicability = applicability; app_info.is_multi_part_suggestion = is_multi_part; } @@ -354,7 +355,7 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s cx, INTERNAL_METADATA_COLLECTOR, item.ident.span, - &format!("Metadata collection error for `{}`: {}", item.ident.name, message), + &format!("metadata collection error for `{}`: {}", item.ident.name, message), ); } @@ -569,7 +570,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { } /// Add a new single expression suggestion to the counter - fn add_singe_span_suggestion(&mut self) { + fn add_single_span_suggestion(&mut self) { self.suggestion_count += 1; } @@ -604,7 +605,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some()); if found_function { // These functions are all multi part suggestions - self.add_singe_span_suggestion() + self.add_single_span_suggestion() } }, ExprKind::MethodCall(path, _path_span, arg, _arg_span) => { @@ -616,7 +617,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { if *is_multi_part { self.add_multi_part_suggestion(); } else { - self.add_singe_span_suggestion(); + self.add_single_span_suggestion(); } break; } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 7f827f1759d..a4efae54894 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -1,4 +1,12 @@ //! Clippy wrappers around rustc's diagnostic functions. +//! +//! These functions are used by the `INTERNAL_METADATA_COLLECTOR` lint to collect the corresponding +//! lint applicability. Please make sure that you update the `LINT_EMISSION_FUNCTIONS` variable in +//! `clippy_lints::utils::internal_lints::metadata_collector` when a new function is added +//! or renamed. +//! +//! Thank you! +//! ~The `INTERNAL_METADATA_COLLECTOR` lint use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::HirId; -- cgit 1.4.1-3-g733a5 From 2b38399920ad97cc9db61e50549fd4af54d4b38d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 14:44:32 -0500 Subject: Improve eval_order_dependence output --- clippy_lints/src/eval_order_dependence.rs | 2 +- tests/ui/eval_order_dependence.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 762f64fe37a..8815ca27494 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { self.cx, EVAL_ORDER_DEPENDENCE, expr.span, - "unsequenced read of a variable", + &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), Some(self.write_expr.span), "whether read occurs before this write depends on evaluation order", ); diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/eval_order_dependence.stderr index 8f4fa2228f7..cf4adbdfa24 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/eval_order_dependence.stderr @@ -1,4 +1,4 @@ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:15:9 | LL | } + x; @@ -11,7 +11,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 1; | ^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:18:5 | LL | x += { @@ -23,7 +23,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 20; | ^^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:31:12 | LL | a: x, @@ -35,7 +35,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 6; | ^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:40:9 | LL | x += { -- cgit 1.4.1-3-g733a5 From 7a7b8bd3e885ec97725579d89ba3ac12a1e6f93f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 15:02:47 -0500 Subject: Fix eval_order_dependence async false positive --- clippy_lints/src/eval_order_dependence.rs | 28 ++++++++++++++-------------- tests/ui/eval_order_dependence.rs | 6 ++++++ tests/ui/eval_order_dependence.stderr | 16 ++++++++-------- 3 files changed, 28 insertions(+), 22 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 8815ca27494..41acf55dd7d 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -70,20 +71,19 @@ declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. - match expr.kind { - ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => { - if let Some(var) = path_to_local(lhs) { - let mut visitor = ReadVisitor { - cx, - var, - write_expr: expr, - last_expr: expr, - }; - check_for_unsequenced_reads(&mut visitor); - } - }, - _ => {}, - } + let var = if_chain! { + if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind; + if let Some(var) = path_to_local(lhs); + if expr.span.desugaring_kind().is_none(); + then { var } else { return; } + }; + let mut visitor = ReadVisitor { + cx, + var, + write_expr: expr, + last_expr: expr, + }; + check_for_unsequenced_reads(&mut visitor); } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { diff --git a/tests/ui/eval_order_dependence.rs b/tests/ui/eval_order_dependence.rs index d806bc6d401..d742856bc41 100644 --- a/tests/ui/eval_order_dependence.rs +++ b/tests/ui/eval_order_dependence.rs @@ -1,3 +1,5 @@ +// edition:2018 + #[warn(clippy::eval_order_dependence)] #[allow( unused_assignments, @@ -107,3 +109,7 @@ fn main() { }, ); } + +async fn issue_6925() { + let _ = vec![async { true }.await, async { false }.await]; +} diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/eval_order_dependence.stderr index cf4adbdfa24..35eb85e95a3 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/eval_order_dependence.stderr @@ -1,48 +1,48 @@ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:15:9 + --> $DIR/eval_order_dependence.rs:17:9 | LL | } + x; | ^ | = note: `-D clippy::eval-order-dependence` implied by `-D warnings` note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:13:9 + --> $DIR/eval_order_dependence.rs:15:9 | LL | x = 1; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:18:5 + --> $DIR/eval_order_dependence.rs:20:5 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:19:9 + --> $DIR/eval_order_dependence.rs:21:9 | LL | x = 20; | ^^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:31:12 + --> $DIR/eval_order_dependence.rs:33:12 | LL | a: x, | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:33:13 + --> $DIR/eval_order_dependence.rs:35:13 | LL | x = 6; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:40:9 + --> $DIR/eval_order_dependence.rs:42:9 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:41:13 + --> $DIR/eval_order_dependence.rs:43:13 | LL | x = 20; | ^^^^^^ -- cgit 1.4.1-3-g733a5 From d66d37303c7074e8e26a0f67bba5a36cd6b12e29 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 16:05:16 -0500 Subject: Fix unnecessary_filter_map false positive --- clippy_lints/src/methods/unnecessary_filter_map.rs | 40 ++++++++++++---------- tests/ui/unnecessary_filter_map.rs | 4 +++ 2 files changed, 26 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index b61c4ffe9b3..8b66587bfd1 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, TyS}; use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; @@ -28,25 +29,28 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< found_mapping |= return_visitor.found_mapping; found_filtering |= return_visitor.found_filtering; - if !found_filtering { - span_lint( - cx, - UNNECESSARY_FILTER_MAP, - expr.span, - "this `.filter_map` can be written more simply using `.map`", - ); - return; - } - - if !found_mapping && !mutates_arg { - span_lint( - cx, - UNNECESSARY_FILTER_MAP, - expr.span, - "this `.filter_map` can be written more simply using `.filter`", - ); + let sugg = if !found_filtering { + "map" + } else if !found_mapping && !mutates_arg { + let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); + match cx.typeck_results().expr_ty(&body.value).kind() { + ty::Adt(adt, subst) + if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) + && TyS::same_type(in_ty, subst.type_at(0)) => + { + "filter" + }, + _ => return, + } + } else { return; - } + }; + span_lint( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + &format!("this `.filter_map` can be written more simply using `.{}`", sugg), + ); } } diff --git a/tests/ui/unnecessary_filter_map.rs b/tests/ui/unnecessary_filter_map.rs index af858e4abcf..c58181f518d 100644 --- a/tests/ui/unnecessary_filter_map.rs +++ b/tests/ui/unnecessary_filter_map.rs @@ -15,3 +15,7 @@ fn main() { let _ = (0..4).filter_map(i32::checked_abs); } + +fn filter_map_none_changes_item_type() -> impl Iterator { + "".chars().filter_map(|_| None) +} -- cgit 1.4.1-3-g733a5 From b8046fab238029c32285c30dca6adefb2a5dce24 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 6 May 2021 14:04:50 +0900 Subject: Add a missing lint to MSRV config doc --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index dc079663fc9..52c1dc3bdd2 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -94,7 +94,7 @@ macro_rules! define_Conf { // N.B., this macro is parsed by util/lintlib.py define_Conf! { - /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports + /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports (msrv: Option = None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), -- cgit 1.4.1-3-g733a5 From ab3094b3dbf0bf6423a9cf0509a170eee89f106e Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 6 May 2021 10:49:31 -0700 Subject: wrong_self_convention: For `to_*` variant don't lint in trait impl taking `self` when non-`Copy` type It relaxes rules for `to_*` variant, so it doesn't lint in trait definitions and implementations anymore. Although, non-`Copy` type implementing trait's `to_*` method taking `self` feels not good (consumes ownership, so should be rather named `into_`), it would be better if this case was a pedantic lint (allow-by-default) instead. --- clippy_lints/src/methods/wrong_self_convention.rs | 2 +- tests/ui/wrong_self_convention2.rs | 2 +- tests/ui/wrong_self_convention2.stderr | 11 ----------- 3 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 tests/ui/wrong_self_convention2.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 6e2bcb113c2..1773c26c251 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -22,7 +22,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), - Convention::IsTraitItem(false)], &[SelfKind::Ref]), + Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index ae3a740d405..18202ef2989 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -23,7 +23,7 @@ mod issue6983 { } struct FooNoCopy; - // trigger lint + // don't trigger impl ToU64 for FooNoCopy { fn to_u64(self) -> u64 { 2 diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr deleted file mode 100644 index 0ca1a390974..00000000000 --- a/tests/ui/wrong_self_convention2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention2.rs:28:19 - | -LL | fn to_u64(self) -> u64 { - | ^^^^ - | - = note: `-D clippy::wrong-self-convention` implied by `-D warnings` - = help: consider choosing a less ambiguous name - -error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From 5f3aae61af8ab129b743d13b14ec5519e97cc445 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Thu, 6 May 2021 23:51:01 +0200 Subject: Handle write!(buf, "\n") case better Make `write!(buf, "\n")` suggest `writeln!(buf)` by removing the trailing comma from `writeln!(buf, )`. changelog: [`write_with_newline`] suggestion on only "\n" improved --- clippy_lints/src/write.rs | 23 ++++++++++++++--------- tests/ui/write_with_newline.stderr | 4 ++-- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 7e962472c07..d0e79efa70d 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -279,8 +279,15 @@ impl EarlyLintPass for Write { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { + if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { + let (nl_span, only_nl) = newline_span(&fmt_str); + let nl_span = match (dest, only_nl) { + // Special case of `write!(buf, "\n")`: Mark everything from the end of + // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains. + (Some(dest_expr), true) => Span::new(dest_expr.span.hi(), nl_span.hi(), nl_span.ctxt()), + _ => nl_span, + }; span_lint_and_then( cx, WRITE_WITH_NEWLINE, @@ -289,10 +296,7 @@ impl EarlyLintPass for Write { |err| { err.multipart_suggestion( "use `writeln!()` instead", - vec![ - (mac.path.span, String::from("writeln")), - (newline_span(&fmt_str), String::new()), - ], + vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())], Applicability::MachineApplicable, ); }, @@ -329,12 +333,13 @@ impl EarlyLintPass for Write { /// Given a format string that ends in a newline and its span, calculates the span of the /// newline, or the format string itself if the format string consists solely of a newline. -fn newline_span(fmtstr: &StrLit) -> Span { +/// Return this and a boolean indicating whether it only consisted of a newline. +fn newline_span(fmtstr: &StrLit) -> (Span, bool) { let sp = fmtstr.span; let contents = &fmtstr.symbol.as_str(); if *contents == r"\n" { - return sp; + return (sp, true); } let newline_sp_hi = sp.hi() @@ -351,7 +356,7 @@ fn newline_span(fmtstr: &StrLit) -> Span { panic!("expected format string to contain a newline"); }; - sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) + (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false) } /// Stores a list of replacement spans for each argument, but only if all the replacements used an @@ -613,7 +618,7 @@ impl Write { |err| { err.multipart_suggestion( &format!("use `{}!` instead", suggested), - vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())], Applicability::MachineApplicable, ); }, diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index a14e86122ee..cecc2ea9406 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -51,8 +51,8 @@ LL | write!(&mut v, "/n"); | help: use `writeln!()` instead | -LL | writeln!(&mut v, ); - | ^^^^^^^ -- +LL | writeln!(&mut v); + | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:36:5 -- cgit 1.4.1-3-g733a5 From a21607d9b504281a00325065955dd825334ad6ef Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 12:08:24 -0700 Subject: needless_collect: For `BTreeMap` and `HashMap` lint only `is_empty` - `len` might produce different results than `count` - they don't have `contain` but `contains_key` method --- clippy_lints/src/loops/needless_collect.rs | 64 ++++++++++++++++++------------ tests/ui/needless_collect.fixed | 10 ++++- tests/ui/needless_collect.rs | 8 +++- tests/ui/needless_collect.stderr | 14 +++++-- 4 files changed, 63 insertions(+), 33 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 9662a0b22a3..1ed58faa925 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -10,7 +10,6 @@ use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; - use rustc_span::symbol::{sym, Ident}; use rustc_span::{MultiSpan, Span}; @@ -28,32 +27,45 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let Some(ty) = cx.typeck_results().node_type_opt(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::BTREEMAP) - || is_type_diagnostic_item(cx, ty, sym::hashmap_type); - if let Some(sugg) = match &*method.ident.name.as_str() { - "len" => Some("count()".to_string()), - "is_empty" => Some("next().is_none()".to_string()), - "contains" => { - let contains_arg = snippet(cx, args[1].span, "??"); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - Some(format!("any(|{}| x == {})", arg, pred)) - } - _ => None, - }; then { - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - method0_span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - Applicability::MachineApplicable, - ); + let is_empty_sugg = Some("next().is_none()".to_string()); + let method_name = &*method.ident.name.as_str(); + let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) { + match method_name { + "len" => Some("count()".to_string()), + "is_empty" => is_empty_sugg, + "contains" => { + let contains_arg = snippet(cx, args[1].span, "??"); + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); + Some(format!("any(|{}| x == {})", arg, pred)) + } + _ => None, + } + } + else if match_type(cx, ty, &paths::BTREEMAP) || + is_type_diagnostic_item(cx, ty, sym::hashmap_type) { + match method_name { + "is_empty" => is_empty_sugg, + _ => None, + } + } + else { + None + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + method0_span.with_hi(expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + Applicability::MachineApplicable, + ); + } } } } diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index af6c7bf15ea..d7595569681 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -13,7 +13,13 @@ fn main() { // Empty } sample.iter().cloned().any(|x| x == 1); - sample.iter().map(|x| (x, x)).count(); + // #7164 HashMap's and BTreeMap's `len` usage should not be linted + sample.iter().map(|x| (x, x)).collect::>().len(); + sample.iter().map(|x| (x, x)).collect::>().len(); + + sample.iter().map(|x| (x, x)).next().is_none(); + sample.iter().map(|x| (x, x)).next().is_none(); + // Notice the `HashSet`--this should not be linted sample.iter().collect::>().len(); // Neither should this diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 6ae14f370b1..9883c75b745 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -13,7 +13,13 @@ fn main() { // Empty } sample.iter().cloned().collect::>().contains(&1); + // #7164 HashMap's and BTreeMap's `len` usage should not be linted sample.iter().map(|x| (x, x)).collect::>().len(); + sample.iter().map(|x| (x, x)).collect::>().len(); + + sample.iter().map(|x| (x, x)).collect::>().is_empty(); + sample.iter().map(|x| (x, x)).collect::>().is_empty(); + // Notice the `HashSet`--this should not be linted sample.iter().collect::>().len(); // Neither should this diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 2a9539d5975..3acdf66a42e 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -19,10 +19,16 @@ LL | sample.iter().cloned().collect::>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:16:35 + --> $DIR/needless_collect.rs:20:35 | -LL | sample.iter().map(|x| (x, x)).collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` +LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 4 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:21:35 + | +LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 59ccc1efb368a9a3b036510f7b2d56519608da85 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 12:16:57 -0700 Subject: needless_collect: replace paths with diag items Related to: #5393 --- clippy_lints/src/loops/needless_collect.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 1ed58faa925..6a9aa08426c 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -2,8 +2,8 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::{is_trait_method, path_to_local_id, paths}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; @@ -30,7 +30,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont then { let is_empty_sugg = Some("next().is_none()".to_string()); let method_name = &*method.ident.name.as_str(); - let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || + let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) { match method_name { "len" => Some("count()".to_string()), @@ -45,7 +45,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont _ => None, } } - else if match_type(cx, ty, &paths::BTREEMAP) || + else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || is_type_diagnostic_item(cx, ty, sym::hashmap_type) { match method_name { "is_empty" => is_empty_sugg, @@ -98,7 +98,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - match_type(cx, ty, &paths::LINKED_LIST); + is_type_diagnostic_item(cx, ty, sym::LinkedList); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if let [iter_call] = &*iter_calls; then { -- cgit 1.4.1-3-g733a5 From 171789eb4526fe4ef65514f26056ca4551915cca Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 12:17:49 -0700 Subject: needless_collect: Lint `LinkedList` and `BinaryHeap` in direct usage. Those two types are supported already when used indirectly. This commit adds support for direct usage as well. --- clippy_lints/src/loops/needless_collect.rs | 4 +++- tests/ui/needless_collect.fixed | 11 ++++++++- tests/ui/needless_collect.rs | 11 ++++++++- tests/ui/needless_collect.stderr | 38 +++++++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 6a9aa08426c..89c95f3d127 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -31,7 +31,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont let is_empty_sugg = Some("next().is_none()".to_string()); let method_name = &*method.ident.name.as_str(); let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || - is_type_diagnostic_item(cx, ty, sym::vecdeque_type) { + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + is_type_diagnostic_item(cx, ty, sym::LinkedList) || + is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { match method_name { "len" => Some("count()".to_string()), "is_empty" => is_empty_sugg, diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index d7595569681..6ecbbcb6249 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -24,4 +24,13 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + + sample.iter().count(); + sample.iter().next().is_none(); + sample.iter().cloned().any(|x| x == 1); + sample.iter().any(|x| x == &1); + + // `BinaryHeap` doesn't have `contains` method + sample.iter().count(); + sample.iter().next().is_none(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 9883c75b745..8dc69bcf5b3 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -24,4 +24,13 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + + sample.iter().collect::>().len(); + sample.iter().collect::>().is_empty(); + sample.iter().cloned().collect::>().contains(&1); + sample.iter().collect::>().contains(&&1); + + // `BinaryHeap` doesn't have `contains` method + sample.iter().collect::>().len(); + sample.iter().collect::>().is_empty(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 3acdf66a42e..039091627a8 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -30,5 +30,41 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 5 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:28:19 + | +LL | sample.iter().collect::>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:29:19 + | +LL | sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:30:28 + | +LL | sample.iter().cloned().collect::>().contains(&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:31:19 + | +LL | sample.iter().collect::>().contains(&&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:34:19 + | +LL | sample.iter().collect::>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:35:19 + | +LL | sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 1b2ca3067e042b3c490d7f9b38258205bc2096f0 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Fri, 7 May 2021 20:24:07 -0700 Subject: Move `inconsistent_struct_constructor` to pedantic The whole point of named fields is that we don't have to worry about order. The names, not the position, communicate the information, so worrying about consistency for consistency's sake is pedantic to a *T*. Fixes #7192. wchargin-branch: inconsistent-struct-constructor-pedantic wchargin-source: 4fe078a21c77ceb625e58fa3b90b613fc4fa6a76 --- clippy_lints/src/inconsistent_struct_constructor.rs | 2 +- clippy_lints/src/lib.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index d138c3a8acf..3b635071f28 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -58,7 +58,7 @@ declare_clippy_lint! { /// Foo { x, y }; /// ``` pub INCONSISTENT_STRUCT_CONSTRUCTOR, - style, + pedantic, "the order of the field init shorthand is inconsistent with the order in the struct definition" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 79fe4d06d07..783bef73214 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1364,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(if_not_else::IF_NOT_ELSE), LintId::of(implicit_hasher::IMPLICIT_HASHER), LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(infinite_iter::MAYBE_INFINITE_ITER), LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), @@ -1510,7 +1511,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(identity_op::IDENTITY_OP), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -1763,7 +1763,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(len_zero::COMPARISON_TO_EMPTY), LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), -- cgit 1.4.1-3-g733a5 From d849e9586ecd203e63bfc549098f80fa11725252 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 8 May 2021 13:29:59 +0200 Subject: Metadata collection monster eating deprecated lints --- clippy_lints/src/deprecated_lints.rs | 11 +++- clippy_lints/src/lib.rs | 2 + .../src/utils/internal_lints/metadata_collector.rs | 70 ++++++++++++++++------ 3 files changed, 62 insertions(+), 21 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 4688b3d5105..fa8add20688 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -1,6 +1,13 @@ +/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This +/// enables the simple extraction of the metadata without changing the current depreciation +/// declaration. +pub struct ClippyDeprecatedLint; + macro_rules! declare_deprecated_lint { - (pub $name: ident, $_reason: expr) => { - declare_lint!(pub $name, Allow, "deprecated lint") + { $(#[$attr:meta])* pub $name: ident, $_reason: expr} => { + $(#[$attr])* + #[allow(dead_code)] + pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {}; } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 79fe4d06d07..8bdd041576f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -162,6 +162,8 @@ macro_rules! extract_msrv_attr { mod consts; #[macro_use] mod utils; +#[cfg(feature = "metadata-collector-lint")] +mod deprecated_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index e85637ca758..af734fa5572 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -28,7 +28,7 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use clippy_utils::{ - diagnostics::span_lint, last_path_segment, match_function_call, match_path, paths, ty::match_type, + diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type, ty::walk_ptrs_ty_depth, }; @@ -41,6 +41,8 @@ const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "i const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; /// Lints within this group will be excluded from the collection const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"]; +/// Collected deprecated lint will be assigned to this group in the JSON output +const DEPRECATED_LINT_GROUP_STR: &str = "DEPRECATED"; const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], @@ -66,6 +68,7 @@ const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ &["clippy_utils", "diagnostics", "multispan_sugg"], &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], ]; +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"]; /// The index of the applicability name of `paths::APPLICABILITY_VALUES` const APPLICABILITY_NAME_INDEX: usize = 2; @@ -225,23 +228,42 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// } /// ``` fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { - if_chain! { - // item validation - if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind; - if is_lint_ref_type(cx, ty); - // blacklist check - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); - if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); - // metadata extraction - if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); - if let Some(docs) = extract_attr_docs_or_lint(cx, item); - then { - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - group, - docs, - )); + if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind { + // Normal lint + if_chain! { + // item validation + if is_lint_ref_type(cx, ty); + // blacklist check + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); + // metadata extraction + if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); + if let Some(docs) = extract_attr_docs_or_lint(cx, item); + then { + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + group, + docs, + )); + } + } + + if_chain! { + if is_deprecated_lint(cx, ty); + // blacklist check + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); + // Metadata the little we can get from a deprecated lint + if let Some(docs) = extract_attr_docs_or_lint(cx, item); + then { + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + DEPRECATED_LINT_GROUP_STR.to_string(), + docs, + )); + } } } } @@ -268,7 +290,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { // - src/misc.rs:734:9 // - src/methods/mod.rs:3545:13 // - src/methods/mod.rs:3496:13 - // We are basically unable to resolve the lint name it self. + // We are basically unable to resolve the lint name itself. return; } @@ -347,6 +369,16 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { None } +fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if let hir::TyKind::Path(ref path) = ty.kind { + if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) { + return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE); + } + } + + false +} + // ================================================================== // Lint emission // ================================================================== -- cgit 1.4.1-3-g733a5 From a988a90e112a68dd48222105f57556f97ea35fbd Mon Sep 17 00:00:00 2001 From: Fridtjof Stoldt Date: Mon, 10 May 2021 12:24:24 +0200 Subject: Update clippy_lints/src/deprecated_lints.rs Co-authored-by: Philipp Krones --- clippy_lints/src/deprecated_lints.rs | 2 +- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index fa8add20688..50ffc2e3f19 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -1,5 +1,5 @@ /// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This -/// enables the simple extraction of the metadata without changing the current depreciation +/// enables the simple extraction of the metadata without changing the current deprecation /// declaration. pub struct ClippyDeprecatedLint; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index af734fa5572..fc4912ba52f 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,9 +8,6 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) -// # NITs -// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames - use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ -- cgit 1.4.1-3-g733a5 From 5ba236f3039241077302fddd7acb671e62195af6 Mon Sep 17 00:00:00 2001 From: Arya Kumar Date: Tue, 11 May 2021 19:34:14 +0000 Subject: added `needless_bitwise_bool` lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/needless_bitwise_bool.rs | 84 +++++++++++++++++++++++++++++++ tests/ui/needless_bitwise_bool.fixed | 40 +++++++++++++++ tests/ui/needless_bitwise_bool.rs | 40 +++++++++++++++ tests/ui/needless_bitwise_bool.stderr | 10 ++++ 6 files changed, 179 insertions(+) create mode 100644 clippy_lints/src/needless_bitwise_bool.rs create mode 100644 tests/ui/needless_bitwise_bool.fixed create mode 100644 tests/ui/needless_bitwise_bool.rs create mode 100644 tests/ui/needless_bitwise_bool.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a328a20dd4..3645f27427d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2549,6 +2549,7 @@ Released 2018-09-13 [`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_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 783bef73214..5b5fc452771 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -288,6 +288,7 @@ mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; +mod needless_bitwise_bool; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; @@ -833,6 +834,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: mutex_atomic::MUTEX_ATOMIC, mutex_atomic::MUTEX_INTEGER, needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, + needless_bitwise_bool::NEEDLESS_BITWISE_BOOL, needless_bool::BOOL_COMPARISON, needless_bool::NEEDLESS_BOOL, needless_borrow::NEEDLESS_BORROW, @@ -1018,6 +1020,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); @@ -1392,6 +1395,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc::USED_UNDERSCORE_BINDING), LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(mut_mut::MUT_MUT), + LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), LintId::of(needless_continue::NEEDLESS_CONTINUE), LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs new file mode 100644 index 00000000000..95febf4a2ad --- /dev/null +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -0,0 +1,84 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_opt; +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}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using + /// a lazy and. + /// + /// **Why is this bad?** + /// The bitwise operators do not support short-circuiting, so it may hinder code performance. + /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold` + /// + /// **Known problems:** + /// This lint evaluates only when the right side is determined to have no side effects. At this time, that + /// determination is quite conservative. + /// + /// **Example:** + /// + /// ```rust + /// if x & !y {} // where both x and y are booleans + /// ``` + /// Use instead: + /// ```rust + /// if x && !y {} + /// ``` + pub NEEDLESS_BITWISE_BOOL, + pedantic, + "Boolean expressions that use bitwise rather than lazy operators" +} + +declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]); + +fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + if_chain! { + if !in_macro(expr.span); + if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind()); + if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr; + if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind; + if !right.can_have_side_effects(); + then { + return true; + } + } + false +} + +fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let ExprKind::Binary(ref op, left, right) = expr.kind { + if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { + let op_snippet = match op.node { + BinOpKind::BitAnd => "&&", + _ => "||", + }; + return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet)); + } + } + None +} + +impl LateLintPass<'_> for NeedlessBitwiseBool { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if is_bitwise_operation(cx, expr) { + span_lint_and_then( + cx, + NEEDLESS_BITWISE_BOOL, + expr.span, + "use of bitwise operator instead of lazy operator between booleans", + |diag| { + if let Some(sugg) = suggession_snippet(cx, expr) { + diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); + } + }, + ); + } + } +} diff --git a/tests/ui/needless_bitwise_bool.fixed b/tests/ui/needless_bitwise_bool.fixed new file mode 100644 index 00000000000..5e1ea663a10 --- /dev/null +++ b/tests/ui/needless_bitwise_bool.fixed @@ -0,0 +1,40 @@ +// run-rustfix + +#![warn(clippy::needless_bitwise_bool)] + +fn returns_bool() -> bool { + true +} + +const fn const_returns_bool() -> bool { + false +} + +fn main() { + let (x, y) = (false, true); + if x & y { + println!("true") + } + if returns_bool() & x { + println!("true") + } + if !returns_bool() & returns_bool() { + println!("true") + } + if y && !x { + println!("true") + } + + // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. + if y & !const_returns_bool() { + println!("true") // This is a const function, in an UnOp + } + + if y & "abcD".is_empty() { + println!("true") // This is a const method call + } + + if y & (0 < 1) { + println!("true") // This is a BinOp with no side effects + } +} diff --git a/tests/ui/needless_bitwise_bool.rs b/tests/ui/needless_bitwise_bool.rs new file mode 100644 index 00000000000..f3075fba0a2 --- /dev/null +++ b/tests/ui/needless_bitwise_bool.rs @@ -0,0 +1,40 @@ +// run-rustfix + +#![warn(clippy::needless_bitwise_bool)] + +fn returns_bool() -> bool { + true +} + +const fn const_returns_bool() -> bool { + false +} + +fn main() { + let (x, y) = (false, true); + if x & y { + println!("true") + } + if returns_bool() & x { + println!("true") + } + if !returns_bool() & returns_bool() { + println!("true") + } + if y & !x { + println!("true") + } + + // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. + if y & !const_returns_bool() { + println!("true") // This is a const function, in an UnOp + } + + if y & "abcD".is_empty() { + println!("true") // This is a const method call + } + + if y & (0 < 1) { + println!("true") // This is a BinOp with no side effects + } +} diff --git a/tests/ui/needless_bitwise_bool.stderr b/tests/ui/needless_bitwise_bool.stderr new file mode 100644 index 00000000000..63c88ef63f5 --- /dev/null +++ b/tests/ui/needless_bitwise_bool.stderr @@ -0,0 +1,10 @@ +error: use of bitwise operator instead of lazy operator between booleans + --> $DIR/needless_bitwise_bool.rs:24:8 + | +LL | if y & !x { + | ^^^^^^ help: try: `y && !x` + | + = note: `-D clippy::needless-bitwise-bool` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 210ec728e5d7f428b22b78ee721ed6507cc6f925 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 11 May 2021 20:23:52 +0200 Subject: Metadata collection monster searching for configurations --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/conf.rs | 24 +++++- .../src/utils/internal_lints/metadata_collector.rs | 88 +++++++++++++++++++++- 3 files changed, 109 insertions(+), 5 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c86441cf3f..f0fae6ee1c7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1009,7 +1009,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "metadata-collector-lint")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new()); } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 52c1dc3bdd2..98b86f73a1f 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -26,13 +26,13 @@ impl TryConf { macro_rules! define_Conf { ($( - #[$doc:meta] + #[doc = $doc:literal] $(#[conf_deprecated($dep:literal)])? ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { - $(#[$doc] pub $name: $ty,)* + $(#[doc = $doc] pub $name: $ty,)* } mod defaults { @@ -89,6 +89,24 @@ macro_rules! define_Conf { Ok(TryConf { conf, errors }) } } + + #[cfg(feature = "metadata-collector-lint")] + pub mod metadata { + use crate::utils::internal_lints::metadata_collector::ClippyConfigurationBasicInfo; + + pub(crate) fn get_configuration_metadata() -> Vec { + vec![ + $( + ClippyConfigurationBasicInfo { + name: stringify!($name), + config_type: stringify!($ty), + default: stringify!($default), + doc_comment: $doc, + }, + )+ + ] + } + } }; } @@ -100,7 +118,7 @@ define_Conf! { (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: u64 = 25), - /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. + /// Lint: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] (cyclomatic_complexity_threshold: Option = None), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index fc4912ba52f..308f61beec3 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -102,13 +102,24 @@ declare_clippy_lint! { impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[allow(clippy::module_name_repetitions)] -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct MetadataCollector { /// All collected lints /// /// We use a Heap here to have the lints added in alphabetic order in the export lints: BinaryHeap, applicability_info: FxHashMap, + config: Vec, +} + +impl MetadataCollector { + pub fn new() -> Self { + Self { + lints: BinaryHeap::::default(), + applicability_info: FxHashMap::::default(), + config: collect_configs(), + } + } } impl Drop for MetadataCollector { @@ -214,6 +225,81 @@ impl Serialize for ApplicabilityInfo { } } +#[derive(Debug)] +pub(crate) struct ClippyConfigurationBasicInfo { + pub name: &'static str, + pub config_type: &'static str, + pub default: &'static str, + pub doc_comment: &'static str, +} + +#[derive(Debug, Clone, Default)] +struct ClippyConfiguration { + name: String, + lints: Vec, + doc: String, + config_type: &'static str, + default: String, +} + +// ================================================================== +// Configuration +// ================================================================== +fn collect_configs() -> Vec { + let cons = crate::utils::conf::metadata::get_configuration_metadata(); + cons.iter() + .map(move |x| { + let (lints, doc) = parse_config_field_doc(x.doc_comment) + .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); + + ClippyConfiguration { + name: to_kebab(x.name), + lints, + doc, + config_type: x.config_type, + default: x.default.to_string(), + } + }) + .collect() +} + +/// This parses the field documentation of the config struct. +/// +/// ```rust, ignore +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") +/// ``` +/// +/// Would yield: +/// ```rust, ignore +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") +/// ``` +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { + const DOC_START: &str = " Lint: "; + if_chain! { + if doc_comment.starts_with(DOC_START); + if let Some(split_pos) = doc_comment.find('.'); + then { + let mut doc_comment = doc_comment.to_string(); + let documentation = doc_comment.split_off(split_pos); + + doc_comment.make_ascii_lowercase(); + let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + + Some((lints, documentation)) + } else { + None + } + } +} + +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` +fn to_kebab(config_name: &str) -> String { + config_name.replace('_', "-") +} + +// ================================================================== +// Lint pass +// ================================================================== impl<'hir> LateLintPass<'hir> for MetadataCollector { /// Collecting lint declarations like: /// ```rust, ignore -- cgit 1.4.1-3-g733a5 From 88ae2d1155c48d1c2b0dc4d049f0411778a94035 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 11 May 2021 21:47:10 +0200 Subject: Metadata formatting the configuration section --- .../src/utils/internal_lints/metadata_collector.rs | 58 ++++++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 308f61beec3..bb1c8ae9585 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -19,6 +19,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; use std::collections::BinaryHeap; +use std::fmt; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -41,6 +42,30 @@ const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"]; /// Collected deprecated lint will be assigned to this group in the JSON output const DEPRECATED_LINT_GROUP_STR: &str = "DEPRECATED"; +/// This template will be used to format the configuration section in the lint documentation. +/// The `configurations` parameter will be replaced with one or multiple formatted +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations +macro_rules! CONFIGURATION_SECTION_TEMPLATE { + () => { + r#" +**Configuration** +This lint has the following configuration variables: + +{configurations} +"# + }; +} +/// This template will be used to format an individual `ClippyConfiguration` instance in the +/// lint documentation. +/// +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and +/// `default` +macro_rules! CONFIGURATION_VALUE_TEMPLATE { + () => { + "* {name}: {ty}: {doc} (defaults to `{default}`)\n" + }; +} + const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -120,6 +145,14 @@ impl MetadataCollector { config: collect_configs(), } } + + fn get_lint_configs(&self, lint_name: &str) -> Option { + self.config + .iter() + .filter_map(|x| x.lints.iter().any(|x| x == lint_name).then(|| format!("{}", x))) + .reduce(|acc, x| acc + &x) + .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations)) + } } impl Drop for MetadataCollector { @@ -225,6 +258,9 @@ impl Serialize for ApplicabilityInfo { } } +// ================================================================== +// Configuration +// ================================================================== #[derive(Debug)] pub(crate) struct ClippyConfigurationBasicInfo { pub name: &'static str, @@ -242,9 +278,6 @@ struct ClippyConfiguration { default: String, } -// ================================================================== -// Configuration -// ================================================================== fn collect_configs() -> Vec { let cons = crate::utils::conf::metadata::get_configuration_metadata(); cons.iter() @@ -297,6 +330,19 @@ fn to_kebab(config_name: &str) -> String { config_name.replace('_', "-") } +impl fmt::Display for ClippyConfiguration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + CONFIGURATION_VALUE_TEMPLATE!(), + name = self.name, + ty = self.config_type, + doc = self.doc, + default = self.default + ) + } +} + // ================================================================== // Lint pass // ================================================================== @@ -321,8 +367,12 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); // metadata extraction if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); - if let Some(docs) = extract_attr_docs_or_lint(cx, item); + if let Some(mut docs) = extract_attr_docs_or_lint(cx, item); then { + if let Some(configuration_section) = self.get_lint_configs(&lint_name) { + docs.push_str(&configuration_section); + } + self.lints.push(LintMetadata::new( lint_name, SerializableSpan::from_item(cx, item), -- cgit 1.4.1-3-g733a5 From b03642e51f4c1cfc51858dd792aa689b0e6597d7 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 12 May 2021 17:30:04 +0200 Subject: Metadata collection clarifying default configuration values --- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index bb1c8ae9585..4a8a46be041 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -290,12 +290,22 @@ fn collect_configs() -> Vec { lints, doc, config_type: x.config_type, - default: x.default.to_string(), + default: clarify_default(x.default), } }) .collect() } +fn clarify_default(default: &'static str) -> String { + if let Some((_start, init)) = default.split_once('[') { + if let Some((init, _end)) = init.split_once(']') { + return format!("[{}]", init); + } + } + + default.to_string() +} + /// This parses the field documentation of the config struct. /// /// ```rust, ignore -- cgit 1.4.1-3-g733a5 From b740a04dc49b32185e69a2128bcb8425d82b0c66 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 12 May 2021 18:47:32 +0200 Subject: Metadata collection collecting configuration deprecation reason --- clippy_lints/src/utils/conf.rs | 21 +++++++++++++++------ .../src/utils/internal_lints/metadata_collector.rs | 7 +++++-- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 98b86f73a1f..25163bb9f37 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -97,11 +97,20 @@ macro_rules! define_Conf { pub(crate) fn get_configuration_metadata() -> Vec { vec![ $( - ClippyConfigurationBasicInfo { - name: stringify!($name), - config_type: stringify!($ty), - default: stringify!($default), - doc_comment: $doc, + { + #[allow(unused_mut, unused_assignments)] + let mut deprecation_reason = None; + + // only set if a deprecation reason was set + $(deprecation_reason = Some(stringify!($dep));)? + + ClippyConfigurationBasicInfo { + name: stringify!($name), + config_type: stringify!($ty), + default: stringify!($default), + doc_comment: $doc, + deprecation_reason, + } }, )+ ] @@ -118,7 +127,7 @@ define_Conf! { (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: u64 = 25), - /// Lint: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. + /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] (cyclomatic_complexity_threshold: Option = None), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 4a8a46be041..bd0de8ad034 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -267,6 +267,7 @@ pub(crate) struct ClippyConfigurationBasicInfo { pub config_type: &'static str, pub default: &'static str, pub doc_comment: &'static str, + pub deprecation_reason: Option<&'static str>, } #[derive(Debug, Clone, Default)] @@ -276,6 +277,7 @@ struct ClippyConfiguration { doc: String, config_type: &'static str, default: String, + deprecation_reason: Option<&'static str>, } fn collect_configs() -> Vec { @@ -291,18 +293,19 @@ fn collect_configs() -> Vec { doc, config_type: x.config_type, default: clarify_default(x.default), + deprecation_reason: x.deprecation_reason, } }) .collect() } fn clarify_default(default: &'static str) -> String { - if let Some((_start, init)) = default.split_once('[') { + if let Some((_start, init)) = default.split_once('[') { if let Some((init, _end)) = init.split_once(']') { return format!("[{}]", init); } } - + default.to_string() } -- cgit 1.4.1-3-g733a5 From 8214bf04450db9e57975993290d3c35a66a40707 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 16 Apr 2021 16:07:20 +0900 Subject: match_single_binding: Fix invalid suggestion when match scrutinee has side effects --- clippy_lints/src/matches.rs | 37 ++++++++++++++++++++++++++--------- tests/ui/match_single_binding.fixed | 5 +---- tests/ui/match_single_binding.rs | 10 +--------- tests/ui/match_single_binding.stderr | 13 +----------- tests/ui/match_single_binding2.fixed | 16 +++++++++++++++ tests/ui/match_single_binding2.rs | 18 +++++++++++++++++ tests/ui/match_single_binding2.stderr | 36 +++++++++++++++++++++++++++++++++- 7 files changed, 100 insertions(+), 35 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 44b4eb29035..345b360e633 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1479,15 +1479,34 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A ); }, PatKind::Wild => { - span_lint_and_sugg( - cx, - MATCH_SINGLE_BINDING, - expr.span, - "this match could be replaced by its body itself", - "consider using the match body instead", - snippet_body, - Applicability::MachineApplicable, - ); + if ex.can_have_side_effects() { + let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); + let sugg = format!( + "{};\n{}{}", + snippet_with_applicability(cx, ex.span, "..", &mut applicability), + indent, + snippet_body + ); + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its scrutinee and body", + "consider using the scrutinee and body instead", + sugg, + applicability, + ) + } else { + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its body itself", + "consider using the match body instead", + snippet_body, + Applicability::MachineApplicable, + ); + } }, _ => (), } diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 526e94b10bd..30bf6402253 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -94,10 +94,7 @@ fn main() { 0 => println!("Disabled branch"), _ => println!("Enabled branch"), } - // Lint - let x = 1; - let y = 1; - println!("Single branch"); + // Ok let x = 1; let y = 1; diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 6a2ca7c5e93..d8bb80d8b96 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -106,15 +106,7 @@ fn main() { 0 => println!("Disabled branch"), _ => println!("Enabled branch"), } - // Lint - let x = 1; - let y = 1; - match match y { - 0 => 1, - _ => 2, - } { - _ => println!("Single branch"), - } + // Ok let x = 1; let y = 1; diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index cbbf5d29c02..795c8c3e24d 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -167,16 +167,5 @@ LL | unwrapped LL | }) | -error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:112:5 - | -LL | / match match y { -LL | | 0 => 1, -LL | | _ => 2, -LL | | } { -LL | | _ => println!("Single branch"), -LL | | } - | |_____^ help: consider using the match body instead: `println!("Single branch");` - -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index e73a85b73d7..a91fcc2125d 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -34,4 +34,20 @@ fn main() { }, None => println!("nothing"), } + + fn side_effects() {} + + // Lint (scrutinee has side effects) + // issue #7094 + side_effects(); + println!("Side effects"); + + // Lint (scrutinee has side effects) + // issue #7094 + let x = 1; + match x { + 0 => 1, + _ => 2, + }; + println!("Single branch"); } diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index 7362cb390e5..476386ebabe 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -34,4 +34,22 @@ fn main() { }, None => println!("nothing"), } + + fn side_effects() {} + + // Lint (scrutinee has side effects) + // issue #7094 + match side_effects() { + _ => println!("Side effects"), + } + + // Lint (scrutinee has side effects) + // issue #7094 + let x = 1; + match match x { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } } diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index bc18d191aa3..4372f55af87 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -30,5 +30,39 @@ LL | let (a, b) = get_tup(); LL | println!("a {:?} and b {:?}", a, b); | -error: aborting due to 2 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding2.rs:42:5 + | +LL | / match side_effects() { +LL | | _ => println!("Side effects"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL | side_effects(); +LL | println!("Side effects"); + | + +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding2.rs:49:5 + | +LL | / match match x { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL | match x { +LL | 0 => 1, +LL | _ => 2, +LL | }; +LL | println!("Single branch"); + | + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From daca50a515f28218142d9945ca714f7420b4fc75 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 24 Mar 2021 09:32:29 -0400 Subject: Improvements to `while_let_on_iterator` * Suggest `&mut iter` when the iterator is used after the loop. * Suggest `&mut iter` when the iterator is a field in a struct. * Don't lint when the iterator is a field in a struct, and the struct is used in the loop. * Lint when the loop is nested in another loop, but suggest `&mut iter` unless the iterator is from a local declared inside the loop. --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/loops/while_let_on_iterator.rs | 445 +++++++++++++++++------- clippy_utils/src/lib.rs | 18 + clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/visitors.rs | 36 +- tests/ui/while_let_on_iterator.fixed | 164 +++++++-- tests/ui/while_let_on_iterator.rs | 164 +++++++-- tests/ui/while_let_on_iterator.stderr | 72 +++- 8 files changed, 706 insertions(+), 197 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index fb53b55ebd6..e67ec4e06c5 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -383,7 +383,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: let mut no_stars = String::with_capacity(doc.len()); for line in doc.lines() { let mut chars = line.chars(); - while let Some(c) = chars.next() { + for c in &mut chars { if c.is_whitespace() { no_stars.push(c); } else { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 82715d9bafa..75e92f08df1 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,170 +1,353 @@ -use super::utils::{LoopNestVisitor, Nesting}; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::implements_trait; -use clippy_utils::usage::mutated_variables; -use clippy_utils::{ - get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, -}; -use if_chain::if_chain; +use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; -use rustc_span::symbol::sym; +use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind { - let pat = &arms[0].pat.kind; - if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) = - (pat, &match_expr.kind) - { - let iter_expr = &method_args[0]; - - // Don't lint when the iterator is recreated on every iteration - if_chain! { - if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; - if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]); - then { - return; - } - } + if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind { + let some_pat = match arm.pat.kind { + PatKind::TupleStruct(QPath::Resolved(None, path), sub_pats, _) => match path.res { + Res::Def(_, id) if match_def_path(cx, id, &paths::OPTION_SOME) => sub_pats.first(), + _ => return, + }, + _ => return, + }; - let lhs_constructor = last_path_segment(qpath); - if method_path.ident.name == sym::next - && is_trait_method(cx, match_expr, sym::Iterator) - && lhs_constructor.ident.name == sym::Some - && (pat_args.is_empty() - || !is_refutable(cx, pat_args[0]) - && !is_used_inside(cx, iter_expr, arms[0].body) - && !is_iterator_used_after_while_let(cx, iter_expr) - && !is_nested(cx, expr, &method_args[0])) + let iter_expr = match scrutinee_expr.kind { + ExprKind::MethodCall(name, _, [iter_expr], _) + if name.ident.name == sym::next && is_trait_method(cx, scrutinee_expr, sym::Iterator) => { - let mut applicability = Applicability::MachineApplicable; - let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability); - let loop_var = if pat_args.is_empty() { - "_".to_string() + if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr) { + iter_expr } else { - snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned() - }; - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(match_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {} in {}", loop_var, iterator), - applicability, - ); + return; + } } + _ => return, + }; + + // Needed to find an outer loop, if there are any. + let loop_expr = if let Some((_, Node::Expr(e))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1) { + e + } else { + return; + }; + + // Refutable patterns don't work with for loops. + // The iterator also can't be accessed withing the loop. + if some_pat.map_or(true, |p| is_refutable(cx, p)) || uses_iter(cx, &iter_expr, arm.body) { + return; } + + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. + // TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable + // borrow of a field isn't necessary. + let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + "&mut " + } else { + "" + }; + let mut applicability = Applicability::MachineApplicable; + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + let loop_var = some_pat.map_or_else( + || "_".into(), + |pat| snippet_with_applicability(cx, pat.span, "_", &mut applicability).into_owned(), + ); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(scrutinee_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}{}", loop_var, ref_mut, iterator), + applicability, + ); } } -fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(expr) { - Some(id) => id, - None => return false, - }; - if let Some(used_mutably) = mutated_variables(container, cx) { - if used_mutably.contains(&def_id) { - return true; +#[derive(Debug)] +struct IterExpr { + /// The span of the whole expression, not just the path and fields stored here. + span: Span, + /// The fields used, in order of child to parent. + fields: Vec, + /// The path being used. + path: Res, +} +/// Parses any expression to find out which field of which variable is used. Will return `None` if +/// the expression might have side effects. +fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { + let span = e.span; + let mut fields = Vec::new(); + loop { + match e.kind { + ExprKind::Path(ref path) => { + break Some(IterExpr { + span, + fields, + path: cx.qpath_res(path, e.hir_id), + }); + }, + ExprKind::Field(base, name) => { + fields.push(name.name); + e = base; + }, + // Dereferencing a pointer has no side effects and doesn't affect which field is being used. + ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base, + + // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have + // already been seen. + ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { + fields.clear(); + e = base; + }, + ExprKind::Unary(UnOp::Deref, base) => { + fields.clear(); + e = base; + }, + + // No effect and doesn't affect which field is being used. + ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base, + _ => break None, } } - false } -fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(iter_expr) { - Some(id) => id, - None => return false, - }; - let mut visitor = VarUsedAfterLoopVisitor { - def_id, - iter_expr_id: iter_expr.hir_id, - past_while_let: false, - var_used_after_while_let: false, - }; - if let Some(enclosing_block) = get_enclosing_block(cx, def_id) { - walk_block(&mut visitor, enclosing_block); +fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool { + loop { + match (&e.kind, fields) { + (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => { + e = base; + fields = tail_fields; + }, + (ExprKind::Path(path), []) => { + break cx.qpath_res(path, e.hir_id) == path_res; + }, + (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base, + _ => break false, + } } - visitor.var_used_after_while_let } -fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - if_chain! { - if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id); - let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id); - if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node); - then { - return is_loop_nested(cx, loop_expr, iter_expr) - } +/// Checks if the given expression is the same field as, is a child of, of the parent of the given +/// field. Used to check if the expression can be used while the given field is borrowed. +fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool { + match expr.kind { + ExprKind::Field(base, name) => { + if let Some((head_field, tail_fields)) = fields.split_first() { + if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) { + return true; + } + // Check if the expression is a parent field + let mut fields_iter = tail_fields.iter(); + while let Some(field) = fields_iter.next() { + if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) { + return true; + } + } + } + + // Check if the expression is a child field. + let mut e = base; + loop { + match e.kind { + ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true, + ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base, + ExprKind::Path(ref path) if fields.is_empty() => { + break cx.qpath_res(path, e.hir_id) == path_res; + }, + _ => break false, + } + } + }, + // If the path matches, this is either an exact match, of the expression is a parent of the field. + ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res, + ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => { + is_expr_same_child_or_parent_field(cx, base, fields, path_res) + }, + _ => false, } - false } -fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - let mut id = loop_expr.hir_id; - let iter_id = if let Some(id) = path_to_local(iter_expr) { - id - } else { - return true; +/// Strips off all field and path expressions. Used to skip them after failing to check for +/// equality. +fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { + let mut e = expr; + let e = loop { + match e.kind { + ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base, + ExprKind::Path(_) => return (None, true), + _ => break e, + } }; - loop { - let parent = cx.tcx.hir().get_parent_node(id); - if parent == id { - return false; + (Some(e), e.hir_id != expr.hir_id) +} + +/// Checks if the given expression uses the iterator. +fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { + struct V<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + uses_iter: bool, + } + impl Visitor<'tcx> for V<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None } - match cx.tcx.hir().find(parent) { - Some(Node::Expr(expr)) => { - if let ExprKind::Loop(..) = expr.kind { - return true; - }; - }, - Some(Node::Block(block)) => { - let mut block_visitor = LoopNestVisitor { - hir_id: id, - iterator: iter_id, - nesting: Nesting::Unknown, - }; - walk_block(&mut block_visitor, block); - if block_visitor.nesting == Nesting::RuledOut { - return false; + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.uses_iter { + // return + } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.uses_iter = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); } - }, - Some(Node::Stmt(_)) => (), - _ => { - return false; - }, + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + if is_res_used(self.cx, self.iter_expr.path, id) { + self.uses_iter = true; + } + } else { + walk_expr(self, e); + } } - id = parent; } -} -struct VarUsedAfterLoopVisitor { - def_id: HirId, - iter_expr_id: HirId, - past_while_let: bool, - var_used_after_while_let: bool, + let mut v = V { + cx, + iter_expr, + uses_iter: false, + }; + v.visit_expr(container); + v.uses_iter } -impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor { - type Map = Map<'tcx>; +#[allow(clippy::too_many_lines)] +fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { + struct AfterLoopVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + loop_id: HirId, + after_loop: bool, + used_iter: bool, + } + impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.used_iter { + return; + } + if self.after_loop { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.used_iter = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); + } + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + self.used_iter |= is_res_used(self.cx, self.iter_expr.path, id); + } else { + walk_expr(self, e); + } + } else if self.loop_id == e.hir_id { + self.after_loop = true; + } else { + walk_expr(self, e); + } + } + } + + struct NestedLoopVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + local_id: HirId, + loop_id: HirId, + after_loop: bool, + found_local: bool, + used_after: bool, + } + impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.past_while_let { - if path_to_local_id(expr, self.def_id) { - self.var_used_after_while_let = true; + fn visit_local(&mut self, l: &'tcx Local<'_>) { + if !self.after_loop { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + if id == self.local_id { + self.found_local = true; + } + }); + } + if let Some(e) = l.init { + self.visit_expr(e); + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.used_after { + return; + } + if self.after_loop { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.used_after = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); + } + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + self.used_after |= is_res_used(self.cx, self.iter_expr.path, id); + } else { + walk_expr(self, e); + } + } else if e.hir_id == self.loop_id { + self.after_loop = true; + } else { + walk_expr(self, e); } - } else if self.iter_expr_id == expr.hir_id { - self.past_while_let = true; } - walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None + + if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) { + // The iterator expression will be used on the next iteration unless it is declared within the outer + // loop. + let local_id = match iter_expr.path { + Res::Local(id) => id, + _ => return true, + }; + let mut v = NestedLoopVisitor { + cx, + iter_expr, + local_id, + loop_id: loop_expr.hir_id, + after_loop: false, + found_local: false, + used_after: false, + }; + v.visit_expr(e); + v.used_after || !v.found_local + } else { + let mut v = AfterLoopVisitor { + cx, + iter_expr, + loop_id: loop_expr.hir_id, + after_loop: false, + used_iter: false, + }; + v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value); + v.used_iter } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7ca9d3a860d..371fe23bedc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -855,6 +855,24 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } +/// Gets the loop enclosing the given expression, if any. +pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + let map = tcx.hir(); + for (_, node) in map.parent_iter(expr.hir_id) { + match node { + Node::Expr( + e @ Expr { + kind: ExprKind::Loop(..), + .. + }, + ) => return Some(e), + Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (), + _ => break, + } + } + None +} + /// Gets the parent node if it's an impl block. pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { let map = tcx.hir(); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 0633a19391f..0c950661757 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -289,7 +289,7 @@ fn has_enclosing_paren(sugg: impl AsRef) -> bool { let mut chars = sugg.as_ref().chars(); if let Some('(') = chars.next() { let mut depth = 1; - while let Some(c) = chars.next() { + for c in &mut chars { if c == '(' { depth += 1; } else if c == ')' { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d431bdf34ee..ffd15076026 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::path_to_local_id; use rustc_hir as hir; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt}; +use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -218,6 +218,7 @@ impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { } } +/// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>), @@ -239,3 +240,36 @@ pub fn visit_break_exprs<'tcx>( node.visit(&mut V(f)); } + +/// Checks if the given resolved path is used the body. +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { + struct V<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + res: Res, + found: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.found { + return; + } + + if let ExprKind::Path(p) = &e.kind { + if self.cx.qpath_res(p, e.hir_id) == self.res { + self.found = true; + } + } else { + walk_expr(self, e) + } + } + } + + let mut v = V { cx, res, found: false }; + v.visit_expr(&cx.tcx.hir().body(body).value); + v.found +} diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 749393db124..0562db48a88 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] fn base() { let mut iter = 1..20; @@ -38,13 +38,6 @@ fn base() { println!("next: {:?}", iter.next()); } - // or this - let mut iter = 1u32..20; - while let Some(_) = iter.next() { - break; - } - println!("Remaining iter {:?}", iter); - // or this let mut iter = 1u32..20; while let Some(_) = iter.next() { @@ -135,18 +128,6 @@ fn refutable2() { fn nested_loops() { let a = [42, 1337]; - let mut y = a.iter(); - loop { - // x is reused, so don't lint here - while let Some(_) = y.next() {} - } - - let mut y = a.iter(); - for _ in 0..2 { - while let Some(_) = y.next() { - // y is reused, don't lint - } - } loop { let mut y = a.iter(); @@ -205,13 +186,138 @@ fn issue1654() { } } -fn main() { - base(); - refutable(); - refutable2(); - nested_loops(); - issue1121(); - issue2965(); - issue3670(); - issue1654(); +fn issue6491() { + // Used in outer loop, needs &mut + let mut it = 1..40; + while let Some(n) = it.next() { + for m in &mut it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } + + // This is fine, inner loop uses a new iterator. + let mut it = 1..40; + for n in it { + let mut it = 1..40; + for m in it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Weird binding shouldn't change anything. + let (mut it, _) = (1..40, 0); + for m in it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Used after the loop, needs &mut. + let mut it = 1..40; + for m in &mut it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("next item {}", it.next().unwrap()); + + println!("n still is {}", n); + } +} + +fn issue6231() { + // Closure in the outer loop, needs &mut + let mut it = 1..40; + let mut opt = Some(0); + while let Some(n) = opt.take().or_else(|| it.next()) { + for m in &mut it { + if n % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } } + +fn issue1924() { + struct S(T); + impl> S { + fn f(&mut self) -> Option { + // Used as a field. + for i in &mut self.0 { + if !(3..=7).contains(&i) { + return Some(i); + } + } + None + } + + fn f2(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.next() { + if i == 1 { + return self.f(); + } + } + None + } + } + impl> S<(S, Option)> { + fn f3(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.0.f(); + } + } + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.f3(); + } + } + // This one is fine, a different field is borrowed + for i in &mut self.0.0.0 { + if i == 1 { + return self.0.1.take(); + } + } + None + } + } + + struct S2(T, u32); + impl> Iterator for S2 { + type Item = u32; + fn next(&mut self) -> Option { + self.0.next() + } + } + + // Don't lint, field of the iterator is accessed in the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == it.1 { + break; + } + } + + // Needs &mut, field of the iterator is accessed after the loop + let mut it = S2(1..40, 0); + for n in &mut it { + if n == 0 { + break; + } + } + println!("iterator field {}", it.1); +} + +fn main() {} diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 30e3b82a7cc..a7c217dc7fd 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] fn base() { let mut iter = 1..20; @@ -38,13 +38,6 @@ fn base() { println!("next: {:?}", iter.next()); } - // or this - let mut iter = 1u32..20; - while let Some(_) = iter.next() { - break; - } - println!("Remaining iter {:?}", iter); - // or this let mut iter = 1u32..20; while let Some(_) = iter.next() { @@ -135,18 +128,6 @@ fn refutable2() { fn nested_loops() { let a = [42, 1337]; - let mut y = a.iter(); - loop { - // x is reused, so don't lint here - while let Some(_) = y.next() {} - } - - let mut y = a.iter(); - for _ in 0..2 { - while let Some(_) = y.next() { - // y is reused, don't lint - } - } loop { let mut y = a.iter(); @@ -205,13 +186,138 @@ fn issue1654() { } } -fn main() { - base(); - refutable(); - refutable2(); - nested_loops(); - issue1121(); - issue2965(); - issue3670(); - issue1654(); +fn issue6491() { + // Used in outer loop, needs &mut + let mut it = 1..40; + while let Some(n) = it.next() { + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } + + // This is fine, inner loop uses a new iterator. + let mut it = 1..40; + while let Some(n) = it.next() { + let mut it = 1..40; + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Weird binding shouldn't change anything. + let (mut it, _) = (1..40, 0); + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Used after the loop, needs &mut. + let mut it = 1..40; + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("next item {}", it.next().unwrap()); + + println!("n still is {}", n); + } } + +fn issue6231() { + // Closure in the outer loop, needs &mut + let mut it = 1..40; + let mut opt = Some(0); + while let Some(n) = opt.take().or_else(|| it.next()) { + while let Some(m) = it.next() { + if n % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } +} + +fn issue1924() { + struct S(T); + impl> S { + fn f(&mut self) -> Option { + // Used as a field. + while let Some(i) = self.0.next() { + if i < 3 || i > 7 { + return Some(i); + } + } + None + } + + fn f2(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.next() { + if i == 1 { + return self.f(); + } + } + None + } + } + impl> S<(S, Option)> { + fn f3(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.0.f(); + } + } + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.f3(); + } + } + // This one is fine, a different field is borrowed + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.1.take(); + } + } + None + } + } + + struct S2(T, u32); + impl> Iterator for S2 { + type Item = u32; + fn next(&mut self) -> Option { + self.0.next() + } + } + + // Don't lint, field of the iterator is accessed in the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == it.1 { + break; + } + } + + // Needs &mut, field of the iterator is accessed after the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == 0 { + break; + } + } + println!("iterator field {}", it.1); +} + +fn main() {} diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 6554977c798..0feca257410 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -19,28 +19,90 @@ 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:101:9 + --> $DIR/while_let_on_iterator.rs:94: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:108:9 + --> $DIR/while_let_on_iterator.rs:101: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:121:9 + --> $DIR/while_let_on_iterator.rs:114: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:153:9 + --> $DIR/while_let_on_iterator.rs:134:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` -error: aborting due to 7 previous errors +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:193:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:204:5 + | +LL | while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:206:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:215:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:224:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:241:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:256:13 + | +LL | while let Some(i) = self.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/while_let_on_iterator.rs:257:20 + | +LL | if i < 3 || i > 7 { + | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)` + | + = note: `-D clippy::manual-range-contains` implied by `-D warnings` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:288:13 + | +LL | while let Some(i) = self.0.0.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:315:5 + | +LL | while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 4713e25ab07badc863fff05fcd5bcf9852cf375e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 13 Apr 2021 09:30:01 -0400 Subject: Cleanup of `while_let_on_iterator` --- clippy_lints/src/loops/while_let_on_iterator.rs | 116 +++++++++++------------- clippy_utils/src/visitors.rs | 2 +- tests/ui/while_let_on_iterator.fixed | 9 +- tests/ui/while_let_on_iterator.rs | 9 +- tests/ui/while_let_on_iterator.stderr | 10 +- 5 files changed, 80 insertions(+), 66 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 75e92f08df1..63560047578 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,6 +2,7 @@ use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}; @@ -9,66 +10,57 @@ use rustc_lint::LateContext; use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind { - let some_pat = match arm.pat.kind { - PatKind::TupleStruct(QPath::Resolved(None, path), sub_pats, _) => match path.res { - Res::Def(_, id) if match_def_path(cx, id, &paths::OPTION_SOME) => sub_pats.first(), - _ => return, - }, - _ => return, - }; - - let iter_expr = match scrutinee_expr.kind { - ExprKind::MethodCall(name, _, [iter_expr], _) - if name.ident.name == sym::next && is_trait_method(cx, scrutinee_expr, sym::Iterator) => - { - if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr) { - iter_expr - } else { - return; - } - } - _ => return, - }; - - // Needed to find an outer loop, if there are any. - let loop_expr = if let Some((_, Node::Expr(e))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1) { - e + let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { + if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind; + // check for `Some(..)` pattern + if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind; + if let Res::Def(_, pat_did) = pat_path.res; + if match_def_path(cx, pat_did, &paths::OPTION_SOME); + // check for call to `Iterator::next` + if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind; + if method_name.ident.name == sym::next; + if is_trait_method(cx, scrutinee_expr, sym::Iterator); + if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr); + // get the loop containing the match expression + if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); + if !uses_iter(cx, &iter_expr, arm.body); + then { + (scrutinee_expr, iter_expr, some_pat, loop_expr) } else { return; - }; + } + }; - // Refutable patterns don't work with for loops. - // The iterator also can't be accessed withing the loop. - if some_pat.map_or(true, |p| is_refutable(cx, p)) || uses_iter(cx, &iter_expr, arm.body) { + let mut applicability = Applicability::MachineApplicable; + let loop_var = if let Some(some_pat) = some_pat.first() { + if is_refutable(cx, some_pat) { + // Refutable patterns don't work with for loops. return; } + snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) + } else { + "_".into() + }; - // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be - // borrowed mutably. - // TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable - // borrow of a field isn't necessary. - let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { - "&mut " - } else { - "" - }; - let mut applicability = Applicability::MachineApplicable; - let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); - let loop_var = some_pat.map_or_else( - || "_".into(), - |pat| snippet_with_applicability(cx, pat.span, "_", &mut applicability).into_owned(), - ); - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(scrutinee_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {} in {}{}", loop_var, ref_mut, iterator), - applicability, - ); - } + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used + // afterwards a mutable borrow of a field isn't necessary. + let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + "&mut " + } else { + "" + }; + + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(scrutinee_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}{}", loop_var, ref_mut, iterator), + applicability, + ); } #[derive(Debug)] @@ -135,8 +127,10 @@ fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symb } } -/// Checks if the given expression is the same field as, is a child of, of the parent of the given -/// field. Used to check if the expression can be used while the given field is borrowed. +/// Checks if the given expression is the same field as, is a child of, or is the parent of the +/// given field. Used to check if the expression can be used while the given field is borrowed +/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but +/// `x.z`, and `y` will return false. fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool { match expr.kind { ExprKind::Field(base, name) => { @@ -166,7 +160,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie } } }, - // If the path matches, this is either an exact match, of the expression is a parent of the field. + // If the path matches, this is either an exact match, or the expression is a parent of the field. ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res, ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => { is_expr_same_child_or_parent_field(cx, base, fields, path_res) @@ -175,8 +169,8 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie } } -/// Strips off all field and path expressions. Used to skip them after failing to check for -/// equality. +/// Strips off all field and path expressions. This will return true if a field or path has been +/// skipped. Used to skip them after failing to check for equality. fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { let mut e = expr; let e = loop { @@ -257,7 +251,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: self.visit_expr(e); } } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { - self.used_iter |= is_res_used(self.cx, self.iter_expr.path, id); + self.used_iter = is_res_used(self.cx, self.iter_expr.path, id); } else { walk_expr(self, e); } @@ -309,7 +303,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: self.visit_expr(e); } } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { - self.used_after |= is_res_used(self.cx, self.iter_expr.path, id); + self.used_after = is_res_used(self.cx, self.iter_expr.path, id); } else { walk_expr(self, e); } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ffd15076026..ecdc666b5f6 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -241,7 +241,7 @@ pub fn visit_break_exprs<'tcx>( node.visit(&mut V(f)); } -/// Checks if the given resolved path is used the body. +/// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 0562db48a88..389297eff0c 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -288,6 +288,8 @@ fn issue1924() { for i in &mut self.0.0.0 { if i == 1 { return self.0.1.take(); + } else { + self.0.1 = Some(i); } } None @@ -320,4 +322,9 @@ fn issue1924() { println!("iterator field {}", it.1); } -fn main() {} +fn main() { + let mut it = 0..20; + for _ in it { + println!("test"); + } +} diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index a7c217dc7fd..df932724a0d 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -288,6 +288,8 @@ fn issue1924() { while let Some(i) = self.0.0.0.next() { if i == 1 { return self.0.1.take(); + } else { + self.0.1 = Some(i); } } None @@ -320,4 +322,9 @@ fn issue1924() { println!("iterator field {}", it.1); } -fn main() {} +fn main() { + let mut it = 0..20; + while let Some(..) = it.next() { + println!("test"); + } +} diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 0feca257410..e8741f74981 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -99,10 +99,16 @@ LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:315:5 + --> $DIR/while_let_on_iterator.rs:317:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` -error: aborting due to 17 previous errors +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:327:5 + | +LL | while let Some(..) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From cd241b33cb8379311286f53c4afc6e671ddfded1 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 10:24:29 +0200 Subject: Trigger `wrong_self_convention` only if it has implicit self --- clippy_lints/src/methods/mod.rs | 24 ++++++++++++++---------- tests/ui/wrong_self_convention2.rs | 23 +++++++++++++++++++++++ tests/ui/wrong_self_convention2.stderr | 19 +++++++++++++++++++ 3 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 tests/ui/wrong_self_convention2.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0b1b6304def..e0d29682146 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1838,16 +1838,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - wrong_self_convention::check( - cx, - &name, - item.vis.node.is_pub(), - self_ty, - first_arg_ty, - first_arg.pat.span, - implements_trait, - false - ); + if sig.decl.implicit_self.has_implicit_self() { + wrong_self_convention::check( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span, + implements_trait, + false + ); + } } } @@ -1903,7 +1905,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; + if sig.decl.implicit_self.has_implicit_self(); if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 18202ef2989..b2c6503de49 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -42,3 +42,26 @@ mod issue7032 { } } } + +mod issue7179 { + pub struct S(i32); + + impl S { + // don't trigger (`s` is not `self`) + pub fn from_be(s: Self) -> Self { + S(i32::from_be(s.0)) + } + + // lint + pub fn from_be_self(self) -> Self { + S(i32::from_be(self.0)) + } + } + + trait T { + // don't trigger (`s` is not `self`) + fn from_be(s: Self) -> Self; + // lint + fn from_be_self(self) -> Self; + } +} diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr new file mode 100644 index 00000000000..d2d74ce099e --- /dev/null +++ b/tests/ui/wrong_self_convention2.stderr @@ -0,0 +1,19 @@ +error: methods called `from_*` usually take no `self` + --> $DIR/wrong_self_convention2.rs:56:29 + | +LL | pub fn from_be_self(self) -> Self { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: methods called `from_*` usually take no `self` + --> $DIR/wrong_self_convention2.rs:65:25 + | +LL | fn from_be_self(self) -> Self; + | ^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 368e621265ab7f3066d1e3e56b0633bca3f7e831 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 11:50:25 +0200 Subject: Stop linting `else if let` pattern in `option_if_let_else` lint --- clippy_lints/src/option_if_let_else.rs | 4 +++- tests/ui/option_if_let_else.fixed | 6 +++++- tests/ui/option_if_let_else.stderr | 13 +------------ 3 files changed, 9 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index e527adbb892..2d98b275de7 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor}; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_else_clause, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; @@ -161,6 +161,7 @@ fn detect_option_if_let_else<'tcx>( if_chain! { if !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; + if !is_else_clause(cx.tcx, expr); if arms.len() == 2; 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; @@ -168,6 +169,7 @@ fn detect_option_if_let_else<'tcx>( 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); + then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 47e7460fa7a..769ccc14bc1 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -10,7 +10,11 @@ fn bad1(string: Option<&str>) -> (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))) } + } else if let Some(x) = string { + Some((true, x)) + } else { + Some((false, "")) + } } fn unop_bad(string: &Option<&str>, mut num: Option) { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 7aab068800a..4ebb068d22e 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -10,17 +10,6 @@ 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:17: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:25:13 | @@ -159,5 +148,5 @@ error: use Option::map_or instead of an if let/else LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From b24929044877b0a3710f40b3cf27b61d79b5d5df Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 13 May 2021 17:09:59 +0200 Subject: needless_collect: use snippet_with_applicability + small code refactor - using early returns. --- clippy_lints/src/loops/needless_collect.rs | 37 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 89c95f3d127..d3406780888 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,6 +1,6 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_trait_method, path_to_local_id}; @@ -28,46 +28,45 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id); then { - let is_empty_sugg = Some("next().is_none()".to_string()); + let mut applicability = Applicability::MachineApplicable; + let is_empty_sugg = "next().is_none()".to_string(); let method_name = &*method.ident.name.as_str(); let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::LinkedList) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { match method_name { - "len" => Some("count()".to_string()), + "len" => "count()".to_string(), "is_empty" => is_empty_sugg, "contains" => { - let contains_arg = snippet(cx, args[1].span, "??"); + let contains_arg = snippet_with_applicability(cx, args[1].span, "??", &mut applicability); let (arg, pred) = contains_arg .strip_prefix('&') .map_or(("&x", &*contains_arg), |s| ("x", s)); - Some(format!("any(|{}| x == {})", arg, pred)) + format!("any(|{}| x == {})", arg, pred) } - _ => None, + _ => return, } } else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || is_type_diagnostic_item(cx, ty, sym::hashmap_type) { match method_name { "is_empty" => is_empty_sugg, - _ => None, + _ => return, } } else { - None + return; }; - if let Some(sugg) = sugg { - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - method0_span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - Applicability::MachineApplicable, - ); - } + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + method0_span.with_hi(expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + applicability, + ); } } } -- cgit 1.4.1-3-g733a5 From c6e0e843d279c0c4703e2b3326826423b791dee2 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 16:45:18 -0700 Subject: Implement unnecessary-async and UI test --- clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/unnecessary_async.rs | 92 +++++++++++++++++++++++++++++++++++ tests/ui/unnecessary_async.rs | 15 ++++++ tests/ui/unnecessary_async.stderr | 13 +++++ 4 files changed, 124 insertions(+) create mode 100644 clippy_lints/src/unnecessary_async.rs create mode 100644 tests/ui/unnecessary_async.rs create mode 100644 tests/ui/unnecessary_async.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1a74f641554..e8b8b2e9117 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,6 +360,7 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; +mod unnecessary_async; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -954,6 +955,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_async::UNNECESSARY_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, @@ -1271,6 +1273,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_late_pass(|| box unnecessary_async::UnnecessaryAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1412,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_async::UNNECESSARY_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_self::UNUSED_SELF), diff --git a/clippy_lints/src/unnecessary_async.rs b/clippy_lints/src/unnecessary_async.rs new file mode 100644 index 00000000000..15a90ba42d2 --- /dev/null +++ b/clippy_lints/src/unnecessary_async.rs @@ -0,0 +1,92 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +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 functions that are declared `async` but have no `.await`s inside of them. + /// + /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. + /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which + /// causes runtime overhead and hassle for the caller. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// async fn get_random_number() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = get_random_number(); + /// + /// // Good + /// fn get_random_number_improved() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = async { get_random_number_improved() }; + /// ``` + pub UNNECESSARY_ASYNC, + pedantic, + "finds async functions with no await statements" +} + +declare_lint_pass!(UnnecessaryAsync => [UNNECESSARY_ASYNC]); + +struct AsyncFnVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found_await: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { + self.found_await = true; + } + walk_expr(self, ex); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Trait(..) = item.kind { + return; + } + } + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if matches!(asyncness, IsAsync::Async) { + let mut visitor = AsyncFnVisitor { cx, found_await: false }; + walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); + if !visitor.found_await { + span_lint_and_help( + cx, + UNNECESSARY_ASYNC, + span, + "unnecessary `async` for function with no await statements", + None, + "consider removing the `async` from this function", + ); + } + } + } + } +} diff --git a/tests/ui/unnecessary_async.rs b/tests/ui/unnecessary_async.rs new file mode 100644 index 00000000000..7d63083b13e --- /dev/null +++ b/tests/ui/unnecessary_async.rs @@ -0,0 +1,15 @@ +// edition:2018 +#![warn(clippy::unnecessary_async)] + +async fn foo() -> i32 { + 4 +} + +async fn bar() -> i32 { + foo().await +} + +fn main() { + foo(); + bar(); +} diff --git a/tests/ui/unnecessary_async.stderr b/tests/ui/unnecessary_async.stderr new file mode 100644 index 00000000000..5542580e45d --- /dev/null +++ b/tests/ui/unnecessary_async.stderr @@ -0,0 +1,13 @@ +error: unnecessary `async` for function with no await statements + --> $DIR/unnecessary_async.rs:4:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-async` implied by `-D warnings` + = help: consider removing the `async` from this function + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 1d8f3b51e662bb66e2b696d0c13057d6ba89f979 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 17:07:30 -0700 Subject: Unnecessary -> Unused --- clippy_lints/src/lib.rs | 8 +-- clippy_lints/src/unnecessary_async.rs | 92 ----------------------------------- clippy_lints/src/unused_async.rs | 92 +++++++++++++++++++++++++++++++++++ tests/ui/unnecessary_async.rs | 15 ------ tests/ui/unnecessary_async.stderr | 13 ----- tests/ui/unused_async.rs | 15 ++++++ tests/ui/unused_async.stderr | 13 +++++ 7 files changed, 124 insertions(+), 124 deletions(-) delete mode 100644 clippy_lints/src/unnecessary_async.rs create mode 100644 clippy_lints/src/unused_async.rs delete mode 100644 tests/ui/unnecessary_async.rs delete mode 100644 tests/ui/unnecessary_async.stderr create mode 100644 tests/ui/unused_async.rs create mode 100644 tests/ui/unused_async.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e8b8b2e9117..52e64d6a48f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,7 +360,7 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; -mod unnecessary_async; +mod unused_async; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -955,7 +955,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_async::UNNECESSARY_ASYNC, + unused_async::UNUSED_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, @@ -1273,7 +1273,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); - store.register_late_pass(|| box unnecessary_async::UnnecessaryAsync); + store.register_late_pass(|| box unused_async::UnusedAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1415,7 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_async::UNNECESSARY_ASYNC), + LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_self::UNUSED_SELF), diff --git a/clippy_lints/src/unnecessary_async.rs b/clippy_lints/src/unnecessary_async.rs deleted file mode 100644 index 15a90ba42d2..00000000000 --- a/clippy_lints/src/unnecessary_async.rs +++ /dev/null @@ -1,92 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; -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 functions that are declared `async` but have no `.await`s inside of them. - /// - /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. - /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which - /// causes runtime overhead and hassle for the caller. - /// - /// **Known problems:** None - /// - /// **Example:** - /// - /// ```rust - /// // Bad - /// async fn get_random_number() -> i64 { - /// 4 // Chosen by fair dice roll. Guaranteed to be random. - /// } - /// let number_future = get_random_number(); - /// - /// // Good - /// fn get_random_number_improved() -> i64 { - /// 4 // Chosen by fair dice roll. Guaranteed to be random. - /// } - /// let number_future = async { get_random_number_improved() }; - /// ``` - pub UNNECESSARY_ASYNC, - pedantic, - "finds async functions with no await statements" -} - -declare_lint_pass!(UnnecessaryAsync => [UNNECESSARY_ASYNC]); - -struct AsyncFnVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found_await: bool, -} - -impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { - self.found_await = true; - } - walk_expr(self, ex); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) - } -} - -impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Trait(..) = item.kind { - return; - } - } - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - fn_kind: FnKind<'tcx>, - fn_decl: &'tcx FnDecl<'tcx>, - body: &Body<'tcx>, - span: Span, - hir_id: HirId, - ) { - if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { - if matches!(asyncness, IsAsync::Async) { - let mut visitor = AsyncFnVisitor { cx, found_await: false }; - walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); - if !visitor.found_await { - span_lint_and_help( - cx, - UNNECESSARY_ASYNC, - span, - "unnecessary `async` for function with no await statements", - None, - "consider removing the `async` from this function", - ); - } - } - } - } -} diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs new file mode 100644 index 00000000000..18ee07d3a95 --- /dev/null +++ b/clippy_lints/src/unused_async.rs @@ -0,0 +1,92 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +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 functions that are declared `async` but have no `.await`s inside of them. + /// + /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. + /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which + /// causes runtime overhead and hassle for the caller. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// async fn get_random_number() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = get_random_number(); + /// + /// // Good + /// fn get_random_number_improved() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = async { get_random_number_improved() }; + /// ``` + pub UNUSED_ASYNC, + pedantic, + "finds async functions with no await statements" +} + +declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); + +struct AsyncFnVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found_await: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { + self.found_await = true; + } + walk_expr(self, ex); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnusedAsync { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Trait(..) = item.kind { + return; + } + } + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if matches!(asyncness, IsAsync::Async) { + let mut visitor = AsyncFnVisitor { cx, found_await: false }; + walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); + if !visitor.found_await { + span_lint_and_help( + cx, + UNUSED_ASYNC, + span, + "unused `async` for function with no await statements", + None, + "consider removing the `async` from this function", + ); + } + } + } + } +} diff --git a/tests/ui/unnecessary_async.rs b/tests/ui/unnecessary_async.rs deleted file mode 100644 index 7d63083b13e..00000000000 --- a/tests/ui/unnecessary_async.rs +++ /dev/null @@ -1,15 +0,0 @@ -// edition:2018 -#![warn(clippy::unnecessary_async)] - -async fn foo() -> i32 { - 4 -} - -async fn bar() -> i32 { - foo().await -} - -fn main() { - foo(); - bar(); -} diff --git a/tests/ui/unnecessary_async.stderr b/tests/ui/unnecessary_async.stderr deleted file mode 100644 index 5542580e45d..00000000000 --- a/tests/ui/unnecessary_async.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: unnecessary `async` for function with no await statements - --> $DIR/unnecessary_async.rs:4:1 - | -LL | / async fn foo() -> i32 { -LL | | 4 -LL | | } - | |_^ - | - = note: `-D clippy::unnecessary-async` implied by `-D warnings` - = help: consider removing the `async` from this function - -error: aborting due to previous error - diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs new file mode 100644 index 00000000000..4f4203f5fdb --- /dev/null +++ b/tests/ui/unused_async.rs @@ -0,0 +1,15 @@ +// edition:2018 +#![warn(clippy::unused_async)] + +async fn foo() -> i32 { + 4 +} + +async fn bar() -> i32 { + foo().await +} + +fn main() { + foo(); + bar(); +} diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr new file mode 100644 index 00000000000..8b834d205b1 --- /dev/null +++ b/tests/ui/unused_async.stderr @@ -0,0 +1,13 @@ +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:4:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = note: `-D clippy::unused-async` implied by `-D warnings` + = help: consider removing the `async` from this function + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 75ef9dc708e669bccbc18c3be4873c613058ef30 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 17:12:25 -0700 Subject: update_lints --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 204d56e2a98..c922cc7e763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2538,6 +2538,7 @@ Released 2018-09-13 [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute [`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 52e64d6a48f..2f5642f24f2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,9 +360,9 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; -mod unused_async; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unused_async; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -955,12 +955,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unused_async::UNUSED_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, unnested_or_patterns::UNNESTED_OR_PATTERNS, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, @@ -1415,9 +1415,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), + LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unused_self::UNUSED_SELF), LintId::of(wildcard_imports::ENUM_GLOB_USE), LintId::of(wildcard_imports::WILDCARD_IMPORTS), -- cgit 1.4.1-3-g733a5 From f810c11d3c44af357ad7af6cf9fc9ab582591c01 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 15 May 2021 19:00:49 +0200 Subject: Applying PR suggestions and cleaning up --- clippy_lints/src/utils/conf.rs | 27 +++++----- .../src/utils/internal_lints/metadata_collector.rs | 61 +++++++++------------- 2 files changed, 39 insertions(+), 49 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 25163bb9f37..fd2dddb3b96 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -92,25 +92,26 @@ macro_rules! define_Conf { #[cfg(feature = "metadata-collector-lint")] pub mod metadata { - use crate::utils::internal_lints::metadata_collector::ClippyConfigurationBasicInfo; + use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; - pub(crate) fn get_configuration_metadata() -> Vec { + macro_rules! wrap_option { + () => (None); + ($x:literal) => (Some($x)); + } + + pub(crate) fn get_configuration_metadata() -> Vec { vec![ $( { - #[allow(unused_mut, unused_assignments)] - let mut deprecation_reason = None; + let deprecation_reason = wrap_option!($($dep)?); - // only set if a deprecation reason was set - $(deprecation_reason = Some(stringify!($dep));)? - - ClippyConfigurationBasicInfo { - name: stringify!($name), - config_type: stringify!($ty), - default: stringify!($default), - doc_comment: $doc, + ClippyConfiguration::new( + stringify!($name), + stringify!($ty), + format!("{:?}", super::defaults::$name()), + $doc, deprecation_reason, - } + ) }, )+ ] diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index bd0de8ad034..b7c86ae6e18 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -149,7 +149,8 @@ impl MetadataCollector { fn get_lint_configs(&self, lint_name: &str) -> Option { self.config .iter() - .filter_map(|x| x.lints.iter().any(|x| x == lint_name).then(|| format!("{}", x))) + .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) + .map(ToString::to_string) .reduce(|acc, x| acc + &x) .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations)) } @@ -261,52 +262,40 @@ impl Serialize for ApplicabilityInfo { // ================================================================== // Configuration // ================================================================== -#[derive(Debug)] -pub(crate) struct ClippyConfigurationBasicInfo { - pub name: &'static str, - pub config_type: &'static str, - pub default: &'static str, - pub doc_comment: &'static str, - pub deprecation_reason: Option<&'static str>, -} - #[derive(Debug, Clone, Default)] -struct ClippyConfiguration { +pub struct ClippyConfiguration { name: String, - lints: Vec, - doc: String, config_type: &'static str, default: String, + lints: Vec, + doc: String, deprecation_reason: Option<&'static str>, } -fn collect_configs() -> Vec { - let cons = crate::utils::conf::metadata::get_configuration_metadata(); - cons.iter() - .map(move |x| { - let (lints, doc) = parse_config_field_doc(x.doc_comment) - .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); - - ClippyConfiguration { - name: to_kebab(x.name), - lints, - doc, - config_type: x.config_type, - default: clarify_default(x.default), - deprecation_reason: x.deprecation_reason, - } - }) - .collect() -} +impl ClippyConfiguration { + pub fn new( + name: &'static str, + config_type: &'static str, + default: String, + doc_comment: &'static str, + deprecation_reason: Option<&'static str>, + ) -> Self { + let (lints, doc) = parse_config_field_doc(doc_comment) + .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); -fn clarify_default(default: &'static str) -> String { - if let Some((_start, init)) = default.split_once('[') { - if let Some((init, _end)) = init.split_once(']') { - return format!("[{}]", init); + Self { + name: to_kebab(name), + lints, + doc, + config_type, + default, + deprecation_reason, } } +} - default.to_string() +fn collect_configs() -> Vec { + crate::utils::conf::metadata::get_configuration_metadata() } /// This parses the field documentation of the config struct. -- cgit 1.4.1-3-g733a5 From ebf065e5172227a99743f4032c5bfff80d5630e6 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 17 May 2021 22:18:50 +0900 Subject: Fix a `manual_unwrap_or` FP with deref coercion --- clippy_lints/src/manual_unwrap_or.rs | 3 +++ tests/ui/manual_unwrap_or.fixed | 8 ++++++++ tests/ui/manual_unwrap_or.rs | 8 ++++++++ 3 files changed, 19 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 520162559e5..cf183d4c16f 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -11,6 +11,7 @@ use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -87,6 +88,8 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if !contains_return_break_continue_macro(or_arm.body); + if !cx.typeck_results().expr_adjustments(unwrap_arm.body).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); then { Some(or_arm) } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index e7a29596b73..1efecb0ba12 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -163,4 +163,12 @@ mod issue6965 { } } +use std::rc::Rc; +fn format_name(name: Option<&Rc>) -> &str { + match name { + None => "", + Some(name) => name, + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 66006b6c616..95d585ad18a 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -205,4 +205,12 @@ mod issue6965 { } } +use std::rc::Rc; +fn format_name(name: Option<&Rc>) -> &str { + match name { + None => "", + Some(name) => name, + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 0dcde712246d417c9cdfedb73b518edaa2b8c60a Mon Sep 17 00:00:00 2001 From: Arya Kumar Date: Mon, 17 May 2021 15:00:15 +0000 Subject: Fix missing variable init in lint example --- clippy_lints/src/needless_bitwise_bool.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index 95febf4a2ad..b30bfbd4294 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -24,10 +24,12 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// let (x,y) = (true, false); /// if x & !y {} // where both x and y are booleans /// ``` /// Use instead: /// ```rust + /// let (x,y) = (true, false); /// if x && !y {} /// ``` pub NEEDLESS_BITWISE_BOOL, -- cgit 1.4.1-3-g733a5 From 2fb35ce4f0ea8d33bbe207c8a1c8822ebb90c813 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 21:40:20 +0200 Subject: Add generic args for comparison in `use_self` and `useless_conversion` lints --- clippy_lints/src/use_self.rs | 10 +++++----- clippy_lints/src/useless_conversion.rs | 15 ++++++++------- clippy_utils/src/ty.rs | 24 ++++++++++++++++++++++++ tests/ui/use_self.fixed | 30 ++++++++++++++++++++++++++++++ tests/ui/use_self.rs | 30 ++++++++++++++++++++++++++++++ tests/ui/use_self.stderr | 8 +++++++- tests/ui/useless_conversion.fixed | 19 +++++++++++++++++++ tests/ui/useless_conversion.rs | 19 +++++++++++++++++++ tests/ui/useless_conversion.stderr | 20 +++++++++++++++++++- 9 files changed, 161 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index aa4d16633ff..2ad6fa77f48 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::same_type_and_consts; use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::{ - def, + self as hir, + def::{self, DefKind}, def_id::LocalDefId, intravisit::{walk_ty, NestedVisitorMap, Visitor}, Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment, @@ -14,7 +14,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{AssocKind, Ty, TyS}; +use rustc_middle::ty::{AssocKind, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Span}; @@ -459,7 +459,7 @@ fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool { fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if_chain! { - if TyS::same_type(ty, self_ty); + if same_type_and_consts(ty, self_ty); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; then { !matches!(path.res, def::Res::SelfTy(..)) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 7edb280be73..2be99fb761b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths}; 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::{self, TyS}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if TyS::same_type(a, b) { + if same_type_and_consts(a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); span_lint_and_sugg( cx, @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if TyS::same_type(a, b) { + if same_type_and_consts(a, b) { let sugg = snippet(cx, args[0].span, "").into_owned(); span_lint_and_sugg( cx, @@ -110,7 +110,8 @@ impl<'tcx> LateLintPass<'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 TyS::same_type(a_type, b); + if same_type_and_consts(a_type, b); + then { span_lint_and_help( cx, @@ -137,7 +138,7 @@ impl<'tcx> LateLintPass<'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 TyS::same_type(a_type, b); + if same_type_and_consts(a_type, b); then { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); @@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::FROM_FROM); - if TyS::same_type(a, b); + if same_type_and_consts(a, b); then { let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 64a80f2554f..e1f8aff3740 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -322,3 +322,27 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { } inner(ty, 0) } + +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, +/// otherwise returns `false` +pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + match (&a.kind(), &b.kind()) { + (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a + .iter() + .zip(substs_b.iter()) + .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_and_consts(type_a, type_b) + }, + _ => true, + }) + }, + _ => a == b, + } +} diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 1282befdfb3..631da6fe066 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -462,3 +462,33 @@ mod issue6818 { a: i32, } } + +mod issue7206 { + struct MyStruct; + impl From> for MyStruct<'b'> { + fn from(_s: MyStruct<'a'>) -> Self { + Self + } + } + + // keep linting non-`Const` generic args + struct S<'a> { + inner: &'a str, + } + + struct S2 { + inner: T, + } + + impl S2 { + fn new() -> Self { + unimplemented!(); + } + } + + impl<'a> S2> { + fn new_again() -> Self { + Self::new() + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 7aaac7b2414..7a10d755faa 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -462,3 +462,33 @@ mod issue6818 { a: i32, } } + +mod issue7206 { + struct MyStruct; + impl From> for MyStruct<'b'> { + fn from(_s: MyStruct<'a'>) -> Self { + Self + } + } + + // keep linting non-`Const` generic args + struct S<'a> { + inner: &'a str, + } + + struct S2 { + inner: T, + } + + impl S2 { + fn new() -> Self { + unimplemented!(); + } + } + + impl<'a> S2> { + fn new_again() -> Self { + S2::new() + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index a32a9b9157d..cf6222c9b45 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -162,5 +162,11 @@ error: unnecessary structure name repetition LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: aborting due to 27 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:491:13 + | +LL | S2::new() + | ^^ help: use the applicable keyword: `Self` + +error: aborting due to 28 previous errors diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 03977de9455..76aa82068d6 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -70,4 +70,23 @@ fn main() { let a: i32 = 1; let b: i32 = 1; let _ = (a + b) * 3; + + // see #7205 + let s: Foo<'a'> = Foo; + let _: Foo<'b'> = s.into(); + let s2: Foo<'a'> = Foo; + let _: Foo<'a'> = s2; + let s3: Foo<'a'> = Foo; + let _ = s3; + let s4: Foo<'a'> = Foo; + let _ = vec![s4, s4, s4].into_iter(); +} + +#[derive(Copy, Clone)] +struct Foo; + +impl From> for Foo<'b'> { + fn from(_s: Foo<'a'>) -> Self { + Foo + } } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f6e094c1661..ccee7abb404 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -70,4 +70,23 @@ fn main() { let a: i32 = 1; let b: i32 = 1; let _ = i32::from(a + b) * 3; + + // see #7205 + let s: Foo<'a'> = Foo; + let _: Foo<'b'> = s.into(); + let s2: Foo<'a'> = Foo; + let _: Foo<'a'> = s2.into(); + let s3: Foo<'a'> = Foo; + let _ = Foo::<'a'>::from(s3); + let s4: Foo<'a'> = Foo; + let _ = vec![s4, s4, s4].into_iter().into_iter(); +} + +#[derive(Copy, Clone)] +struct Foo; + +impl From> for Foo<'b'> { + fn from(_s: Foo<'a'>) -> Self { + Foo + } } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 26a33595031..e6760f700f3 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -70,5 +70,23 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 11 previous errors +error: useless conversion to the same type: `Foo<'a'>` + --> $DIR/useless_conversion.rs:78:23 + | +LL | let _: Foo<'a'> = s2.into(); + | ^^^^^^^^^ help: consider removing `.into()`: `s2` + +error: useless conversion to the same type: `Foo<'a'>` + --> $DIR/useless_conversion.rs:80:13 + | +LL | let _ = Foo::<'a'>::from(s3); + | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` + +error: useless conversion to the same type: `std::vec::IntoIter>` + --> $DIR/useless_conversion.rs:82:13 + | +LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From b2270e1f459cbeb1b6f4281d230990a40fe993d8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 16 May 2021 17:40:29 -0500 Subject: Simplify manual_unwrap_or --- clippy_lints/src/manual_unwrap_or.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index cf183d4c16f..d7e8d180f7e 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -54,21 +54,6 @@ impl LateLintPass<'_> for ManualUnwrapOr { } } -#[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>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { @@ -101,10 +86,10 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) { - Some(Case::Option) + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::option_type) { + Some("Option") } else if is_type_diagnostic_item(cx, ty, sym::result_type) { - Some(Case::Result) + Some("Result") } else { None }; @@ -127,7 +112,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), + &format!("this pattern reimplements `{}::unwrap_or`", ty_name), "replace with", format!( "{}.unwrap_or({})", -- cgit 1.4.1-3-g733a5 From ca0f0002a4dd7a1572fc571483771a67d1d73a37 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 17 May 2021 14:02:42 -0500 Subject: Remove dead code after not linting else if let --- clippy_lints/src/option_if_let_else.rs | 45 +++------------------------------- 1 file changed, 3 insertions(+), 42 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 2d98b275de7..b6af4175edf 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_else_clause, is_lang_ctor}; +use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; @@ -81,7 +81,6 @@ struct OptionIfLetElseOccurence { method_sugg: String, some_expr: String, none_expr: String, - wrap_braces: bool, } /// Extracts the body of a given arm. If the arm contains only an expression, @@ -106,37 +105,6 @@ 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 curly braces. Otherwise, we don't. -fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { - let mut should_wrap = false; - - if let Some(Expr { - kind: - ExprKind::Match( - _, - arms, - MatchSource::IfLetDesugar { - contains_else_clause: true, - }, - ), - .. - }) = parent.expr - { - should_wrap = expr.hir_id == arms[1].body.hir_id; - } else if let Some(Expr { - kind: ExprKind::If(_, _, Some(else_clause)), - .. - }) = parent.expr - { - should_wrap = expr.hir_id == else_clause.hir_id; - } - - should_wrap - }) -} - fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", @@ -176,7 +144,6 @@ fn detect_option_if_let_else<'tcx>( let none_body = extract_body_from_arm(&arms[1])?; 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 { ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), @@ -192,7 +159,6 @@ fn detect_option_if_let_else<'tcx>( 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, }) } else { None @@ -210,13 +176,8 @@ impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), "try", format!( - "{}{}.{}({}, {}){}", - if detection.wrap_braces { "{ " } else { "" }, - 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, ), Applicability::MaybeIncorrect, ); -- cgit 1.4.1-3-g733a5 From 8356c485a99890339022a7afd99fc13ae09e5805 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 17 May 2021 14:20:26 -0500 Subject: Fix manual_unwrap_or FP deref reference --- clippy_lints/src/manual_unwrap_or.rs | 4 +--- tests/ui/manual_unwrap_or.fixed | 7 +++++++ tests/ui/manual_unwrap_or.rs | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index d7e8d180f7e..2f579edd6ad 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -11,7 +11,6 @@ use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -72,9 +71,8 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); + if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); if !contains_return_break_continue_macro(or_arm.body); - if !cx.typeck_results().expr_adjustments(unwrap_arm.body).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); then { Some(or_arm) } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 1efecb0ba12..3717f962745 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -171,4 +171,11 @@ fn format_name(name: Option<&Rc>) -> &str { } } +fn implicit_deref_ref() { + let _: &str = match Some(&"bye") { + None => "hi", + Some(s) => s, + }; +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 95d585ad18a..989adde1f5b 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -213,4 +213,11 @@ fn format_name(name: Option<&Rc>) -> &str { } } +fn implicit_deref_ref() { + let _: &str = match Some(&"bye") { + None => "hi", + Some(s) => s, + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From be540e65965d6ab0e3a04d6fa05651a701e6b8a4 Mon Sep 17 00:00:00 2001 From: "Bruno A. Muciño" Date: Mon, 17 May 2021 21:59:08 -0500 Subject: Remove powi, "square can be computed more efficiently" powi(2) produces exactly the same native code as x * x --- clippy_lints/src/floating_point_arithmetic.rs | 12 +--------- tests/ui/floating_point_powi.fixed | 5 ++-- tests/ui/floating_point_powi.rs | 5 ++-- tests/ui/floating_point_powi.stderr | 34 +++++++++------------------ 4 files changed, 18 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index e0b687b0205..08f28cd54c5 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -323,7 +323,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { cx, SUBOPTIMAL_FLOPS, parent.span, - "square can be computed more efficiently", + "multiply and add expressions can be calculated more efficiently and accurately", "consider using", format!( "{}.mul_add({}, {})", @@ -337,16 +337,6 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { return; } } - - 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], "..")), - Applicability::MachineApplicable, - ); } } } diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 56762400593..85f7c531e39 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -4,8 +4,6 @@ fn main() { let one = 1; let x = 3f32; - let _ = x * x; - let _ = x * x; let y = 4f32; let _ = x.mul_add(x, y); @@ -13,7 +11,10 @@ fn main() { 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(2); + let _ = x.powi(1 + 1); let _ = x.powi(3); + let _ = x.powi(4) + y; let _ = x.powi(one + 1); 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 1f800e4628d..ece61d1bec4 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -4,8 +4,6 @@ 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; @@ -13,7 +11,10 @@ fn main() { let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied + let _ = x.powi(2); + let _ = x.powi(1 + 1); let _ = x.powi(3); + let _ = x.powi(4) + y; 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 d5a5f1bcca1..37d840988bb 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,40 +1,28 @@ -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::suboptimal-flops` implied by `-D warnings` - -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 efficiently - --> $DIR/floating_point_powi.rs:11:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:9:13 | LL | let _ = x.powi(2) + y; | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:12:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:10: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 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:11:13 | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:14:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From a149ba26fa07b42b40f6e38f69ef74ecd756d68f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 18 May 2021 10:51:59 -0400 Subject: Fix ICE in `implicit_return` async functions always return a value --- clippy_lints/src/implicit_return.rs | 6 +++++- tests/ui/crashes/ice-7231.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-7231.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 30174fa2100..260a8f50157 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -147,7 +147,11 @@ fn lint_implicit_returns( visit_break_exprs(block, |break_expr, dest, sub_expr| { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && break_expr.span.ctxt() == ctxt { - lint_break(cx, break_expr.span, sub_expr.unwrap().span); + // At this point sub_expr can be `None` in async functions which either diverge, or return the + // unit type. + if let Some(sub_expr) = sub_expr { + lint_break(cx, break_expr.span, sub_expr.span); + } } else { // the break expression is from a macro call, add a return to the loop add_return = true; diff --git a/tests/ui/crashes/ice-7231.rs b/tests/ui/crashes/ice-7231.rs new file mode 100644 index 00000000000..5595d8d1d62 --- /dev/null +++ b/tests/ui/crashes/ice-7231.rs @@ -0,0 +1,10 @@ +// edition:2018 +#![allow(clippy::never_loop)] + +async fn f() { + loop { + break; + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 760f70312e6b894427cfc68a066bc1e87513337c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 12:20:43 -0400 Subject: Improve `multiple_inherent_impl` lint Treat different generic arguments as different types. Allow the lint to be ignored on the type definition, or any impl blocks. --- clippy_lints/src/inherent_impl.rs | 137 ++++++++++++++++++++++++++------------ clippy_lints/src/lib.rs | 2 +- tests/ui/impl.rs | 31 +++++++++ tests/ui/impl.stderr | 30 ++++++++- 4 files changed, 154 insertions(+), 46 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index c31013e49be..980bcc78f5d 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,12 +1,16 @@ //! lint on inherent implementations -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_macro; -use rustc_hir::def_id::DefIdMap; -use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{in_macro, is_allowed}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{ + def_id::{LocalDefId, LOCAL_CRATE}, + Crate, Item, ItemKind, Node, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; +use std::collections::hash_map::Entry; declare_clippy_lint! { /// **What it does:** Checks for multiple inherent implementations of a struct @@ -40,51 +44,96 @@ declare_clippy_lint! { "Multiple inherent impl that could be grouped" } -#[allow(clippy::module_name_repetitions)] -#[derive(Default)] -pub struct MultipleInherentImpl { - impls: DefIdMap, -} - -impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); +declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - ref generics, - of_trait: None, - .. - }) = item.kind + fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) { + // Map from a type to it's first impl block. Needed to distinguish generic arguments. + // e.g. `Foo` and `Foo` + let mut type_map = FxHashMap::default(); + // List of spans to lint. (lint_span, first_span) + let mut lint_spans = Vec::new(); + + for (_, impl_ids) in cx + .tcx + .crate_inherent_impls(LOCAL_CRATE) + .inherent_impls + .iter() + .filter(|(id, impls)| { + impls.len() > 1 + // Check for `#[allow]` on the type definition + && !is_allowed( + cx, + MULTIPLE_INHERENT_IMPL, + cx.tcx.hir().local_def_id_to_hir_id(id.expect_local()), + ) + }) { - // 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() { - self.impls.insert(item.def_id.to_def_id(), item.span); + for impl_id in impl_ids.iter().map(|id| id.expect_local()) { + match type_map.entry(cx.tcx.type_of(impl_id)) { + Entry::Vacant(e) => { + // Store the id for the first impl block of this type. The span is retrieved lazily. + e.insert(IdOrSpan::Id(impl_id)); + }, + Entry::Occupied(mut e) => { + if let Some(span) = get_impl_span(cx, impl_id) { + let first_span = match *e.get() { + IdOrSpan::Span(s) => s, + IdOrSpan::Id(id) => { + if let Some(s) = get_impl_span(cx, id) { + // Remember the span of the first block. + *e.get_mut() = IdOrSpan::Span(s); + s + } else { + // The first impl block isn't considered by the lint. Replace it with the + // current one. + *e.get_mut() = IdOrSpan::Span(span); + continue; + } + }, + }; + lint_spans.push((span, first_span)); + } + }, + } } + + // Switching to the next type definition, no need to keep the current entries around. + type_map.clear(); } - } - fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'_>) { - if !krate.items.is_empty() { - // Retrieve all inherent implementations from the crate, grouped by type - for impls in cx.tcx.crate_inherent_impls(def_id::LOCAL_CRATE).inherent_impls.values() { - // Filter out implementations that have generic params (type or lifetime) - let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def)); - if let Some(initial_span) = impl_spans.next() { - impl_spans.for_each(|additional_span| { - span_lint_and_then( - cx, - MULTIPLE_INHERENT_IMPL, - *additional_span, - "multiple implementations of this structure", - |diag| { - diag.span_note(*initial_span, "first implementation here"); - }, - ) - }) - } - } + // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. + lint_spans.sort_by_key(|x| x.0.lo()); + for (span, first_span) in lint_spans { + span_lint_and_note( + cx, + MULTIPLE_INHERENT_IMPL, + span, + "multiple implementations of this structure", + Some(first_span), + "first implementation here", + ); } } } + +/// Gets the span for the given impl block unless it's not being considered by the lint. +fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { + let id = cx.tcx.hir().local_def_id_to_hir_id(id); + if let Node::Item(&Item { + kind: ItemKind::Impl(ref impl_item), + span, + .. + }) = cx.tcx.hir().get(id) + { + (!in_macro(span) && impl_item.generics.params.is_empty() && !is_allowed(cx, MULTIPLE_INHERENT_IMPL, id)) + .then(|| span) + } else { + None + } +} + +enum IdOrSpan { + Id(LocalDefId), + Span(Span), +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..47c576bac48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1154,7 +1154,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); store.register_late_pass(|| box map_unit_fn::MapUnit); - store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); + store.register_late_pass(|| box inherent_impl::MultipleInherentImpl); store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box duration_subsec::DurationSubsec); diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 1c46e3a5337..39443775015 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -33,4 +33,35 @@ impl fmt::Debug for MyStruct { } } +// issue #5772 +struct WithArgs(T); +impl WithArgs { + fn f1() {} +} +impl WithArgs { + fn f2() {} +} +impl WithArgs { + fn f3() {} +} + +// Ok, the struct is allowed to have multiple impls. +#[allow(clippy::multiple_inherent_impl)] +struct Allowed; +impl Allowed {} +impl Allowed {} +impl Allowed {} + +struct AllowedImpl; +#[allow(clippy::multiple_inherent_impl)] +impl AllowedImpl {} +// Ok, the first block is skipped by this lint. +impl AllowedImpl {} + +struct OneAllowedImpl; +impl OneAllowedImpl {} +#[allow(clippy::multiple_inherent_impl)] +impl OneAllowedImpl {} +impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. + fn main() {} diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index aab688cc2d8..8703ecac93e 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -31,5 +31,33 @@ LL | | fn first() {} LL | | } | |_^ -error: aborting due to 2 previous errors +error: multiple implementations of this structure + --> $DIR/impl.rs:44:1 + | +LL | / impl WithArgs { +LL | | fn f3() {} +LL | | } + | |_^ + | +note: first implementation here + --> $DIR/impl.rs:41:1 + | +LL | / impl WithArgs { +LL | | fn f2() {} +LL | | } + | |_^ + +error: multiple implementations of this structure + --> $DIR/impl.rs:65:1 + | +LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> $DIR/impl.rs:62:1 + | +LL | impl OneAllowedImpl {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 8036d7f7842e77fde3be49b4c475665a61d9f560 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 19 May 2021 16:32:09 +0200 Subject: Adding the default lint level to the metadata collection And stripping the clippy:: prefix from the group --- .../src/utils/internal_lints/metadata_collector.rs | 72 ++++++++++++++++++---- 1 file changed, 59 insertions(+), 13 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b7c86ae6e18..19f20376c6f 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -37,10 +37,28 @@ const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "i /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; -/// Lints within this group will be excluded from the collection -const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"]; +/// Lints within this group will be excluded from the collection. These groups +/// have to be defined without the `clippy::` prefix. +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"]; /// Collected deprecated lint will be assigned to this group in the JSON output -const DEPRECATED_LINT_GROUP_STR: &str = "DEPRECATED"; +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated"; +/// This is the lint level for deprecated lints that will be displayed in the lint list +const DEPRECATED_LINT_LEVEL: &str = "none"; +/// This array holds Clippy's lint groups with their corresponding default lint level. The +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`. +const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [ + ("correctness", "deny"), + ("restriction", "allow"), + ("style", "warm"), + ("pedantic", "allow"), + ("complexity", "warn"), + ("perf", "warn"), + ("cargo", "allow"), + ("nursery", "allow"), +]; +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed +/// to only keep the actual lint group in the output. +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::"; /// This template will be used to format the configuration section in the lint documentation. /// The `configurations` parameter will be replaced with one or multiple formatted @@ -188,6 +206,7 @@ struct LintMetadata { id: String, id_span: SerializableSpan, group: String, + level: &'static str, docs: String, /// This field is only used in the output and will only be /// mapped shortly before the actual output. @@ -195,11 +214,12 @@ struct LintMetadata { } impl LintMetadata { - fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { + fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self { Self { id, id_span, group, + level, docs, applicability: None, } @@ -368,7 +388,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); // metadata extraction - if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); + if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item); if let Some(mut docs) = extract_attr_docs_or_lint(cx, item); then { if let Some(configuration_section) = self.get_lint_configs(&lint_name) { @@ -379,6 +399,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { lint_name, SerializableSpan::from_item(cx, item), group, + level, docs, )); } @@ -396,6 +417,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { lint_name, SerializableSpan::from_item(cx, item), DEPRECATED_LINT_GROUP_STR.to_string(), + DEPRECATED_LINT_LEVEL, docs, )); } @@ -475,15 +497,32 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { }) } -fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option { +fn get_lint_group_and_level_or_lint( + cx: &LateContext<'_>, + lint_name: &str, + item: &'hir Item<'_>, +) -> Option<(String, &'static str)> { let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - get_lint_group(cx, lint_lst[0]) - .or_else(|| { - lint_collection_error_item(cx, item, "Unable to determine lint group"); + if let Some(group) = get_lint_group(cx, lint_lst[0]) { + if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { + return None; + } + + if let Some(level) = get_lint_level_from_group(&group) { + Some((group, level)) + } else { + lint_collection_error_item( + cx, + item, + &format!("Unable to determine lint level for found group `{}`", group), + ); None - }) - .filter(|group| !EXCLUDED_LINT_GROUPS.contains(&group.as_str())) + } + } else { + lint_collection_error_item(cx, item, "Unable to determine lint group"); + None + } } else { lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); None @@ -496,14 +535,21 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { continue; } - if lints.iter().any(|x| *x == lint_id) { - return Some((*group_name).to_string()); + if lints.iter().any(|group_lint| *group_lint == lint_id) { + let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name); + return Some((*group).to_string()); } } None } +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> { + DEFAULT_LINT_LEVELS + .iter() + .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level)) +} + fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let hir::TyKind::Path(ref path) = ty.kind { if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) { -- cgit 1.4.1-3-g733a5 From 9dc366bc4d8a32197f28cb94b9c0e0a51108c809 Mon Sep 17 00:00:00 2001 From: Fridtjof Stoldt Date: Wed, 19 May 2021 17:23:33 +0200 Subject: Fixed a type Co-authored-by: Philipp Krones --- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 19f20376c6f..d6879022057 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -49,7 +49,7 @@ const DEPRECATED_LINT_LEVEL: &str = "none"; const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [ ("correctness", "deny"), ("restriction", "allow"), - ("style", "warm"), + ("style", "warn"), ("pedantic", "allow"), ("complexity", "warn"), ("perf", "warn"), -- cgit 1.4.1-3-g733a5 From 73048291157e845877a987cab5da8f0dbf1309ef Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 20 May 2021 13:57:52 +0200 Subject: Early return from LintPass registration when collecting metadata This speeds up the metadata collection by 2-2.5x on my machine. During metadata collection other lint passes don't have to be registered, only the lints themselves. --- clippy_lints/src/lib.rs | 951 ++++++++++++++++++++++++------------------------ 1 file changed, 477 insertions(+), 474 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2c83409b402..d1c129eba82 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -995,457 +995,172 @@ 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` - // all the internal lints - #[cfg(feature = "internal-lints")] - { - store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); - store.register_early_pass(|| box utils::internal_lints::ProduceIce); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); - store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); - store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); - store.register_late_pass(|| box utils::internal_lints::IfChainStyle); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); - store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); - store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); - store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - } - #[cfg(feature = "metadata-collector-lint")] - { - if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new()); - } - } - - store.register_late_pass(|| box utils::author::Author); - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeApi); - let vec_box_size_threshold = conf.vec_box_size_threshold; - let type_complexity_threshold = conf.type_complexity_threshold; - store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); - store.register_late_pass(|| box booleans::NonminimalBool); - store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); - store.register_late_pass(|| box eq_op::EqOp); - store.register_late_pass(|| box enum_clike::UnportableVariant); - store.register_late_pass(|| box float_literal::FloatLiteral); - 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 needless_for_each::NeedlessForEach); - store.register_late_pass(|| box approx_const::ApproxConstant); - store.register_late_pass(|| box misc::MiscLints); - store.register_late_pass(|| box eta_reduction::EtaReduction); - store.register_late_pass(|| box identity_op::IdentityOp); - store.register_late_pass(|| box erasing_op::ErasingOp); - store.register_late_pass(|| box mut_mut::MutMut); - 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 blocks_in_if_conditions::BlocksInIfConditions); - store.register_late_pass(|| box collapsible_match::CollapsibleMatch); - store.register_late_pass(|| box unicode::Unicode); - store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); - store.register_late_pass(|| box strings::StringAdd); - store.register_late_pass(|| box implicit_return::ImplicitReturn); - store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); - store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); - store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); - store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports); + 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(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), + LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), + LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), + LintId::of(exit::EXIT), + LintId::of(float_literal::LOSSY_FLOAT_LITERAL), + LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), + LintId::of(implicit_return::IMPLICIT_RETURN), + LintId::of(indexing_slicing::INDEXING_SLICING), + LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), + LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), + LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(map_err_ignore::MAP_ERR_IGNORE), + LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), + LintId::of(mem_forget::MEM_FORGET), + 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::UNWRAP_USED), + LintId::of(methods::WRONG_PUB_SELF_CONVENTION), + LintId::of(misc::FLOAT_CMP_CONST), + LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), + 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_fn::PANIC_IN_RESULT_FN), + LintId::of(panic_unimplemented::PANIC), + 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(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(shadow::SHADOW_REUSE), + LintId::of(shadow::SHADOW_SAME), + LintId::of(strings::STRING_ADD), + LintId::of(strings::STRING_TO_STRING), + LintId::of(strings::STR_TO_STRING), + LintId::of(types::RC_BUFFER), + LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), + LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), + LintId::of(verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(write::PRINT_STDERR), + LintId::of(write::PRINT_STDOUT), + LintId::of(write::USE_DEBUG), + ]); - let msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); - None - }) - }); + 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(bytecount::NAIVE_BYTECOUNT), + LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), + LintId::of(casts::CAST_LOSSLESS), + LintId::of(casts::CAST_POSSIBLE_TRUNCATION), + LintId::of(casts::CAST_POSSIBLE_WRAP), + LintId::of(casts::CAST_PRECISION_LOSS), + LintId::of(casts::CAST_PTR_ALIGNMENT), + LintId::of(casts::CAST_SIGN_LOSS), + LintId::of(casts::PTR_AS_PTR), + LintId::of(checked_conversions::CHECKED_CONVERSIONS), + LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(copy_iterator::COPY_ITERATOR), + LintId::of(default::DEFAULT_TRAIT_ACCESS), + LintId::of(dereference::EXPLICIT_DEREF_METHODS), + LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), + LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_MARKDOWN), + LintId::of(doc::MISSING_ERRORS_DOC), + LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(empty_enum::EMPTY_ENUM), + LintId::of(enum_variants::MODULE_NAME_REPETITIONS), + LintId::of(enum_variants::PUB_ENUM_VARIANT_NAMES), + LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), + LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), + LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(functions::MUST_USE_CANDIDATE), + LintId::of(functions::TOO_MANY_LINES), + LintId::of(if_not_else::IF_NOT_ELSE), + LintId::of(implicit_hasher::IMPLICIT_HASHER), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + LintId::of(infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), + LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), + LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(let_underscore::LET_UNDERSCORE_DROP), + LintId::of(literal_representation::LARGE_DIGIT_GROUPS), + LintId::of(literal_representation::UNREADABLE_LITERAL), + LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), + LintId::of(loops::EXPLICIT_ITER_LOOP), + LintId::of(macro_use::MACRO_USE_IMPORTS), + LintId::of(manual_ok_or::MANUAL_OK_OR), + 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), + LintId::of(methods::CLONED_INSTEAD_OF_COPIED), + LintId::of(methods::FILTER_MAP_NEXT), + LintId::of(methods::FLAT_MAP_OPTION), + LintId::of(methods::IMPLICIT_CLONE), + LintId::of(methods::INEFFICIENT_TO_STRING), + LintId::of(methods::MAP_FLATTEN), + 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), + LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), + LintId::of(needless_continue::NEEDLESS_CONTINUE), + LintId::of(needless_for_each::NEEDLESS_FOR_EACH), + 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(redundant_else::REDUNDANT_ELSE), + LintId::of(ref_option_ref::REF_OPTION_REF), + LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(strings::STRING_ADD_ASSIGN), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(types::LINKEDLIST), + LintId::of(types::OPTION_OPTION), + LintId::of(unicode::NON_ASCII_LITERAL), + LintId::of(unicode::UNICODE_NOT_NFC), + LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), + LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), + LintId::of(unused_async::UNUSED_ASYNC), + LintId::of(unused_self::UNUSED_SELF), + LintId::of(wildcard_imports::ENUM_GLOB_USE), + LintId::of(wildcard_imports::WILDCARD_IMPORTS), + LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), + ]); - store.register_late_pass(move || box methods::Methods::new(msrv)); - store.register_late_pass(move || box matches::Matches::new(msrv)); - store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); - store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); - store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); - store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); - store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); - store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); - store.register_late_pass(move || box ranges::Ranges::new(msrv)); - store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); - store.register_late_pass(move || box use_self::UseSelf::new(msrv)); - store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); - store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark); - store.register_late_pass(move || box casts::Casts::new(msrv)); - store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); - - store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); - store.register_late_pass(|| box map_clone::MapClone); - store.register_late_pass(|| box map_err_ignore::MapErrIgnore); - store.register_late_pass(|| box shadow::Shadow); - store.register_late_pass(|| box unit_types::UnitTypes); - store.register_late_pass(|| box loops::Loops); - store.register_late_pass(|| box main_recursion::MainRecursion::default()); - store.register_late_pass(|| box lifetimes::Lifetimes); - store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box minmax::MinMaxPass); - store.register_late_pass(|| box open_options::OpenOptions); - store.register_late_pass(|| box zero_div_zero::ZeroDiv); - store.register_late_pass(|| box mutex_atomic::Mutex); - store.register_late_pass(|| box needless_update::NeedlessUpdate); - store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); - store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); - store.register_late_pass(|| box no_effect::NoEffect); - store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); - store.register_late_pass(|| box transmute::Transmute); - let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; - 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 get_last_with_len::GetLastWithLen); - store.register_late_pass(|| box drop_forget_ref::DropForgetRef); - store.register_late_pass(|| box empty_enum::EmptyEnum); - store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons); - store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons); - store.register_late_pass(|| box regex::Regex::default()); - store.register_late_pass(|| box copies::CopyAndPaste); - store.register_late_pass(|| box copy_iterator::CopyIterator); - store.register_late_pass(|| box format::UselessFormat); - store.register_late_pass(|| box swap::Swap); - store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional); - store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); - let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::>(); - store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); - let too_many_arguments_threshold = conf.too_many_arguments_threshold; - let too_many_lines_threshold = conf.too_many_lines_threshold; - store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)); - let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); - store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); - store.register_late_pass(|| box neg_multiply::NegMultiply); - store.register_late_pass(|| box mem_discriminant::MemDiscriminant); - store.register_late_pass(|| box mem_forget::MemForget); - store.register_late_pass(|| box arithmetic::Arithmetic::default()); - store.register_late_pass(|| box assign_ops::AssignOps); - store.register_late_pass(|| box let_if_seq::LetIfSeq); - store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); - store.register_late_pass(|| box missing_doc::MissingDoc::new()); - store.register_late_pass(|| box missing_inline::MissingInline); - store.register_late_pass(move || box exhaustive_items::ExhaustiveItems); - store.register_late_pass(|| box if_let_some_result::OkIfLet); - 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; - 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 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 pass_by_ref_or_value); - store.register_late_pass(|| box ref_option_ref::RefOptionRef); - store.register_late_pass(|| box try_err::TryErr); - store.register_late_pass(|| box 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 useless_conversion::UselessConversion::default()); - store.register_late_pass(|| box implicit_hasher::ImplicitHasher); - store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); - store.register_late_pass(|| box double_comparison::DoubleComparisons); - store.register_late_pass(|| box question_mark::QuestionMark); - store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); - store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); - store.register_late_pass(|| box map_unit_fn::MapUnit); - store.register_late_pass(|| box inherent_impl::MultipleInherentImpl); - store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); - store.register_late_pass(|| box unwrap::Unwrap); - store.register_late_pass(|| box duration_subsec::DurationSubsec); - store.register_late_pass(|| box indexing_slicing::IndexingSlicing); - store.register_late_pass(|| box non_copy_const::NonCopyConst); - 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 unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); - store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); - store.register_late_pass(|| box transmuting_null::TransmutingNull); - store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); - store.register_late_pass(|| box integer_division::IntegerDivision); - store.register_late_pass(|| box inherent_to_string::InherentToString); - let max_trait_bounds = conf.max_trait_bounds; - 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); - 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); - 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 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); - store.register_early_pass(|| box needless_continue::NeedlessContinue); - store.register_early_pass(|| box redundant_else::RedundantElse); - store.register_late_pass(|| box create_dir::CreateDir); - store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); - let cargo_ignore_publish = conf.cargo_ignore_publish; - store.register_late_pass(move || box cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish)); - store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); - store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; - store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); - let literal_representation_threshold = conf.literal_representation_threshold; - store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); - let enum_variant_name_threshold = conf.enum_variant_name_threshold; - store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); - store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); - let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; - store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive)); - store.register_late_pass(|| box default::Default::default()); - store.register_late_pass(|| box unused_self::UnusedSelf); - store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); - store.register_late_pass(|| box exit::Exit); - store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome); - 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(|| box floating_point_arithmetic::FloatingPointArithmetic); - store.register_early_pass(|| box as_conversions::AsConversions); - store.register_late_pass(|| box let_underscore::LetUnderscore); - store.register_late_pass(|| box atomic_ordering::AtomicOrdering); - store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); - let max_fn_params_bools = conf.max_fn_params_bools; - 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_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); - store.register_late_pass(|| box dereference::Dereferencing::default()); - store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); - store.register_late_pass(|| box future_not_send::FutureNotSend); - 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_late_pass(|| box manual_async_fn::ManualAsyncFn); - store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); - store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; - store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { - single_char_binding_names_threshold, - }); - 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_late_pass(|| box unwrap_in_result::UnwrapInResult); - store.register_late_pass(|| box self_assignment::SelfAssignment); - store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); - store.register_late_pass(|| box manual_ok_or::ManualOkOr); - store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); - store.register_late_pass(|| box semicolon_if_nothing_returned::SemicolonIfNothingReturned); - store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - 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_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); - store.register_late_pass(|| box strings::StrToString); - store.register_late_pass(|| box strings::StringToString); - store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); - store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); - store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); - store.register_late_pass(|| box redundant_slicing::RedundantSlicing); - store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); - store.register_late_pass(|| box manual_map::ManualMap); - store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); - store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); - store.register_late_pass(|| box unused_async::UnusedAsync); - - 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(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(exit::EXIT), - LintId::of(float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), - LintId::of(implicit_return::IMPLICIT_RETURN), - LintId::of(indexing_slicing::INDEXING_SLICING), - LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(integer_division::INTEGER_DIVISION), - LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(map_err_ignore::MAP_ERR_IGNORE), - LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(mem_forget::MEM_FORGET), - 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::UNWRAP_USED), - LintId::of(methods::WRONG_PUB_SELF_CONVENTION), - LintId::of(misc::FLOAT_CMP_CONST), - LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), - 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_fn::PANIC_IN_RESULT_FN), - LintId::of(panic_unimplemented::PANIC), - 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(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(shadow::SHADOW_REUSE), - LintId::of(shadow::SHADOW_SAME), - LintId::of(strings::STRING_ADD), - LintId::of(strings::STRING_TO_STRING), - LintId::of(strings::STR_TO_STRING), - LintId::of(types::RC_BUFFER), - LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), - LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(verbose_file_reads::VERBOSE_FILE_READS), - LintId::of(write::PRINT_STDERR), - LintId::of(write::PRINT_STDOUT), - LintId::of(write::USE_DEBUG), - ]); - - 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(bytecount::NAIVE_BYTECOUNT), - LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(casts::CAST_LOSSLESS), - LintId::of(casts::CAST_POSSIBLE_TRUNCATION), - LintId::of(casts::CAST_POSSIBLE_WRAP), - LintId::of(casts::CAST_PRECISION_LOSS), - LintId::of(casts::CAST_PTR_ALIGNMENT), - LintId::of(casts::CAST_SIGN_LOSS), - LintId::of(casts::PTR_AS_PTR), - LintId::of(checked_conversions::CHECKED_CONVERSIONS), - LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(copy_iterator::COPY_ITERATOR), - LintId::of(default::DEFAULT_TRAIT_ACCESS), - LintId::of(dereference::EXPLICIT_DEREF_METHODS), - LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(doc::DOC_MARKDOWN), - LintId::of(doc::MISSING_ERRORS_DOC), - LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(empty_enum::EMPTY_ENUM), - LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(enum_variants::PUB_ENUM_VARIANT_NAMES), - LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(functions::MUST_USE_CANDIDATE), - LintId::of(functions::TOO_MANY_LINES), - LintId::of(if_not_else::IF_NOT_ELSE), - LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(let_underscore::LET_UNDERSCORE_DROP), - LintId::of(literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(literal_representation::UNREADABLE_LITERAL), - LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(loops::EXPLICIT_ITER_LOOP), - LintId::of(macro_use::MACRO_USE_IMPORTS), - LintId::of(manual_ok_or::MANUAL_OK_OR), - 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), - LintId::of(methods::CLONED_INSTEAD_OF_COPIED), - LintId::of(methods::FILTER_MAP_NEXT), - LintId::of(methods::FLAT_MAP_OPTION), - LintId::of(methods::IMPLICIT_CLONE), - LintId::of(methods::INEFFICIENT_TO_STRING), - LintId::of(methods::MAP_FLATTEN), - 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), - LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), - LintId::of(needless_continue::NEEDLESS_CONTINUE), - LintId::of(needless_for_each::NEEDLESS_FOR_EACH), - 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(redundant_else::REDUNDANT_ELSE), - LintId::of(ref_option_ref::REF_OPTION_REF), - LintId::of(shadow::SHADOW_UNRELATED), - LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(types::LINKEDLIST), - LintId::of(types::OPTION_OPTION), - LintId::of(unicode::NON_ASCII_LITERAL), - LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), - LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), - LintId::of(unused_async::UNUSED_ASYNC), - LintId::of(unused_self::UNUSED_SELF), - LintId::of(wildcard_imports::ENUM_GLOB_USE), - LintId::of(wildcard_imports::WILDCARD_IMPORTS), - LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), - ]); - - #[cfg(feature = "internal-lints")] - store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_LINT), - LintId::of(utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), - ]); + #[cfg(feature = "internal-lints")] + store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ + LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::DEFAULT_LINT), + LintId::of(utils::internal_lints::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::INVALID_PATHS), + LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), + LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), + ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), @@ -2048,32 +1763,320 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), ]); - store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), - LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), - LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), - ]); + store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ + LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), + LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), + ]); + + 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), + 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), + 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(regex::TRIVIAL_REGEX), + LintId::of(strings::STRING_LIT_AS_BYTES), + LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(use_self::USE_SELF), + ]); + + #[cfg(feature = "metadata-collector-lint")] + { + if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new()); + return; + } + } + + // all the internal lints + #[cfg(feature = "internal-lints")] + { + store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); + store.register_early_pass(|| box utils::internal_lints::ProduceIce); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); + store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::IfChainStyle); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); + store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + } + + store.register_late_pass(|| box utils::author::Author); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); + store.register_late_pass(|| box serde_api::SerdeApi); + let vec_box_size_threshold = conf.vec_box_size_threshold; + let type_complexity_threshold = conf.type_complexity_threshold; + store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); + store.register_late_pass(|| box booleans::NonminimalBool); + store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); + store.register_late_pass(|| box eq_op::EqOp); + store.register_late_pass(|| box enum_clike::UnportableVariant); + store.register_late_pass(|| box float_literal::FloatLiteral); + 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 needless_for_each::NeedlessForEach); + store.register_late_pass(|| box approx_const::ApproxConstant); + store.register_late_pass(|| box misc::MiscLints); + store.register_late_pass(|| box eta_reduction::EtaReduction); + store.register_late_pass(|| box identity_op::IdentityOp); + store.register_late_pass(|| box erasing_op::ErasingOp); + store.register_late_pass(|| box mut_mut::MutMut); + 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 blocks_in_if_conditions::BlocksInIfConditions); + store.register_late_pass(|| box collapsible_match::CollapsibleMatch); + store.register_late_pass(|| box unicode::Unicode); + store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); + store.register_late_pass(|| box strings::StringAdd); + store.register_late_pass(|| box implicit_return::ImplicitReturn); + store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); + store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); + store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); + store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); + store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports); + + let msrv = conf.msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); + None + }) + }); + + store.register_late_pass(move || box methods::Methods::new(msrv)); + store.register_late_pass(move || box matches::Matches::new(msrv)); + store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); + store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); + store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); + store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); + store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); + store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); + store.register_late_pass(move || box use_self::UseSelf::new(msrv)); + store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark); + store.register_late_pass(move || box casts::Casts::new(msrv)); + store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); + + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); + store.register_late_pass(|| box map_clone::MapClone); + store.register_late_pass(|| box map_err_ignore::MapErrIgnore); + store.register_late_pass(|| box shadow::Shadow); + store.register_late_pass(|| box unit_types::UnitTypes); + store.register_late_pass(|| box loops::Loops); + store.register_late_pass(|| box main_recursion::MainRecursion::default()); + store.register_late_pass(|| box lifetimes::Lifetimes); + store.register_late_pass(|| box entry::HashMapPass); + store.register_late_pass(|| box minmax::MinMaxPass); + store.register_late_pass(|| box open_options::OpenOptions); + store.register_late_pass(|| box zero_div_zero::ZeroDiv); + store.register_late_pass(|| box mutex_atomic::Mutex); + store.register_late_pass(|| box needless_update::NeedlessUpdate); + store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); + store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); + store.register_late_pass(|| box no_effect::NoEffect); + store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); + store.register_late_pass(|| box transmute::Transmute); + let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; + 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 get_last_with_len::GetLastWithLen); + store.register_late_pass(|| box drop_forget_ref::DropForgetRef); + store.register_late_pass(|| box empty_enum::EmptyEnum); + store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons); + store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons); + store.register_late_pass(|| box regex::Regex::default()); + store.register_late_pass(|| box copies::CopyAndPaste); + store.register_late_pass(|| box copy_iterator::CopyIterator); + store.register_late_pass(|| box format::UselessFormat); + store.register_late_pass(|| box swap::Swap); + store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional); + store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); + let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::>(); + store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); + let too_many_arguments_threshold = conf.too_many_arguments_threshold; + let too_many_lines_threshold = conf.too_many_lines_threshold; + store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)); + let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); + store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); + store.register_late_pass(|| box neg_multiply::NegMultiply); + store.register_late_pass(|| box mem_discriminant::MemDiscriminant); + store.register_late_pass(|| box mem_forget::MemForget); + store.register_late_pass(|| box arithmetic::Arithmetic::default()); + store.register_late_pass(|| box assign_ops::AssignOps); + store.register_late_pass(|| box let_if_seq::LetIfSeq); + store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); + store.register_late_pass(|| box missing_doc::MissingDoc::new()); + store.register_late_pass(|| box missing_inline::MissingInline); + store.register_late_pass(move || box exhaustive_items::ExhaustiveItems); + store.register_late_pass(|| box if_let_some_result::OkIfLet); + 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; + 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 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 pass_by_ref_or_value); + store.register_late_pass(|| box ref_option_ref::RefOptionRef); + store.register_late_pass(|| box try_err::TryErr); + store.register_late_pass(|| box 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 useless_conversion::UselessConversion::default()); + store.register_late_pass(|| box implicit_hasher::ImplicitHasher); + store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); + store.register_late_pass(|| box double_comparison::DoubleComparisons); + store.register_late_pass(|| box question_mark::QuestionMark); + store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); + store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); + store.register_late_pass(|| box map_unit_fn::MapUnit); + store.register_late_pass(|| box inherent_impl::MultipleInherentImpl); + store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); + store.register_late_pass(|| box unwrap::Unwrap); + store.register_late_pass(|| box duration_subsec::DurationSubsec); + store.register_late_pass(|| box indexing_slicing::IndexingSlicing); + store.register_late_pass(|| box non_copy_const::NonCopyConst); + 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 unnecessary_sort_by::UnnecessarySortBy); + store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); + store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); + store.register_late_pass(|| box transmuting_null::TransmutingNull); + store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); + store.register_late_pass(|| box integer_division::IntegerDivision); + store.register_late_pass(|| box inherent_to_string::InherentToString); + let max_trait_bounds = conf.max_trait_bounds; + 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); + 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); + 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 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); + store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box redundant_else::RedundantElse); + store.register_late_pass(|| box create_dir::CreateDir); + store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); + let cargo_ignore_publish = conf.cargo_ignore_publish; + store.register_late_pass(move || box cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish)); + store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); + store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); + let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; + store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); + let literal_representation_threshold = conf.literal_representation_threshold; + store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); + let enum_variant_name_threshold = conf.enum_variant_name_threshold; + store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); + store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; + store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive)); + store.register_late_pass(|| box default::Default::default()); + store.register_late_pass(|| box unused_self::UnusedSelf); + store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); + store.register_late_pass(|| box exit::Exit); + store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome); + 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(|| box floating_point_arithmetic::FloatingPointArithmetic); + store.register_early_pass(|| box as_conversions::AsConversions); + store.register_late_pass(|| box let_underscore::LetUnderscore); + store.register_late_pass(|| box atomic_ordering::AtomicOrdering); + store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); + let max_fn_params_bools = conf.max_fn_params_bools; + 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_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); + store.register_late_pass(|| box dereference::Dereferencing::default()); + store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); + store.register_late_pass(|| box future_not_send::FutureNotSend); + 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_late_pass(|| box manual_async_fn::ManualAsyncFn); + store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); + store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; + store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { + single_char_binding_names_threshold, + }); + 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_late_pass(|| box unwrap_in_result::UnwrapInResult); + store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); + store.register_late_pass(|| box manual_ok_or::ManualOkOr); + store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box semicolon_if_nothing_returned::SemicolonIfNothingReturned); + store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + 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_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); + store.register_late_pass(|| box strings::StrToString); + store.register_late_pass(|| box strings::StringToString); + store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); + store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); + store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); + store.register_late_pass(|| box redundant_slicing::RedundantSlicing); + store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); + store.register_late_pass(|| box manual_map::ManualMap); + store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); + store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_late_pass(|| box unused_async::UnusedAsync); - 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), - 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), - 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(regex::TRIVIAL_REGEX), - LintId::of(strings::STRING_LIT_AS_BYTES), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(use_self::USE_SELF), - ]); } #[rustfmt::skip] -- cgit 1.4.1-3-g733a5 From 6e03a306ac44c6670064c68197df8813fe1cac06 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 17 Apr 2021 20:46:13 -0400 Subject: Remove fix for rustc bug from `needless_borrow` The spans given for derived traits used to not indicate they were from a macro expansion. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/needless_borrow.rs | 28 ++++------------------------ 2 files changed, 5 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d1c129eba82..5cd7d5f14e3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1890,7 +1890,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box zero_div_zero::ZeroDiv); store.register_late_pass(|| box mutex_atomic::Mutex); store.register_late_pass(|| box needless_update::NeedlessUpdate); - store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); + store.register_late_pass(|| box needless_borrow::NeedlessBorrow); store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); store.register_late_pass(|| box no_effect::NoEffect); store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index eef3c16730b..3adbbfb8e89 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -3,16 +3,14 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_automatically_derived; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind}; +use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -37,15 +35,13 @@ declare_clippy_lint! { } #[derive(Default)] -pub struct NeedlessBorrow { - derived_item: Option, -} +pub struct NeedlessBorrow; impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() || self.derived_item.is_some() { + if e.span.from_expansion() { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind { @@ -86,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() || self.derived_item.is_some() { + if pat.span.from_expansion() { return; } if_chain! { @@ -116,20 +112,4 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } } } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if is_automatically_derived(attrs) { - debug_assert!(self.derived_item.is_none()); - self.derived_item = Some(item.def_id); - } - } - - fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let Some(id) = self.derived_item { - if item.def_id == id { - self.derived_item = None; - } - } - } } -- cgit 1.4.1-3-g733a5 From 6d4dc35882a55610e9da81237d62bea1c0c1634e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 17 Apr 2021 20:35:39 -0400 Subject: Improve `needless_borrow` lint Suggest changing usages of ref bindings to match the new type Split out some cases into new lint `ref_binding_to_reference` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 +- clippy_lints/src/needless_borrow.rs | 203 +++++++++++++++++++++++++++---- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/lib.rs | 4 +- clippy_utils/src/visitors.rs | 37 ++---- tests/ui/needless_borrow.fixed | 17 --- tests/ui/needless_borrow.rs | 17 --- tests/ui/needless_borrow.stderr | 16 +-- tests/ui/needless_borrow_pat.rs | 151 +++++++++++++++++++++++ tests/ui/needless_borrow_pat.stderr | 112 +++++++++++++++++ tests/ui/ref_binding_to_reference.rs | 76 ++++++++++++ tests/ui/ref_binding_to_reference.stderr | 88 ++++++++++++++ 13 files changed, 626 insertions(+), 102 deletions(-) create mode 100644 tests/ui/needless_borrow_pat.rs create mode 100644 tests/ui/needless_borrow_pat.stderr create mode 100644 tests/ui/ref_binding_to_reference.rs create mode 100644 tests/ui/ref_binding_to_reference.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index da5a0712c95..abfe7f91f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2622,6 +2622,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5cd7d5f14e3..a263e8b0ca4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -841,6 +841,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: needless_bool::BOOL_COMPARISON, needless_bool::NEEDLESS_BOOL, needless_borrow::NEEDLESS_BORROW, + needless_borrow::REF_BINDING_TO_REFERENCE, needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, @@ -1116,6 +1117,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(mut_mut::MUT_MUT), LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), + LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE), LintId::of(needless_continue::NEEDLESS_CONTINUE), LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), @@ -1890,7 +1892,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box zero_div_zero::ZeroDiv); store.register_late_pass(|| box mutex_atomic::Mutex); store.register_late_pass(|| box needless_update::NeedlessUpdate); - store.register_late_pass(|| box needless_borrow::NeedlessBorrow); + store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); store.register_late_pass(|| box no_effect::NoEffect); store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 3adbbfb8e89..965b131770e 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -3,14 +3,18 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_applicability, snippet_with_context}; +use clippy_utils::{get_parent_expr, in_macro, path_to_local}; use if_chain::if_chain; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_hir::{BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -34,13 +38,65 @@ declare_clippy_lint! { "taking a reference that is going to be automatically dereferenced" } +declare_clippy_lint! { + /// **What it does:** Checks for `ref` bindings which create a reference to a reference. + /// + /// **Why is this bad?** The address-of operator at the use site is clearer about the need for a reference. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let x = Some(""); + /// if let Some(ref x) = x { + /// // use `x` here + /// } + /// + /// // Good + /// let x = Some(""); + /// if let Some(x) = x { + /// // use `&x` here + /// } + /// ``` + pub REF_BINDING_TO_REFERENCE, + pedantic, + "`ref` binding to a reference" +} + +impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE]); #[derive(Default)] -pub struct NeedlessBorrow; +pub struct NeedlessBorrow { + /// The body the first local was found in. Used to emit lints when the traversal of the body has + /// been finished. Note we can't lint at the end of every body as they can be nested within each + /// other. + current_body: Option, + /// The list of locals currently being checked by the lint. + /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted. + /// This is needed for or patterns where one of the branches can be linted, but another can not + /// be. + /// + /// e.g. `m!(x) | Foo::Bar(ref x)` + ref_locals: FxIndexMap>, +} -impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]); +struct RefPat { + /// Whether every usage of the binding is dereferenced. + always_deref: bool, + /// The spans of all the ref bindings for this local. + spans: Vec, + /// The applicability of this suggestion. + app: Applicability, + /// All the replacements which need to be made. + replacements: Vec<(Span, String)>, +} impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let Some(local) = path_to_local(e) { + self.check_local_usage(cx, e, local); + } + if e.span.from_expansion() { return; } @@ -81,35 +137,132 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } } } + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { - return; + if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind { + if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) { + // This binding id has been seen before. Add this pattern to the list of changes. + if let Some(prev_pat) = opt_prev_pat { + if in_macro(pat.span) { + // Doesn't match the context of the previous pattern. Can't lint here. + *opt_prev_pat = None; + } else { + prev_pat.spans.push(pat.span); + prev_pat.replacements.push(( + pat.span, + snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app) + .0 + .into(), + )); + } + } + return; + } + + if_chain! { + if !in_macro(pat.span); + if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind(); + // only lint immutable refs, because borrowed `&mut T` cannot be moved out + if let ty::Ref(_, _, Mutability::Not) = *tam.kind(); + then { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; + self.current_body = self.current_body.or(cx.enclosing_body); + self.ref_locals.insert( + id, + Some(RefPat { + always_deref: true, + spans: vec![pat.span], + app, + replacements: vec![(pat.span, snip.into())], + }), + ); + } + } } - if_chain! { - if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind; - if let ty::Ref(_, tam, mutbl) = *cx.typeck_results().pat_ty(pat).kind(); - if mutbl == Mutability::Not; - if let ty::Ref(_, _, mutbl) = *tam.kind(); - // only lint immutable refs, because borrowed `&mut T` cannot be moved out - if mutbl == Mutability::Not; - then { + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if Some(body.id()) == self.current_body { + for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { + let replacements = pat.replacements; + let app = pat.app; span_lint_and_then( cx, - NEEDLESS_BORROW, - pat.span, + if pat.always_deref { + NEEDLESS_BORROW + } else { + REF_BINDING_TO_REFERENCE + }, + pat.spans, "this pattern creates a reference to a reference", |diag| { - if let Some(snippet) = snippet_opt(cx, name.span) { - diag.span_suggestion( - pat.span, - "change this to", - snippet, - Applicability::MachineApplicable, - ); - } - } + diag.multipart_suggestion("try this", replacements, app); + }, ) } + self.current_body = None; + } + } +} +impl NeedlessBorrow { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) { + if let Some(outer_pat) = self.ref_locals.get_mut(&local) { + if let Some(pat) = outer_pat { + // Check for auto-deref + if !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), + .. + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, + .. + ] + ) { + match get_parent_expr(cx, e) { + // Field accesses are the same no matter the number of references. + Some(Expr { + kind: ExprKind::Field(..), + .. + }) => (), + Some(&Expr { + span, + kind: ExprKind::Unary(UnOp::Deref, _), + .. + }) if !in_macro(span) => { + // Remove explicit deref. + let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((span, snip.into())); + }, + Some(parent) if !in_macro(parent.span) => { + // Double reference might be needed at this point. + if parent.precedence().order() == PREC_POSTFIX { + // Parentheses would be needed here, don't lint. + *outer_pat = None; + } else { + pat.always_deref = false; + let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((e.span, format!("&{}", snip))); + } + }, + _ if !in_macro(e.span) => { + // Double reference might be needed at this point. + pat.always_deref = false; + let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); + pat.replacements.push((e.span, format!("&{}", snip))); + }, + // Edge case for macros. The span of the identifier will usually match the context of the + // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc + // macros + _ => *outer_pat = None, + } + } + } } } } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 0c0e4d3b4ce..25b4491616c 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -70,7 +70,7 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::ExprKind::Struct(path, ref fields, None) => match path { hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { start: None, end: None, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 82250151aab..2b9b214daa7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1254,12 +1254,12 @@ pub fn match_function_call<'tcx>( path: &[&str], ) -> Option<&'tcx [Expr<'tcx>]> { if_chain! { - if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Call(ref fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); if match_def_path(cx, fun_def_id, path); then { - return Some(&args) + return Some(args) } }; None diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ecdc666b5f6..73f132eef4d 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -189,34 +189,21 @@ impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { } } +/// A type which can be visited. pub trait Visitable<'tcx> { - fn visit>(self, v: &mut V); + /// Calls the corresponding `visit_*` function on the visitor. + fn visit>(self, visitor: &mut V); } -impl Visitable<'tcx> for &'tcx Expr<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_expr(self) - } -} -impl Visitable<'tcx> for &'tcx Block<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_block(self) - } -} -impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_stmt(self) - } -} -impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_body(self) - } -} -impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_arm(self) - } +macro_rules! visitable_ref { + ($t:ident, $f:ident) => { + impl Visitable<'tcx> for &'tcx $t<'tcx> { + fn visit>(self, visitor: &mut V) { + visitor.$f(self); + } + } + }; } +visitable_ref!(Block, visit_block); /// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 5ae4a0e79b9..a87171dc3f2 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -18,7 +18,6 @@ fn main() { let vec = Vec::new(); let vec_val = g(&vec); // should not error, because `&Vec` derefs to `&[T]` h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` - if let Some(cake) = Some(&5) {} let garbl = match 42 { 44 => &a, 45 => { @@ -43,19 +42,3 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[warn(clippy::needless_borrow)] -#[allow(dead_code)] -fn issue_1432() { - let mut v = Vec::::new(); - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - let _ = v.iter().filter(|&a| a.is_empty()); - - let _ = v.iter().filter(|&a| a.is_empty()); -} - -#[allow(dead_code)] -#[warn(clippy::needless_borrow)] -#[derive(Debug)] -enum Foo<'a> { - Str(&'a str), -} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 1e281316c8a..059dc8ceac3 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -18,7 +18,6 @@ fn main() { let vec = Vec::new(); let vec_val = g(&vec); // should not error, because `&Vec` derefs to `&[T]` h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` - if let Some(ref cake) = Some(&5) {} let garbl = match 42 { 44 => &a, 45 => { @@ -43,19 +42,3 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[warn(clippy::needless_borrow)] -#[allow(dead_code)] -fn issue_1432() { - let mut v = Vec::::new(); - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - let _ = v.iter().filter(|&ref a| a.is_empty()); - - let _ = v.iter().filter(|&a| a.is_empty()); -} - -#[allow(dead_code)] -#[warn(clippy::needless_borrow)] -#[derive(Debug)] -enum Foo<'a> { - Str(&'a str), -} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index bea4b41b803..6eddf26db06 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -6,23 +6,11 @@ LL | let c = x(&&a); | = note: `-D clippy::needless-borrow` implied by `-D warnings` -error: this pattern creates a reference to a reference - --> $DIR/needless_borrow.rs:21:17 - | -LL | if let Some(ref cake) = Some(&5) {} - | ^^^^^^^^ help: change this to: `cake` - error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:28:15 + --> $DIR/needless_borrow.rs:27:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` -error: this pattern creates a reference to a reference - --> $DIR/needless_borrow.rs:51:31 - | -LL | let _ = v.iter().filter(|&ref a| a.is_empty()); - | ^^^^^ help: change this to: `a` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/needless_borrow_pat.rs b/tests/ui/needless_borrow_pat.rs new file mode 100644 index 00000000000..f0926220755 --- /dev/null +++ b/tests/ui/needless_borrow_pat.rs @@ -0,0 +1,151 @@ +// edition:2018 +// FIXME: run-rustfix waiting on multi-span suggestions + +#![warn(clippy::needless_borrow)] +#![allow(clippy::needless_borrowed_reference)] + +fn f1(_: &str) {} +macro_rules! m1 { + ($e:expr) => { + f1($e); + }; +} +macro_rules! m3 { + ($i:ident) => { + Some(ref $i) + }; +} +macro_rules! if_chain { + (if $e:expr; $($rest:tt)*) => { + if $e { + if_chain!($($rest)*) + } + }; + + (if let $p:pat = $e:expr; $($rest:tt)*) => { + if let $p = $e { + if_chain!($($rest)*) + } + }; + + (then $b:block) => { + $b + }; +} + +#[allow(dead_code)] +fn main() { + let x = String::new(); + + // Ok, reference to a String. + let _: &String = match Some(x.clone()) { + Some(ref x) => x, + None => return, + }; + + // Ok, reference to a &mut String + let _: &&mut String = match Some(&mut x.clone()) { + Some(ref x) => x, + None => return, + }; + + // Ok, the pattern is from a macro + let _: &String = match Some(&x) { + m3!(x) => x, + None => return, + }; + + // Err, reference to a &String + let _: &String = match Some(&x) { + Some(ref x) => x, + None => return, + }; + + // Err, reference to a &String. + let _: &String = match Some(&x) { + Some(ref x) => *x, + None => return, + }; + + // Err, reference to a &String + let _: &String = match Some(&x) { + Some(ref x) => { + f1(x); + f1(*x); + x + }, + None => return, + }; + + // Err, reference to a &String + match Some(&x) { + Some(ref x) => m1!(x), + None => return, + }; + + // Err, reference to a &String + let _ = |&ref x: &&String| { + let _: &String = x; + }; + + // Err, reference to a &String + let (ref y,) = (&x,); + let _: &String = *y; + + let y = &&x; + // Ok, different y + let _: &String = *y; + + let x = (0, 0); + // Err, reference to a &u32. Don't suggest adding a reference to the field access. + let _: u32 = match Some(&x) { + Some(ref x) => x.0, + None => return, + }; + + enum E { + A(&'static u32), + B(&'static u32), + } + // Err, reference to &u32. + let _: &u32 = match E::A(&0) { + E::A(ref x) | E::B(ref x) => *x, + }; + + // Err, reference to &String. + if_chain! { + if true; + if let Some(ref x) = Some(&String::new()); + then { + f1(x); + } + } +} + +// Err, reference to a &String +fn f2<'a>(&ref x: &&'a String) -> &'a String { + let _: &String = x; + *x +} + +trait T1 { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &String = x; + } +} + +struct S; +impl T1 for S { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &String = *x; + } +} + +// Ok - used to error due to rustc bug +#[allow(dead_code)] +#[derive(Debug)] +enum Foo<'a> { + Str(&'a str), +} diff --git a/tests/ui/needless_borrow_pat.stderr b/tests/ui/needless_borrow_pat.stderr new file mode 100644 index 00000000000..32913d59f7a --- /dev/null +++ b/tests/ui/needless_borrow_pat.stderr @@ -0,0 +1,112 @@ +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:60:14 + | +LL | Some(ref x) => x, + | ^^^^^ help: try this: `x` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:66:14 + | +LL | Some(ref x) => *x, + | ^^^^^ + | +help: try this + | +LL | Some(x) => x, + | ^ ^ + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:72:14 + | +LL | Some(ref x) => { + | ^^^^^ + | +help: try this + | +LL | Some(x) => { +LL | f1(x); +LL | f1(x); + | + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:82:14 + | +LL | Some(ref x) => m1!(x), + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:87:15 + | +LL | let _ = |&ref x: &&String| { + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:92:10 + | +LL | let (ref y,) = (&x,); + | ^^^^^ + | +help: try this + | +LL | let (y,) = (&x,); +LL | let _: &String = y; + | + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:102:14 + | +LL | Some(ref x) => x.0, + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:112:14 + | +LL | E::A(ref x) | E::B(ref x) => *x, + | ^^^^^ ^^^^^ + | +help: try this + | +LL | E::A(x) | E::B(x) => x, + | ^ ^ ^ + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:118:21 + | +LL | if let Some(ref x) = Some(&String::new()); + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:126:12 + | +LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { + | ^^^^^ + | +help: try this + | +LL | fn f2<'a>(&x: &&'a String) -> &'a String { +LL | let _: &String = x; +LL | x + | + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:133:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:141:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ + | +help: try this + | +LL | fn f(&x: &&String) { +LL | let _: &String = x; + | + +error: aborting due to 12 previous errors + diff --git a/tests/ui/ref_binding_to_reference.rs b/tests/ui/ref_binding_to_reference.rs new file mode 100644 index 00000000000..c7235e1c221 --- /dev/null +++ b/tests/ui/ref_binding_to_reference.rs @@ -0,0 +1,76 @@ +// edition:2018 +// FIXME: run-rustfix waiting on multi-span suggestions + +#![warn(clippy::ref_binding_to_reference)] +#![allow(clippy::needless_borrowed_reference)] + +fn f1(_: &str) {} +macro_rules! m2 { + ($e:expr) => { + f1(*$e); + }; +} +macro_rules! m3 { + ($i:ident) => { + Some(ref $i) + }; +} + +#[allow(dead_code)] +fn main() { + let x = String::new(); + + // Ok, the pattern is from a macro + let _: &&String = match Some(&x) { + m3!(x) => x, + None => return, + }; + + // Err, reference to a &String + let _: &&String = match Some(&x) { + Some(ref x) => x, + None => return, + }; + + // Err, reference to a &String + let _: &&String = match Some(&x) { + Some(ref x) => { + f1(x); + f1(*x); + x + }, + None => return, + }; + + // Err, reference to a &String + match Some(&x) { + Some(ref x) => m2!(x), + None => return, + } + + // Err, reference to a &String + let _ = |&ref x: &&String| { + let _: &&String = x; + }; +} + +// Err, reference to a &String +fn f2<'a>(&ref x: &&'a String) -> &'a String { + let _: &&String = x; + *x +} + +trait T1 { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &&String = x; + } +} + +struct S; +impl T1 for S { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &&String = x; + } +} diff --git a/tests/ui/ref_binding_to_reference.stderr b/tests/ui/ref_binding_to_reference.stderr new file mode 100644 index 00000000000..00aeff4fefa --- /dev/null +++ b/tests/ui/ref_binding_to_reference.stderr @@ -0,0 +1,88 @@ +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:31:14 + | +LL | Some(ref x) => x, + | ^^^^^ + | + = note: `-D clippy::ref-binding-to-reference` implied by `-D warnings` +help: try this + | +LL | Some(x) => &x, + | ^ ^^ + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:37:14 + | +LL | Some(ref x) => { + | ^^^^^ + | +help: try this + | +LL | Some(x) => { +LL | f1(x); +LL | f1(x); +LL | &x + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:47:14 + | +LL | Some(ref x) => m2!(x), + | ^^^^^ + | +help: try this + | +LL | Some(x) => m2!(&x), + | ^ ^^ + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:52:15 + | +LL | let _ = |&ref x: &&String| { + | ^^^^^ + | +help: try this + | +LL | let _ = |&x: &&String| { +LL | let _: &&String = &x; + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:58:12 + | +LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { + | ^^^^^ + | +help: try this + | +LL | fn f2<'a>(&x: &&'a String) -> &'a String { +LL | let _: &&String = &x; +LL | x + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:65:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ + | +help: try this + | +LL | fn f(&x: &&String) { +LL | let _: &&String = &x; + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:73:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ + | +help: try this + | +LL | fn f(&x: &&String) { +LL | let _: &&String = &x; + | + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 2eafec182d82fe85bcf88f78bd76ea143b5a6fba Mon Sep 17 00:00:00 2001 From: John Simon Date: Thu, 20 May 2021 10:12:54 -0400 Subject: Allow wparam and lparam in similar_names --- clippy_lints/src/non_expressive_names.rs | 1 + tests/ui/similar_names.rs | 4 ++++ tests/ui/similar_names.stderr | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 52661416de6..7b12363a3fe 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -125,6 +125,7 @@ const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[ &["args", "arms"], &["qpath", "path"], &["lit", "lint"], + &["wparam", "lparam"], ]; struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 5981980988b..2b1bc1f4859 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -72,6 +72,10 @@ fn main() { let rx1: i32; let tx_cake: i32; let rx_cake: i32; + + // names often used in win32 code (for example WindowProc) + let wparam: i32; + let lparam: i32; } fn foo() { diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index 0256f126a94..a7eb2be0778 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -92,13 +92,13 @@ LL | let parsee: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:81:16 + --> $DIR/similar_names.rs:85:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:80:16 + --> $DIR/similar_names.rs:84:16 | LL | apple: spring, | ^^^^^^ -- cgit 1.4.1-3-g733a5 From f355aebf107bb4de078525bbafb16952cc9ecb34 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 19 May 2021 16:27:06 -0400 Subject: Move `needless_borrow` to style --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/needless_borrow.rs | 2 +- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 4 +- clippy_lints/src/unused_io_amount.rs | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 10 +- clippy_utils/src/consts.rs | 22 +-- clippy_utils/src/higher.rs | 36 ++--- clippy_utils/src/hir_utils.rs | 163 ++++++++++----------- clippy_utils/src/lib.rs | 64 ++++---- clippy_utils/src/numeric_literal.rs | 2 +- clippy_utils/src/ptr.rs | 4 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/ty.rs | 17 +-- clippy_utils/src/usage.rs | 4 +- 15 files changed, 164 insertions(+), 173 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a263e8b0ca4..877d9054d7b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1361,6 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_borrow::NEEDLESS_BORROW), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), @@ -1544,6 +1545,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc_early::REDUNDANT_PATTERN), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(needless_borrow::NEEDLESS_BORROW), LintId::of(neg_multiply::NEG_MULTIPLY), LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), @@ -1783,7 +1785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), - 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(regex::TRIVIAL_REGEX), diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 965b131770e..bcedebfccc7 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// let x: &i32 = &5; /// ``` pub NEEDLESS_BORROW, - nursery, + style, "taking a reference that is going to be automatically dereferenced" } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index b0674f90678..12c44436874 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -211,7 +211,7 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ]; if_chain! { - if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Call(fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::>(); diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index a45bb102389..1eaad438237 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -70,7 +70,7 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { for item in items { track_uses( cx, - &item, + item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros, @@ -117,7 +117,7 @@ fn track_uses( match &item.kind { ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - check_mod(cx, &items); + check_mod(cx, items); }, ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { macros.push(item.ident.name); diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index c27a6d4e347..ee082d30d93 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { let mut call = call; - while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind { + while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind { if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") { call = &args[0]; } else { diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index e9fa043b20f..a672af88271 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -379,7 +379,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// } /// ``` fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { - if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind { + if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { // Normal lint if_chain! { // item validation @@ -489,7 +489,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { .hir() .attrs(item.hir_id()) .iter() - .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string())) + .filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string())) .reduce(|mut acc, sym| { acc.push_str(&sym); acc.push('\n'); @@ -596,7 +596,7 @@ fn extract_emission_info<'hir>( let mut multi_part = false; for arg in args { - let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg)); + let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg)); if match_type(cx, arg_ty, &paths::LINT) { // If we found the lint arg, extract the lint name @@ -671,7 +671,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { if let ExprKind::Path(qpath) = &expr.kind; if let QPath::Resolved(_, path) = qpath; - let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr)); + let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); if match_type(self.cx, expr_ty, &paths::LINT); then { if let hir::def::Res::Def(DefKind::Static, _) = path.res { @@ -730,7 +730,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr)); + let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); if_chain! { if match_type(self.cx, expr_ty, &paths::APPLICABILITY); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 2a305d8bcbe..0d7fdeeb920 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -229,25 +229,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { pub fn expr(&mut self, e: &Expr<'_>) -> Option { match e.kind { ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), - ExprKind::Block(ref block, _) => self.block(block), + ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))), - ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), - ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), - ExprKind::Repeat(ref value, _) => { + ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), + ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), + ExprKind::Repeat(value, _) => { let n = match self.typeck_results.expr_ty(e).kind() { ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, _ => span_bug!(e.span, "typeck error"), }; self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) }, - ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { + ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op { UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), - ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), - ExprKind::Call(ref callee, ref args) => { + ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), + ExprKind::Binary(op, left, right) => self.binop(op, left, right), + ExprKind::Call(callee, args) => { // We only handle a few const functions for now. if_chain! { if args.is_empty(); @@ -273,8 +273,8 @@ 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))), + ExprKind::Index(arr, index) => self.index(arr, index), + ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), // TODO: add other expressions. _ => None, } @@ -349,7 +349,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ) .ok() .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?; - let result = miri_to_const(&result); + let result = miri_to_const(result); if result.is_some() { self.needed_resolution = true; } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 25b4491616c..8be36756b33 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -58,7 +58,7 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { } match expr.kind { - hir::ExprKind::Call(ref path, ref args) + hir::ExprKind::Call(path, args) if matches!( path.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) @@ -70,7 +70,7 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(path, ref fields, None) => match path { + hir::ExprKind::Struct(path, fields, None) => match path { hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { start: None, end: None, @@ -112,7 +112,7 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { // } // ``` if_chain! { - if let Some(ref expr) = local.init; + if let Some(expr) = local.init; if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; then { return true; @@ -140,14 +140,14 @@ pub fn for_loop<'tcx>( expr: &'tcx hir::Expr<'tcx>, ) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> { if_chain! { - if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; - if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; + if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind; + if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind; if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(); - if let hir::ExprKind::Loop(ref block, ..) = arms[0].body.kind; + if let hir::ExprKind::Loop(block, ..) = arms[0].body.kind; if block.expr.is_none(); if let [ _, _, ref let_stmt, ref body ] = *block.stmts; - if let hir::StmtKind::Local(ref local) = let_stmt.kind; - if let hir::StmtKind::Expr(ref expr) = body.kind; + if let hir::StmtKind::Local(local) = let_stmt.kind; + if let hir::StmtKind::Expr(expr) = body.kind; then { return Some((&*local.pat, &iterargs[0], expr, arms[0].span)); } @@ -182,7 +182,7 @@ pub enum VecArgs<'a> { /// from `vec!`. pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option> { if_chain! { - if let hir::ExprKind::Call(ref fun, ref args) = expr.kind; + if let hir::ExprKind::Call(fun, args) = expr.kind; if let hir::ExprKind::Path(ref qpath) = fun.kind; if is_expn_of(fun.span, "vec").is_some(); if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -194,8 +194,8 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option(e: &'tcx Expr<'tcx>) -> Option) -> Option>> { if_chain! { - if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind; + if let ExprKind::Match(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; @@ -238,12 +238,12 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option(e: &'tcx Expr<'tcx>) -> Option(e: &'tcx Expr<'tcx>) -> Option { impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { - (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { + (&StmtKind::Local(l), &StmtKind::Local(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. if let Some(typeck) = self.inner.maybe_typeck_results { - let l_ty = typeck.pat_ty(&l.pat); - let r_ty = typeck.pat_ty(&r.pat); + let l_ty = typeck.pat_ty(l.pat); + let r_ty = typeck.pat_ty(r.pat); if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) { return false; } @@ -110,11 +110,9 @@ impl HirEqInterExpr<'_, '_, '_> { // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) - && self.eq_pat(&l.pat, &r.pat) - }, - (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => { - self.eq_expr(l, r) + && self.eq_pat(l.pat, r.pat) }, + (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), _ => false, } } @@ -165,7 +163,7 @@ impl HirEqInterExpr<'_, '_, '_> { left.eq(right) }, _ => { - over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) + over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r)) && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) }, } @@ -192,20 +190,20 @@ impl HirEqInterExpr<'_, '_, '_> { &reduce_exprkind(self.inner.cx, &left.kind), &reduce_exprkind(self.inner.cx, &right.kind), ) { - (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { + (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { + (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => { self.inner.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)) => { + (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => { self.inner.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)) => { + (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r), + (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => { l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| { l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) @@ -215,40 +213,37 @@ impl HirEqInterExpr<'_, '_, '_> { both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) && both(le, re, |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), + (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { self.inner.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)) => { + (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => { self.eq_expr(lx, rx) && self.eq_ty(lt, rt) }, - (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref r_f_exp, ref r_f_ident)) => { + (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => { l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) }, - (&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => { - self.eq_expr(la, ra) && self.eq_expr(li, ri) - }, - (&ExprKind::If(ref lc, ref lt, ref le), &ExprKind::If(ref rc, ref rt, ref re)) => { + (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), + (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r)) }, (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, - (&ExprKind::Loop(ref lb, ref ll, ref lls, _), &ExprKind::Loop(ref rb, ref rl, ref rls, _)) => { + (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => { + (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => { ls == rs && self.eq_expr(le, re) && over(la, ra, |l, r| { - self.eq_pat(&l.pat, &r.pat) + self.eq_pat(l.pat, r.pat) && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r)) - && self.eq_expr(&l.body, &r.body) + && self.eq_expr(l.body, r.body) }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { self.inner.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)) => { + (&ExprKind::Repeat(le, ref ll_id), &ExprKind::Repeat(re, ref rl_id)) => { let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(ll_id.body)); let ll = celcx.expr(&self.inner.cx.tcx.hir().body(ll_id.body).value); let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(rl_id.body)); @@ -258,15 +253,15 @@ impl HirEqInterExpr<'_, '_, '_> { }, (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), - (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => { + (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) && over(lf, rf, |l, r| self.eq_expr_field(l, r)) }, (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), - (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re), + (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), - (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), + (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), _ => false, }; is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) @@ -277,7 +272,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool { - left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr) + left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr) } fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool { @@ -308,11 +303,11 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { - (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), - (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => { + (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r), + (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r)) }, - (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { + (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { @@ -323,15 +318,13 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), - (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), - (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref r, rs)) => { - ls == rs && over(l, r, |l, r| self.eq_pat(l, r)) - }, + (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), + (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri) }, - (&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re), - (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => { + (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), + (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => { over(ls, rs, |l, r| self.eq_pat(l, r)) && over(le, re, |l, r| self.eq_pat(l, r)) && both(li, ri, |l, r| self.eq_pat(l, r)) @@ -344,10 +337,10 @@ impl HirEqInterExpr<'_, '_, '_> { #[allow(clippy::similar_names)] fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { match (left, right) { - (&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => { + (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath) }, - (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => { + (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item, @@ -359,14 +352,14 @@ impl HirEqInterExpr<'_, '_, '_> { match (left.res, right.res) { (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r), (Res::Local(_), _) | (_, Res::Local(_)) => false, - _ => over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)), + _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)), } } fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { if !(left.parenthesized || right.parenthesized) { - over(&left.args, &right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work - && over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r)) + over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work + && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r)) } else if left.parenthesized && right.parenthesized { over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r)) && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| { @@ -390,8 +383,8 @@ impl HirEqInterExpr<'_, '_, '_> { #[allow(clippy::similar_names)] fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { - (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), - (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { + (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), + (&TyKind::Array(lt, ref ll_id), &TyKind::Array(rt, ref rl_id)) => { let cx = self.inner.cx; let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); @@ -404,14 +397,14 @@ impl HirEqInterExpr<'_, '_, '_> { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty) }, (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), - (&TyKind::Tup(ref l), &TyKind::Tup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)), + (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), (&TyKind::Infer, &TyKind::Infer) => true, _ => false, } } fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool { - left.ident.name == right.ident.name && self.eq_ty(&left.ty(), &right.ty()) + left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty()) } } @@ -540,7 +533,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_stmt(s); } - if let Some(ref e) = b.expr { + if let Some(e) = b.expr { self.hash_expr(e); } @@ -570,7 +563,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&e.kind).hash(&mut self.s); match e.kind { - ExprKind::AddrOf(kind, m, ref e) => { + ExprKind::AddrOf(kind, m, e) => { match kind { BorrowKind::Ref => 0, BorrowKind::Raw => 1, @@ -584,20 +577,20 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(i.ident.name); } }, - ExprKind::Assign(ref l, ref r, _) => { + ExprKind::Assign(l, r, _) => { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::AssignOp(ref o, ref l, ref r) => { + ExprKind::AssignOp(ref o, l, r) => { o.node .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); self.hash_expr(l); self.hash_expr(r); }, - ExprKind::Block(ref b, _) => { + ExprKind::Block(b, _) => { self.hash_block(b); }, - ExprKind::Binary(op, ref l, ref r) => { + ExprKind::Binary(op, l, r) => { op.node .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); self.hash_expr(l); @@ -607,18 +600,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(i) = i.label { self.hash_name(i.ident.name); } - if let Some(ref j) = *j { + if let Some(j) = *j { self.hash_expr(&*j); } }, - ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => { + ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { self.hash_expr(e); }, - ExprKind::Call(ref fun, args) => { + ExprKind::Call(fun, args) => { self.hash_expr(fun); self.hash_exprs(args); }, - ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => { + ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => { self.hash_expr(e); self.hash_ty(ty); }, @@ -631,15 +624,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // closures inherit TypeckResults self.hash_expr(&self.cx.tcx.hir().body(eid).value); }, - ExprKind::Field(ref e, ref f) => { + ExprKind::Field(e, ref f) => { self.hash_expr(e); self.hash_name(f.name); }, - ExprKind::Index(ref a, ref i) => { + ExprKind::Index(a, i) => { self.hash_expr(a); self.hash_expr(i); }, - ExprKind::InlineAsm(ref asm) => { + ExprKind::InlineAsm(asm) => { for piece in asm.template { match piece { InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s), @@ -694,22 +687,22 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Lit(ref l) => { l.node.hash(&mut self.s); }, - ExprKind::Loop(ref b, ref i, ..) => { + ExprKind::Loop(b, ref i, ..) => { self.hash_block(b); if let Some(i) = *i { self.hash_name(i.ident.name); } }, - ExprKind::If(ref cond, ref then, ref else_opt) => { + ExprKind::If(cond, then, ref else_opt) => { let c: fn(_, _, _) -> _ = ExprKind::If; c.hash(&mut self.s); self.hash_expr(cond); - self.hash_expr(&**then); - if let Some(ref e) = *else_opt { + self.hash_expr(then); + if let Some(e) = *else_opt { self.hash_expr(e); } }, - ExprKind::Match(ref e, arms, ref s) => { + ExprKind::Match(e, arms, ref s) => { self.hash_expr(e); for arm in arms { @@ -717,39 +710,39 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(ref e) = arm.guard { self.hash_guard(e); } - self.hash_expr(&arm.body); + self.hash_expr(arm.body); } s.hash(&mut self.s); }, - ExprKind::MethodCall(ref path, ref _tys, args, ref _fn_span) => { + ExprKind::MethodCall(path, ref _tys, args, ref _fn_span) => { self.hash_name(path.ident.name); self.hash_exprs(args); }, ExprKind::ConstBlock(ref l_id) => { self.hash_body(l_id.body); }, - ExprKind::Repeat(ref e, ref l_id) => { + ExprKind::Repeat(e, ref l_id) => { self.hash_expr(e); self.hash_body(l_id.body); }, ExprKind::Ret(ref e) => { - if let Some(ref e) = *e { + if let Some(e) = *e { self.hash_expr(e); } }, ExprKind::Path(ref qpath) => { self.hash_qpath(qpath); }, - ExprKind::Struct(ref path, fields, ref expr) => { + ExprKind::Struct(path, fields, ref expr) => { self.hash_qpath(path); for f in fields { self.hash_name(f.ident.name); - self.hash_expr(&f.expr); + self.hash_expr(f.expr); } - if let Some(ref e) = *expr { + if let Some(e) = *expr { self.hash_expr(e); } }, @@ -759,7 +752,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Array(v) => { self.hash_exprs(v); }, - ExprKind::Unary(lop, ref le) => { + ExprKind::Unary(lop, le) => { lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); self.hash_expr(le); }, @@ -778,10 +771,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_qpath(&mut self, p: &QPath<'_>) { match *p { - QPath::Resolved(_, ref path) => { + QPath::Resolved(_, path) => { self.hash_path(path); }, - QPath::TypeRelative(_, ref path) => { + QPath::TypeRelative(_, path) => { self.hash_name(path.ident.name); }, QPath::LangItem(lang_item, ..) => { @@ -875,7 +868,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { match &b.kind { StmtKind::Local(local) => { self.hash_pat(local.pat); - if let Some(ref init) = local.init { + if let Some(init) = local.init { self.hash_expr(init); } }, @@ -888,7 +881,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_guard(&mut self, g: &Guard<'_>) { match g { - Guard::If(ref expr) | Guard::IfLet(_, ref expr) => { + Guard::If(expr) | Guard::IfLet(_, expr) => { self.hash_expr(expr); }, } @@ -921,25 +914,25 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_body(anon_const.body); }, TyKind::Ptr(ref mut_ty) => { - self.hash_ty(&mut_ty.ty); + self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, TyKind::Rptr(lifetime, ref mut_ty) => { self.hash_lifetime(lifetime); - self.hash_ty(&mut_ty.ty); + self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, TyKind::BareFn(bfn) => { bfn.unsafety.hash(&mut self.s); bfn.abi.hash(&mut self.s); for arg in bfn.decl.inputs { - self.hash_ty(&arg); + self.hash_ty(arg); } match bfn.decl.output { FnRetTy::DefaultReturn(_) => { ().hash(&mut self.s); }, - FnRetTy::Return(ref ty) => { + FnRetTy::Return(ty) => { self.hash_ty(ty); }, } @@ -951,8 +944,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, TyKind::Path(ref qpath) => match qpath { - QPath::Resolved(ref maybe_ty, ref path) => { - if let Some(ref ty) = maybe_ty { + QPath::Resolved(ref maybe_ty, path) => { + if let Some(ty) = maybe_ty { self.hash_ty(ty); } for segment in path.segments { @@ -960,7 +953,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_generic_args(segment.args().args); } }, - QPath::TypeRelative(ref ty, ref segment) => { + QPath::TypeRelative(ty, segment) => { self.hash_ty(ty); segment.ident.name.hash(&mut self.s); }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2b9b214daa7..610011af059 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -328,7 +328,7 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) /// Checks if an expression references a variable of the given name. pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { - if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { if let [p] = path.segments { return p.ident.name == var; } @@ -338,8 +338,8 @@ pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { - QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"), - QPath::TypeRelative(_, ref seg) => seg, + QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), + QPath::TypeRelative(_, seg) => seg, QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"), } } @@ -367,8 +367,8 @@ pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { match *path { - QPath::Resolved(_, ref path) => path.segments.get(0), - QPath::TypeRelative(_, ref seg) => Some(seg), + QPath::Resolved(_, path) => path.segments.get(0), + QPath::TypeRelative(_, seg) => Some(seg), QPath::LangItem(..) => None, } } @@ -388,8 +388,8 @@ pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment /// ``` pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { match *path { - QPath::Resolved(_, ref path) => match_path(path, segments), - QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + QPath::Resolved(_, path) => match_path(path, segments), + QPath::TypeRelative(ty, segment) => match ty.kind { TyKind::Path(ref inner_path) => { if let [prefix @ .., end] = segments { if match_qpath(inner_path, prefix) { @@ -457,7 +457,7 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { - if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { if let Res::Local(id) = path.res { return Some(id); } @@ -661,12 +661,12 @@ pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option first - if let ExprKind::MethodCall(ref path, _, ref args, _) = current.kind { + if let ExprKind::MethodCall(path, _, args, _) = current.kind { if path.ident.name.as_str() == *method_name { if args.iter().any(|e| e.span.from_expansion()) { return None; } - matched.push(&**args); // build up `matched` backwards + matched.push(args); // build up `matched` backwards current = &args[0] // go to parent expression } else { return None; @@ -712,7 +712,7 @@ 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), - PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p), + PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p), _ => None, } } @@ -854,7 +854,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio kind: ImplItemKind::Fn(_, eid), .. }) => match cx.tcx.hir().body(eid).value.kind { - ExprKind::Block(ref block, _) => Some(block), + ExprKind::Block(block, _) => Some(block), _ => None, }, _ => None, @@ -1028,7 +1028,7 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> /// 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 { - if let ExprKind::Call(ref fun, _) = expr.kind { + if let ExprKind::Call(fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { @@ -1058,21 +1058,21 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { match pat.kind { 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::Box(pat) | PatKind::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::Or(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, _) => { + PatKind::Tuple(pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), + PatKind::Struct(ref qpath, fields, _) => { is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) }, - PatKind::TupleStruct(ref qpath, ref pats, _) => { + PatKind::TupleStruct(ref qpath, pats, _) => { is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat)) }, - PatKind::Slice(ref head, ref middle, ref tail) => { + PatKind::Slice(head, ref middle, tail) => { match &cx.typeck_results().node_type(pat.hir_id).kind() { rustc_ty::Slice(..) => { // [..] is the only irrefutable slice pattern. @@ -1111,7 +1111,7 @@ pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { /// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return /// themselves. pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { - while let ExprKind::Block(ref block, ..) = expr.kind { + while let ExprKind::Block(block, ..) = expr.kind { match (block.stmts.is_empty(), block.expr.as_ref()) { (true, Some(e)) => expr = e, _ => break, @@ -1130,7 +1130,7 @@ pub fn is_self(slf: &Param<'_>) -> bool { pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { if_chain! { - if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind; + if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind; if let Res::SelfTy(..) = path.res; then { return true @@ -1148,7 +1148,7 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if_chain! { - if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind; + if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind; if is_lang_ctor(cx, path, ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id); @@ -1167,7 +1167,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc } } - if let ExprKind::Match(_, ref arms, ref source) = expr.kind { + if let ExprKind::Match(_, arms, ref source) = expr.kind { // desugared from a `?` operator if let MatchSource::TryDesugar = *source { return Some(expr); @@ -1254,7 +1254,7 @@ pub fn match_function_call<'tcx>( path: &[&str], ) -> Option<&'tcx [Expr<'tcx>]> { if_chain! { - if let ExprKind::Call(ref fun, args) = expr.kind; + if let ExprKind::Call(fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); if match_def_path(cx, fun_def_id, path); @@ -1316,15 +1316,15 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, let mut conds = Vec::new(); let mut blocks: Vec<&Block<'_>> = Vec::new(); - while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind { - conds.push(&**cond); - if let ExprKind::Block(ref block, _) = then_expr.kind { + while let ExprKind::If(cond, then_expr, ref else_expr) = expr.kind { + conds.push(cond); + if let ExprKind::Block(block, _) = then_expr.kind { blocks.push(block); } else { panic!("ExprKind::If node is not an ExprKind::Block"); } - if let Some(ref else_expr) = *else_expr { + if let Some(else_expr) = *else_expr { expr = else_expr; } else { break; @@ -1333,8 +1333,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, // final `else {..}` if !blocks.is_empty() { - if let ExprKind::Block(ref block, _) = expr.kind { - blocks.push(&**block); + if let ExprKind::Block(block, _) = expr.kind { + blocks.push(block); } } @@ -1383,7 +1383,7 @@ pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { // 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! { + ExprKind::Call(path, _) => if_chain! { if let ExprKind::Path(ref qpath) = path.kind; if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id); then { @@ -1396,7 +1396,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) + did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some()) } /// Gets the node where an expression is either used, or it's type is unified with another branch. diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 268bc5b3205..546706d51d7 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -52,7 +52,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 (unsuffixed, suffix) = split_suffix(src, lit_kind); let float = matches!(lit_kind, LitKind::Float(..)); Some(NumericLiteral::new(unsuffixed, suffix, float)) } else { diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 5885cc83560..791688cd194 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -55,7 +55,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { if self.abort { return; } - if let ExprKind::MethodCall(ref seg, _, ref args, _) = expr.kind { + if let ExprKind::MethodCall(seg, _, args, _) = expr.kind { if args.len() == 1 && match_var(&args[0], self.name) { if seg.ident.name.as_str() == "capacity" { self.abort = true; @@ -79,5 +79,5 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { } fn get_binding_name(arg: &Param<'_>) -> Option { - get_pat_name(&arg.pat) + get_pat_name(arg.pat) } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index a08dcf19e5b..0e6ead675c2 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -255,7 +255,7 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t cursor = proj_base; match elem { ProjectionElem::Field(..) => { - let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty; + let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty; if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index c36e215f184..a92d3be5d3c 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -142,21 +142,18 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), - ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), - ty::Slice(ref ty) - | ty::Array(ref ty, _) - | ty::RawPtr(ty::TypeAndMut { ref ty, .. }) - | ty::Ref(_, ref ty, _) => { + ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did)).is_some(), + ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(), + ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays is_must_use_ty(cx, *ty) }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), + ty::Tuple(substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { - 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; } } @@ -166,7 +163,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Dynamic(binder, _) => { for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { + if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() { return true; } } @@ -305,7 +302,7 @@ pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bo /// Returns the base type for HIR references and pointers. pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty), + TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), _ => ty, } } diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 650b70c63af..31d5a888c69 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -71,12 +71,12 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::MutBorrow = bk { - self.update(&cmt) + self.update(cmt) } } fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - self.update(&cmt) + self.update(cmt) } fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} -- cgit 1.4.1-3-g733a5 From 7db0e4f791cf8baf3fe8e978a9056d0d8464a1bd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 21 May 2021 12:27:40 -0400 Subject: Suggest `&mut iter` inside a closure for `while_let_on_iterator` --- clippy_lints/src/loops/while_let_on_iterator.rs | 11 +++++++---- clippy_utils/src/lib.rs | 10 ++++++---- tests/ui/while_let_on_iterator.fixed | 14 ++++++++++++++ tests/ui/while_let_on_iterator.rs | 14 ++++++++++++++ tests/ui/while_let_on_iterator.stderr | 10 ++++++++-- 5 files changed, 49 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 63560047578..d57588716a5 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,7 +1,9 @@ use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; +use clippy_utils::{ + get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; @@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: } } - if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) { - // The iterator expression will be used on the next iteration unless it is declared within the outer - // loop. + if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) { + // The iterator expression will be used on the next iteration (for loops), or on the next call (for + // closures) unless it is declared within the enclosing expression. TODO: Check for closures + // used where an `FnOnce` type is expected. let local_id = match iter_expr.path { Res::Local(id) => id, _ => return true, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2b9b214daa7..d32c3ec929a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -861,14 +861,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } -/// Gets the loop enclosing the given expression, if any. -pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +/// Gets the loop or closure enclosing the given expression, if any. +pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { let map = tcx.hir(); for (_, node) in map.parent_iter(expr.hir_id) { match node { Node::Expr( - e @ Expr { - kind: ExprKind::Loop(..), + e + @ + Expr { + kind: ExprKind::Loop(..) | ExprKind::Closure(..), .. }, ) => return Some(e), diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index c3e2cf0c4a4..52e80ceee83 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -320,6 +320,20 @@ fn issue1924() { println!("iterator field {}", it.1); } +fn issue7249() { + let mut it = 0..10; + let mut x = || { + // Needs &mut, the closure can be called multiple times + for x in &mut it { + if x % 2 == 0 { + break; + } + } + }; + x(); + x(); +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 1717006a449..5078a3c9028 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -320,6 +320,20 @@ fn issue1924() { println!("iterator field {}", it.1); } +fn issue7249() { + let mut it = 0..10; + let mut x = || { + // Needs &mut, the closure can be called multiple times + while let Some(x) = it.next() { + if x % 2 == 0 { + break; + } + } + }; + x(); + x(); +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index eff559bef7e..cb0afeae15e 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -105,10 +105,16 @@ LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:325:5 + --> $DIR/while_let_on_iterator.rs:327:9 + | +LL | while let Some(x) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:339:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From 60dd2b65dcb10f10c02e90300941bc698fa4a39b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 21 May 2021 15:42:57 -0400 Subject: Fix `redundant_closure` for `vec![]` macro in a closure with arguments --- clippy_lints/src/eta_reduction.rs | 24 +++++++++++++----------- tests/ui/eta.fixed | 3 +++ tests/ui/eta.rs | 3 +++ tests/ui/eta.stderr | 16 ++++++++-------- 4 files changed, 27 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2f1aa53236d..afc5d546474 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -92,17 +92,19 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { let ex = &body.value; if ex.span.ctxt() != expr.span.ctxt() { - if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); + if decl.inputs.is_empty() { + if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } } // skip `foo(|| macro!())` return; diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 2be2283e3fd..9e752311c67 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -48,6 +48,9 @@ fn main() { // See #515 let a: Option)>> = Some(vec![1i32, 2]).map(|v| -> Box)> { Box::new(v) }); + + // issue #7224 + let _: Option> = Some(0).map(|_| vec![]); } trait TestTrait { diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index f0373f9ccf6..44be4628cbd 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -48,6 +48,9 @@ fn main() { // See #515 let a: Option)>> = Some(vec![1i32, 2]).map(|v| -> Box)> { Box::new(v) }); + + // issue #7224 + let _: Option> = Some(0).map(|_| vec![]); } trait TestTrait { diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 57ed6527966..8795d3b42c6 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -33,7 +33,7 @@ LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:89:51 + --> $DIR/eta.rs:92:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,43 +41,43 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:91:51 + --> $DIR/eta.rs:94:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:94:42 + --> $DIR/eta.rs:97:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:99:29 + --> $DIR/eta.rs:102:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:101:27 + --> $DIR/eta.rs:104:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:104:65 + --> $DIR/eta.rs:107:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:187:27 + --> $DIR/eta.rs:190:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:192:27 + --> $DIR/eta.rs:195:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` -- cgit 1.4.1-3-g733a5 From ae0d4da764fd751494fb270fd04c2c686eac1302 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sat, 22 May 2021 21:47:11 +0300 Subject: Fix invalid syntax in `from_iter_instead_of_collect` suggestion with "as Trait" --- .../src/methods/from_iter_instead_of_collect.rs | 47 ++++++++++++++-------- tests/ui/from_iter_instead_of_collect.fixed | 14 +++++++ tests/ui/from_iter_instead_of_collect.rs | 14 +++++++ tests/ui/from_iter_instead_of_collect.stderr | 40 ++++++++++-------- 4 files changed, 81 insertions(+), 34 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 28d0e8cd4ae..b4188d9ed30 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -37,6 +37,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { + fn strip_angle_brackets(s: &str) -> Option<&str> { + s.strip_prefix('<')?.strip_suffix('>') + } + let call_site = expr.span.source_callsite(); if_chain! { if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); @@ -44,23 +48,32 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) - if let Some((_, elements)) = snippet_split.split_last(); then { - // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) { - // remove the type specifier from the path elements - let without_ts = elements.iter().filter_map(|e| { - if e == type_specifier { None } else { Some((*e).to_string()) } - }).collect::>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{}", without_ts.join("::"), type_specifier) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) + if_chain! { + if let [type_specifier, _] = snippet_split.as_slice(); + if let Some(type_specifier) = strip_angle_brackets(type_specifier); + if let Some((type_specifier, ..)) = type_specifier.split_once(" as "); + then { + type_specifier.to_string() + } else { + // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) + if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { + // remove the type specifier from the path elements + let without_ts = elements.iter().filter_map(|e| { + if e == type_specifier { None } else { Some((*e).to_string()) } + }).collect::>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) + format!("{}{}", without_ts.join("::"), type_specifier) + } else { + // type is not explicitly specified so wildcards are needed + // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` + let ty_str = ty.to_string(); + let start = ty_str.find('<').unwrap_or(0); + let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); + let nb_wildcard = ty_str[start..end].split(',').count(); + let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); + format!("{}<{}>", elements.join("::"), wildcards) + } + } } } else { ty.to_string() diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index b5f548810e6..12db43b5343 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -6,6 +6,20 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; use std::iter::FromIterator; +struct Foo(Vec); + +impl FromIterator for Foo { + fn from_iter>(_: T) -> Self { + todo!() + } +} + +impl<'a> FromIterator<&'a bool> for Foo { + fn from_iter>(iter: T) -> Self { + iter.into_iter().copied().collect::() + } +} + fn main() { let iter_expr = std::iter::repeat(5).take(5); let _ = iter_expr.collect::>(); diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index b842b5451d1..f5ec190e0cd 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -6,6 +6,20 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; use std::iter::FromIterator; +struct Foo(Vec); + +impl FromIterator for Foo { + fn from_iter>(_: T) -> Self { + todo!() + } +} + +impl<'a> FromIterator<&'a bool> for Foo { + fn from_iter>(iter: T) -> Self { + >::from_iter(iter.into_iter().copied()) + } +} + fn main() { let iter_expr = std::iter::repeat(5).take(5); let _ = Vec::from_iter(iter_expr); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 434734c9a21..8f08ac8c3ff 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,88 +1,94 @@ error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:11:13 + --> $DIR/from_iter_instead_of_collect.rs:19:9 | -LL | let _ = Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` +LL | >::from_iter(iter.into_iter().copied()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:13:13 + --> $DIR/from_iter_instead_of_collect.rs:25:13 + | +LL | let _ = Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` + +error: usage of `FromIterator::from_iter` + --> $DIR/from_iter_instead_of_collect.rs:27:13 | LL | let _ = HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:18:19 + --> $DIR/from_iter_instead_of_collect.rs:32:19 | LL | assert_eq!(a, Vec::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:19:19 + --> $DIR/from_iter_instead_of_collect.rs:33:19 | LL | assert_eq!(a, Vec::::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:21:17 + --> $DIR/from_iter_instead_of_collect.rs:35:17 | LL | let mut b = VecDeque::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:24:17 + --> $DIR/from_iter_instead_of_collect.rs:38:17 | LL | let mut b = VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:29:21 + --> $DIR/from_iter_instead_of_collect.rs:43:21 | LL | let mut b = collections::VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:34:14 + --> $DIR/from_iter_instead_of_collect.rs:48:14 | LL | let bm = BTreeMap::from_iter(values.iter().cloned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:35:19 + --> $DIR/from_iter_instead_of_collect.rs:49:19 | LL | let mut bar = BTreeMap::from_iter(bm.range(0..2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:38:19 + --> $DIR/from_iter_instead_of_collect.rs:52:19 | LL | let mut bts = BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:42:17 + --> $DIR/from_iter_instead_of_collect.rs:56:17 | LL | let _ = collections::BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:43:17 + --> $DIR/from_iter_instead_of_collect.rs:57:17 | LL | let _ = collections::BTreeSet::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:46:15 + --> $DIR/from_iter_instead_of_collect.rs:60:15 | LL | for _i in Vec::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:47:15 + --> $DIR/from_iter_instead_of_collect.rs:61:15 | LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 2f78d57d8b4328867b33928f613e6d773f310869 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 24 May 2021 06:42:20 +0200 Subject: Downgrade suspicious_op..._groupings to Nursery This addresses #6722. --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 877d9054d7b..60029d17374 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1408,7 +1408,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(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap::ALMOST_SWAPPED), @@ -1562,7 +1561,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(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(try_err::TRY_ERR), @@ -1789,6 +1787,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), LintId::of(strings::STRING_LIT_AS_BYTES), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), ]); diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 4272935bc31..8056887d714 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_OPERATION_GROUPINGS, - style, + nursery, "groupings of binary operations that look suspiciously like typos" } -- cgit 1.4.1-3-g733a5 From 24743b39683ef1f69e3156c312ab12d9265ca7bb Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 12 May 2021 10:50:19 -0500 Subject: Use UnhashMap --- clippy_lints/src/trait_bounds.rs | 3 ++- clippy_utils/src/lib.rs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index b0589b0512e..74a94db1800 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -3,6 +3,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{in_macro, SpanlessHash}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; @@ -100,7 +101,7 @@ impl TraitBounds { hasher.hash_ty(ty); hasher.finish() }; - let mut map = FxHashMap::default(); + let mut map: UnhashMap>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { if_chain! { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 610011af059..18b7e6cd4bc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,7 +63,7 @@ use std::hash::BuildHasherDefault; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -1578,8 +1578,8 @@ where let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - let mut map: FxHashMap<_, Vec<&_>> = - FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + let mut map: UnhashMap> = + UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); for expr in exprs { match map.entry(hash(expr)) { -- cgit 1.4.1-3-g733a5 From 1ac7e19b4c2303e6d3168f7c1ba5b8e93c77c455 Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Mon, 24 May 2021 22:09:07 +0000 Subject: Move `semicolon_if_nothing_returned` to `pedantic` --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/semicolon_if_nothing_returned.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 60029d17374..cf54021202f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1038,7 +1038,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), - LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(shadow::SHADOW_REUSE), LintId::of(shadow::SHADOW_SAME), LintId::of(strings::STRING_ADD), @@ -1129,6 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(ranges::RANGE_PLUS_ONE), LintId::of(redundant_else::REDUNDANT_ELSE), LintId::of(ref_option_ref::REF_OPTION_REF), + LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(shadow::SHADOW_UNRELATED), LintId::of(strings::STRING_ADD_ASSIGN), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 553987a426b..16e4d73851f 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -8,11 +8,11 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Looks for blocks of expressions and fires if the last expression returns `()` - /// but is not followed by a semicolon. + /// **What it does:** Looks for blocks of expressions and fires if the last expression returns + /// `()` but is not followed by a semicolon. /// - /// **Why is this bad?** The semicolon might be optional but when - /// extending the block with new code, it doesn't require a change in previous last line. + /// **Why is this bad?** The semicolon might be optional but when extending the block with new + /// code, it doesn't require a change in previous last line. /// /// **Known problems:** None. /// @@ -30,7 +30,7 @@ declare_clippy_lint! { /// } /// ``` pub SEMICOLON_IF_NOTHING_RETURNED, - restriction, + pedantic, "add a semicolon if nothing is returned" } -- cgit 1.4.1-3-g733a5 From cadad20da1e970e62ea28f32a2f4a9177e8ca859 Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Tue, 25 May 2021 00:54:50 +0000 Subject: Add semicolons up to `needless_for_each.rs` --- clippy_lints/src/assertions_on_constants.rs | 2 +- clippy_lints/src/attrs.rs | 6 +++--- clippy_lints/src/bit_mask.rs | 6 +++--- clippy_lints/src/booleans.rs | 6 +++--- clippy_lints/src/collapsible_if.rs | 2 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/copies.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 4 ++-- clippy_lints/src/double_comparison.rs | 8 ++++---- clippy_lints/src/entry.rs | 2 +- clippy_lints/src/eq_op.rs | 12 ++++++------ clippy_lints/src/eta_reduction.rs | 7 ++++--- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/functions/too_many_lines.rs | 2 +- clippy_lints/src/future_not_send.rs | 2 +- clippy_lints/src/implicit_return.rs | 2 +- clippy_lints/src/infinite_iter.rs | 2 +- clippy_lints/src/invalid_upcast_comparisons.rs | 4 ++-- clippy_lints/src/len_zero.rs | 4 ++-- clippy_lints/src/let_underscore.rs | 8 ++++---- clippy_lints/src/lifetimes.rs | 4 ++-- clippy_lints/src/literal_representation.rs | 8 ++++---- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 8 ++++---- clippy_lints/src/loops/same_item_push.rs | 4 ++-- clippy_lints/src/loops/utils.rs | 8 ++++---- clippy_lints/src/macro_use.rs | 6 +++--- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/map_clone.rs | 6 +++--- clippy_lints/src/matches.rs | 10 +++++----- clippy_lints/src/methods/bind_instead_of_map.rs | 2 +- clippy_lints/src/methods/cloned_instead_of_copied.rs | 2 +- clippy_lints/src/methods/flat_map_option.rs | 2 +- clippy_lints/src/methods/mod.rs | 8 ++++---- clippy_lints/src/methods/unnecessary_fold.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 10 +++++----- clippy_lints/src/mut_reference.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/needless_arbitrary_self_type.rs | 4 ++-- clippy_lints/src/needless_bool.rs | 18 +++++++++--------- clippy_lints/src/needless_borrow.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 4 ++-- .../src/utils/internal_lints/metadata_collector.rs | 2 +- 46 files changed, 105 insertions(+), 104 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index c565e29d078..6c9ceaa45c2 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { &format!("`assert!(false, {})` should probably be replaced", panic_message), None, &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message), - ) + ); }; if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") { diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c5b01461c1c..932cd58bf62 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -273,7 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); if is_relevant_item(cx, item) { - check_attrs(cx, item.span, item.ident.name, attrs) + check_attrs(cx, item.span, item.ident.name, attrs); } match item.kind { ItemKind::ExternCrate(..) | ItemKind::Use(..) => { @@ -343,13 +343,13 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if is_relevant_impl(cx, item) { - check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())) + check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if is_relevant_trait(cx, item) { - check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())) + check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); } } } diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index f7daf3dab49..0ac6dc28e73 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -115,9 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { if let ExprKind::Binary(cmp, left, right) = &e.kind { if cmp.node.is_comparison() { if let Some(cmp_opt) = fetch_int_literal(cx, right) { - check_compare(cx, left, cmp.node, cmp_opt, e.span) + check_compare(cx, left, cmp.node, cmp_opt, e.span); } else if let Some(cmp_val) = fetch_int_literal(cx, left) { - check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span) + check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span); } } } @@ -171,7 +171,7 @@ fn check_compare(cx: &LateContext<'_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp } fetch_int_literal(cx, right) .or_else(|| fetch_int_literal(cx, left)) - .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span)) + .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span)); } } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 67f0e0c7870..e72399af232 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { _: Span, _: HirId, ) { - NonminimalBoolVisitor { cx }.visit_body(body) + NonminimalBoolVisitor { cx }.visit_body(body); } } @@ -184,7 +184,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { Term(n) => { let terminal = self.terminals[n as usize]; if let Some(str) = simplify_not(self.cx, terminal) { - self.output.push_str(&str) + self.output.push_str(&str); } else { self.output.push('!'); let snip = snippet_opt(self.cx, terminal.span)?; @@ -452,7 +452,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { } match &e.kind { ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => { - self.bool_expr(e) + self.bool_expr(e); }, ExprKind::Unary(UnOp::Not, inner) => { if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() { diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index dae5c86bd44..6e950738239 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -92,7 +92,7 @@ declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { if !expr.span.from_expansion() { - check_if(cx, expr) + check_if(cx, expr); } } } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 2a61d58e653..b6999bef6e7 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { "`if` chain can be rewritten with `match`", None, "consider rewriting the `if` chain to use `cmp` and `match`", - ) + ); } } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index f956d171bfb..376a14b8181 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -476,7 +476,7 @@ fn emit_branches_sharing_code_lint( } suggestions.push(("end", span, suggestion.to_string())); - add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit() + add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit(); } let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 6e883942680..759f7d4062d 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -181,9 +181,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { match stmt.kind { StmtKind::Local(local) => { if local.ty.is_some() { - self.ty_bounds.push(TyBound::Any) + self.ty_bounds.push(TyBound::Any); } else { - self.ty_bounds.push(TyBound::Nothing) + self.ty_bounds.push(TyBound::Nothing); } }, diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 58543ae6e4e..4966638cb1b 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -70,16 +70,16 @@ impl<'tcx> DoubleComparisons { #[rustfmt::skip] match (op, lkind, rkind) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { - lint_double_comparison!(<=) + lint_double_comparison!(<=); }, (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => { - lint_double_comparison!(>=) + lint_double_comparison!(>=); }, (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => { - lint_double_comparison!(!=) + lint_double_comparison!(!=); }, (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => { - lint_double_comparison!(==) + lint_double_comparison!(==); }, _ => (), }; diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 8db5050a5ac..2eb8b1422ed 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -469,7 +469,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { let mut is_map_used = self.is_map_used; for arm in arms { if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { - self.visit_non_tail_expr(guard) + self.visit_non_tail_expr(guard); } is_map_used |= self.visit_cond_arm(arm.body); } diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 90f391b5f5c..47fe4e0de0d 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -156,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { vec![(left.span, lsnip), (right.span, rsnip)], ); }, - ) + ); } else if lcpy && !rcpy && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()]) @@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { Applicability::MaybeIncorrect, // FIXME #2597 ); }, - ) + ); } else if !lcpy && rcpy && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { Applicability::MaybeIncorrect, // FIXME #2597 ); }, - ) + ); } }, // &foo == bar @@ -218,9 +218,9 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { Applicability::MaybeIncorrect, // FIXME #2597 ); }, - ) + ); } - }, + }, // foo == &bar (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let rty = cx.typeck_results().expr_ty(r); @@ -236,7 +236,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { rsnip, Applicability::MaybeIncorrect, // FIXME #2597 ); - }) + }); } }, _ => {}, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index afc5d546474..8d066f305ee 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { for arg in args { // skip `foo(macro!())` if arg.span.ctxt() == expr.span.ctxt() { - check_closure(cx, arg) + check_closure(cx, arg); } } }, @@ -190,9 +190,10 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a cx.tcx.impl_of_method(method_def_id).and_then(|_| { //a type may implicitly implement other type's methods (e.g. Deref) if match_types(expected_type_of_self, actual_type_of_self) { - return Some(get_type_name(cx, actual_type_of_self)); + Some(get_type_name(cx, actual_type_of_self)) + } else { + None } - None }) } diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 41acf55dd7d..5fdf5bc9e9d 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -110,7 +110,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { self.visit_expr(e); for arm in arms { if let Some(Guard::If(if_expr)) = arm.guard { - self.visit_expr(if_expr) + self.visit_expr(if_expr); } // make sure top level arm expressions aren't linted self.maybe_walk_expr(&*arm.body); diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 20288427b4a..7f4fb68cf2f 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -240,7 +240,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { } }, Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { - self.mutates_static |= is_mutated_static(target) + self.mutates_static |= is_mutated_static(target); }, _ => {}, } diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index aa5494d5a7d..c02c343dc58 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -63,6 +63,6 @@ pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<' "this function has too many lines ({}/{})", line_count, too_many_lines_threshold ), - ) + ); } } diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 04730ace887..515b8887453 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { )); } } - }) + }); }, ); } diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 260a8f50157..f2f830ca5c0 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -67,7 +67,7 @@ fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) { "change `break` to `return` as shown", format!("return {}", snip), app, - ) + ); } #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index afee20ce43e..6b887da2630 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for InfiniteIter { return; }, }; - span_lint(cx, lint, expr.span, msg) + span_lint(cx, lint, expr.span, msg); } } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index c67c02eefa5..aafdebef248 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -177,7 +177,7 @@ fn upcast_comparison_bounds_err<'tcx>( }, Rel::Eq | Rel::Ne => unreachable!(), } { - err_upcast_comparison(cx, span, lhs, true) + err_upcast_comparison(cx, span, lhs, true); } else if match rel { Rel::Lt => { if invert { @@ -195,7 +195,7 @@ fn upcast_comparison_bounds_err<'tcx>( }, Rel::Eq | Rel::Ne => unreachable!(), } { - err_upcast_comparison(cx, span, lhs, false) + err_upcast_comparison(cx, span, lhs, false); } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index bb57adff7be..583514b22f9 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -380,9 +380,9 @@ 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) + check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to); } else { - check_empty_expr(cx, span, method, lit, op) + check_empty_expr(cx, span, method, lit, op); } } diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 17e23781db7..e627b1385bc 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { None, "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" - ) + ); } else if init_ty.needs_drop(cx.tcx, cx.param_env) { span_lint_and_help( cx, @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { None, "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" - ) + ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( cx, @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding let on an expression with `#[must_use]` type", None, "consider explicitly using expression value" - ) + ); } else if is_must_use_func_call(cx, init) { span_lint_and_help( cx, @@ -163,7 +163,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding let on a result of a `#[must_use]` function", None, "consider explicitly using function result" - ) + ); } } } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 116ad072837..5ae68ba5b2f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -205,7 +205,7 @@ fn could_use_elision<'tcx>( output_visitor.visit_ty(ty); } for lt in named_generics { - input_visitor.visit_generic_param(lt) + input_visitor.visit_generic_param(lt); } if input_visitor.abort() || output_visitor.abort() { @@ -463,7 +463,7 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker { // `'b` in `'a: 'b` is useless unless used elsewhere in // a non-lifetime bound if let GenericParamKind::Type { .. } = param.kind { - walk_generic_param(self, param) + walk_generic_param(self, param); } } fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index e93b2e36b86..e0c5578bd60 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -231,7 +231,7 @@ impl EarlyLintPass for LiteralDigitGrouping { } if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit) + self.check_lit(cx, lit); } } } @@ -294,7 +294,7 @@ impl LiteralDigitGrouping { } }; if should_warn { - warning_type.display(num_lit.format(), cx, lit.span) + warning_type.display(num_lit.format(), cx, lit.span); } } } @@ -424,7 +424,7 @@ impl EarlyLintPass for DecimalLiteralRepresentation { } if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit) + self.check_lit(cx, lit); } } } @@ -446,7 +446,7 @@ impl DecimalLiteralRepresentation { let hex = format!("{:#X}", val); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, lit.span) + warning_type.display(num_lit.format(), cx, lit.span); }); } } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index ce02ad013be..f0327b5d777 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -43,7 +43,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, m "to write this more concisely, try", format!("&{}{}", muta, object), applicability, - ) + ); } /// Returns `true` if the type of expr is one that provides `IntoIterator` impls diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 1425d50f560..d07b5a93b67 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -88,10 +88,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { if let ty::BorrowKind::MutBorrow = bk { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)) + self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); } if Some(id) == self.hir_id_high { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)) + self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } } @@ -100,10 +100,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)) + self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); } if Some(id) == self.hir_id_high { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)) + self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index cb2c83e9029..f2e0a0f7003 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -32,10 +32,10 @@ pub(super) fn check<'tcx>( "it looks like the same item is being pushed into this Vec", None, &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + "try using vec![{}; SIZE] or {}.resize(NEW_SIZE, {})", item_str, vec_str, item_str ), - ) + ); } if !matches!(pat.kind, PatKind::Wild) { diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 4db6644b9d7..2f7360210ba 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -80,10 +80,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } }, ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { - *state = IncrementVisitorVarState::DontWarn + *state = IncrementVisitorVarState::DontWarn; }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - *state = IncrementVisitorVarState::DontWarn + *state = IncrementVisitorVarState::DontWarn; }, _ => (), } @@ -207,7 +207,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - self.state = InitializeVisitorState::DontWarn + self.state = InitializeVisitorState::DontWarn; }, _ => (), } @@ -292,7 +292,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { return; } } - walk_pat(self, pat) + walk_pat(self, pat); } fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 914b583186c..66479ae264e 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -212,9 +212,9 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let mut suggestions = vec![]; for ((root, span), path) in used { if path.len() == 1 { - suggestions.push((span, format!("{}::{}", root, path[0]))) + suggestions.push((span, format!("{}::{}", root, path[0]))); } else { - suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")))) + suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")))); } } @@ -231,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { "remove the attribute and import the macro directly, try", help, Applicability::MaybeIncorrect, - ) + ); } } } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 23428524dee..6a06090f6e2 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { kind_word, snippet(cx, pattern.span, "..")))] .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), - ) + ); }); } } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 99c35ae3bbf..e1f80ab025c 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -125,7 +125,7 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { "remove the `map` call", String::new(), Applicability::MachineApplicable, - ) + ); } fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { @@ -142,7 +142,7 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { snippet_with_applicability(cx, root, "..", &mut applicability) ), applicability, - ) + ); } else { span_lint_and_sugg( cx, @@ -155,6 +155,6 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { snippet_with_applicability(cx, root, "..", &mut applicability) ), applicability, - ) + ); } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index fcd37687010..e07d8ea56e4 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1144,7 +1144,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) "try this", suggestions.join(" | "), Applicability::MaybeIncorrect, - ) + ); }, }; } @@ -1242,7 +1242,7 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp cast, ), applicability, - ) + ); } } } @@ -1494,7 +1494,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A "consider using the scrutinee and body instead", sugg, applicability, - ) + ); } else { span_lint_and_sugg( cx, @@ -1747,7 +1747,7 @@ mod redundant_pattern_match { match match_source { MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), MatchSource::IfLetDesugar { contains_else_clause } => { - find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause) + find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause); }, MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false), _ => {}, @@ -1876,7 +1876,7 @@ mod redundant_pattern_match { { self.res = true; } else { - self.visit_expr(self_arg) + self.visit_expr(self_arg); } } args.iter().for_each(|arg| self.visit_expr(arg)); diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 287bff886bf..da428a7b487 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -135,7 +135,7 @@ pub(crate) trait BindInsteadOfMap { .into_iter() .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), ), - ) + ); }); true } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index ecec6da3aa0..f5b4b6bf8ea 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -41,5 +41,5 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, "try", "copied".into(), Applicability::MachineApplicable, - ) + ); } diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 12d560653ed..32d40d97bf4 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -30,5 +30,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg "try", "filter_map".into(), Applicability::MachineApplicable, - ) + ); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e0d29682146..cabfe8023ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1951,7 +1951,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => { - zst_offset::check(cx, expr, recv) + zst_offset::check(cx, expr, recv); }, ("and_then", [arg]) => { let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); @@ -2012,7 +2012,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), ("filter", [f_arg]) => { - filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false) + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), _ => {}, @@ -2058,7 +2058,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, Some(("map", [m_recv, m_arg], span)) => { - option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span) + option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, _ => {}, }, @@ -2073,7 +2073,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { - search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span) + search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 75517c48a21..4c4034437da 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -87,7 +87,7 @@ pub(super) fn check( ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true), ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false), ast::LitKind::Int(1, _) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false) + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false); }, _ => (), } diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index dd38316fa25..050b6805b7c 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -310,7 +310,7 @@ impl EarlyLintPass for MiscEarlyLints { if in_external_macro(cx.sess(), expr.span) { return; } - double_neg::check(cx, expr) + double_neg::check(cx, expr); } } @@ -334,15 +334,15 @@ impl MiscEarlyLints { }; unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip) + mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip); } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { - /* nothing to do */ + // nothing to do } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, lit, &lit_snip) + zero_prefixed_literal::check(cx, lit, &lit_snip); } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { let suffix = float_ty.name_str(); - unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float") + unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float"); } } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index cea6fce1195..6efe8ffcde0 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -48,7 +48,7 @@ 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(), "method") + check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method"); }, _ => (), } diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 533c5a22db0..81bf853300f 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { _ if !self.found => self.expr_span = Some(expr.span), _ => return, } - walk_expr(self, expr) + walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 3e2b2782ed5..fe3c4455be5 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -121,7 +121,7 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { 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) + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl); } }, TyKind::Rptr(lifetime, mut_ty) => { @@ -129,7 +129,7 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { 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) + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); } } }, diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index dd458198637..3b3736fd3a1 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } if is_else_clause(cx.tcx, e) { - snip = snip.blockify() + snip = snip.blockify(); } span_lint_and_sugg( @@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison { |h: Sugg<'_>| !h, "equality checks against false can be replaced by a negation", )); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal) + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); }, BinOpKind::Ne => { let true_case = Some(( @@ -152,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison { "inequality checks against true can be replaced by a negation", )); let false_case = Some((|h| h, "inequality checks against false are unnecessary")); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal) + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); }, BinOpKind::Lt => check_comparison( cx, @@ -251,22 +251,22 @@ fn check_comparison<'a, 'tcx>( snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability) ), applicability, - ) + ); } } match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { (Bool(true), Other) => left_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, e, right_side, applicability, m, h) + suggest_bool_comparison(cx, e, right_side, applicability, m, h); }), (Other, Bool(true)) => right_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, e, left_side, applicability, m, h) + suggest_bool_comparison(cx, e, left_side, applicability, m, h); }), (Bool(false), Other) => left_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, e, right_side, applicability, m, h) + suggest_bool_comparison(cx, e, right_side, applicability, m, h); }), (Other, Bool(false)) => right_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, e, left_side, applicability, m, h) + suggest_bool_comparison(cx, e, left_side, applicability, m, h); }), (Other, Other) => no_literal.map_or((), |(h, m)| { let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability); @@ -279,7 +279,7 @@ fn check_comparison<'a, 'tcx>( "try simplifying it as shown", h(left_side, right_side).to_string(), applicability, - ) + ); }), _ => (), } diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index bcedebfccc7..dd1dfa2bdfb 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { |diag| { diag.multipart_suggestion("try this", replacements, app); }, - ) + ); } self.current_body = None; } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 079b6642d58..3b936e6aaef 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -145,7 +145,7 @@ impl<'tcx> Visitor<'tcx> for RetCollector { self.ret_in_loop = true } - self.spans.push(expr.span) + self.spans.push(expr.span); }, ExprKind::Loop(..) => { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index e70f8a09ebe..39ef170ae36 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -292,7 +292,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { LitKind::Str(ref text, _) => { let str_pat = self.next("s"); println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat); - println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str()) + println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str()); }, } }, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index ee7be24eae8..2c73c3d929b 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1100,7 +1100,7 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { IF_CHAIN_STYLE, if_chain_local_span(cx, local, if_chain_span), "`let` expression should be inside `then { .. }`", - ) + ); } } @@ -1141,7 +1141,7 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`") + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); } } } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a672af88271..46af03663b8 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -818,7 +818,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some()); if found_function { // These functions are all multi part suggestions - self.add_single_span_suggestion() + self.add_single_span_suggestion(); } }, ExprKind::MethodCall(path, _path_span, arg, _arg_span) => { -- cgit 1.4.1-3-g733a5 From 527fb42a324296343f94548a116c51646de16065 Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Tue, 25 May 2021 01:46:33 +0000 Subject: Add all the semicolons to `clippy_lints` --- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 2 +- clippy_lints/src/open_options.rs | 10 +++++----- clippy_lints/src/question_mark.rs | 4 ++-- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/redundant_pub_crate.rs | 2 +- clippy_lints/src/returns.rs | 4 ++-- clippy_lints/src/shadow.rs | 10 +++++----- clippy_lints/src/slow_vector_initialization.rs | 4 ++-- clippy_lints/src/suspicious_operation_groupings.rs | 6 +++--- clippy_lints/src/types/mod.rs | 4 ++-- clippy_lints/src/unicode.rs | 4 ++-- clippy_lints/src/upper_case_acronyms.rs | 2 +- clippy_lints/src/use_self.rs | 4 ++-- clippy_lints/src/verbose_file_reads.rs | 2 +- clippy_lints/src/write.rs | 2 +- 15 files changed, 31 insertions(+), 31 deletions(-) (limited to 'clippy_lints/src') 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 0704173a011..c824f6f54b5 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { 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/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 9efe45336bf..fded48038e3 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -123,7 +123,7 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "the method `create` is called more than once", ); } else { - create = true + create = true; } create_arg = create_arg || (arg == Argument::True); }, @@ -136,7 +136,7 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "the method `append` is called more than once", ); } else { - append = true + append = true; } append_arg = append_arg || (arg == Argument::True); }, @@ -149,7 +149,7 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "the method `truncate` is called more than once", ); } else { - truncate = true + truncate = true; } truncate_arg = truncate_arg || (arg == Argument::True); }, @@ -162,7 +162,7 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "the method `read` is called more than once", ); } else { - read = true + read = true; } read_arg = read_arg || (arg == Argument::True); }, @@ -175,7 +175,7 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "the method `write` is called more than once", ); } else { - write = true + write = true; } write_arg = write_arg || (arg == Argument::True); }, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 30bee213900..d66bac52243 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -88,7 +88,7 @@ impl QuestionMark { "replace it with", replacement_str, applicability, - ) + ); } } } @@ -129,7 +129,7 @@ impl QuestionMark { "replace it with", replacement, applicability, - ) + ); } } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 92921bedf4d..8f56a21ac5b 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -57,7 +57,7 @@ impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { self.found_return = true; } - ast_visit::walk_expr(self, ex) + ast_visit::walk_expr(self, ex); } } diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index e091095de13..05f9e01acb4 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { Applicability::MachineApplicable, ); }, - ) + ); } } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index b565c77aaec..251d527c265 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { } else { RetReplacement::Empty }; - check_final_expr(cx, &body.value, Some(body.value.span), replacement) + check_final_expr(cx, &body.value, Some(body.value.span), replacement); }, FnKind::ItemFn(..) | FnKind::Method(..) => { if let ExprKind::Block(block, _) = body.value.kind { @@ -241,7 +241,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option match replacement { RetReplacement::Empty => { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index d6101bd5e36..ac3f7ebd14b 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -120,7 +120,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo let mut bindings = Vec::with_capacity(decl.inputs.len()); for arg in iter_input_pats(decl, body) { if let PatKind::Binding(.., ident, _) = arg.pat.kind { - bindings.push((ident.name, ident.span)) + bindings.push((ident.name, ident.span)); } } check_expr(cx, &body.value, &mut bindings); @@ -156,7 +156,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & .. } = *local; if let Some(t) = *ty { - check_ty(cx, t, bindings) + check_ty(cx, t, bindings); } if let Some(o) = *init { check_expr(cx, o, bindings); @@ -324,14 +324,14 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut } match expr.kind { ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => { - check_expr(cx, e, bindings) + check_expr(cx, e, bindings); }, ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings), // ExprKind::Call // ExprKind::MethodCall ExprKind::Array(v) | ExprKind::Tup(v) => { for e in v { - check_expr(cx, e, bindings) + check_expr(cx, e, bindings); } }, ExprKind::If(cond, then, ref otherwise) => { @@ -374,7 +374,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings), TyKind::Tup(tup) => { for t in tup { - check_ty(cx, t, bindings) + check_ty(cx, t, bindings); } }, TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings), diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index a9ae2b77119..e5c58d70b60 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -158,7 +158,7 @@ impl SlowVectorInit { ) { match initialization { InitializationType::Extend(e) | InitializationType::Resize(e) => { - Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization") + Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization"); }, }; } @@ -290,7 +290,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_block(&mut self, block: &'tcx Block<'_>) { if self.initialization_found { if let Some(s) = block.stmts.get(0) { - self.visit_stmt(s) + self.visit_stmt(s); } self.initialization_found = false; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 8056887d714..bb707f78fcc 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -266,7 +266,7 @@ fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicabilit "did you mean", sugg, applicability, - ) + ); } fn ident_swap_sugg( @@ -475,7 +475,7 @@ impl Add for IdentLocation { impl AddAssign for IdentLocation { fn add_assign(&mut self, other: Self) { - *self = *self + other + *self = *self + other; } } @@ -506,7 +506,7 @@ impl Add for IdentDifference { impl AddAssign for IdentDifference { fn add_assign(&mut self, other: Self) { - *self = *self + other + *self = *self + other; } } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index d9b47a699dc..70b9e8adef8 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -306,7 +306,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { match item.kind { TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { - self.check_ty(cx, ty, CheckTyContext::default()) + self.check_ty(cx, ty, CheckTyContext::default()); }, TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()), TraitItemKind::Type(..) => (), @@ -433,7 +433,7 @@ impl Types { }, TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => { context.is_nested_call = true; - self.check_ty(cx, ty, context) + self.check_ty(cx, ty, context); }, TyKind::Tup(tys) => { context.is_nested_call = true; diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d81e31f5a21..45291a120ed 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -71,7 +71,7 @@ impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Lit(ref lit) = expr.kind { if let LitKind::Str(_, _) = lit.node { - check_str(cx, lit.span, expr.hir_id) + check_str(cx, lit.span, expr.hir_id); } } } @@ -82,7 +82,7 @@ fn escape>(s: T) -> String { for c in s { if c as u32 > 0x7F { for d in c.escape_unicode() { - result.push(d) + result.push(d); } } else { result.push(c); diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 4ac2ec55b98..debbd86a59e 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -92,7 +92,7 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { "consider making the acronym lowercase, except the initial letter", corrected, Applicability::MaybeIncorrect, - ) + ); } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 2ad6fa77f48..254b104bdef 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -356,7 +356,7 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector { fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) { self.types_to_skip.push(hir_ty.hir_id); - walk_ty(self, hir_ty) + walk_ty(self, hir_ty); } fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -385,7 +385,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> { } } - walk_ty(self, hir_ty) + walk_ty(self, hir_ty); } fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs index ec209b30951..3ab68df2b6d 100644 --- a/clippy_lints/src/verbose_file_reads.rs +++ b/clippy_lints/src/verbose_file_reads.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for VerboseFileReads { "use of `File::read_to_string`", None, "consider using `fs::read_to_string` instead", - ) + ); } } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index d0e79efa70d..5229a705865 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -300,7 +300,7 @@ impl EarlyLintPass for Write { Applicability::MachineApplicable, ); }, - ) + ); } } } else if mac.path == sym!(writeln) { -- cgit 1.4.1-3-g733a5 From 6d737772246d9912ba7bbf9b906f467da02ef70e Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Tue, 25 May 2021 02:03:31 +0000 Subject: Fix `same_item_push.rs` --- clippy_lints/src/loops/same_item_push.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index f2e0a0f7003..0f6cd5de761 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( "it looks like the same item is being pushed into this Vec", None, &format!( - "try using vec![{}; SIZE] or {}.resize(NEW_SIZE, {})", + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", item_str, vec_str, item_str ), ); -- cgit 1.4.1-3-g733a5 From 9cad27fce878977e40dd5b8042165b9a45e86c83 Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Tue, 25 May 2021 02:06:45 +0000 Subject: Add semicolon in `needless_for_each.rs` --- clippy_lints/src/needless_for_each.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3b936e6aaef..a723a472a25 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -142,7 +142,7 @@ impl<'tcx> Visitor<'tcx> for RetCollector { match expr.kind { ExprKind::Ret(..) => { if self.loop_depth > 0 && !self.ret_in_loop { - self.ret_in_loop = true + self.ret_in_loop = true; } self.spans.push(expr.span); -- cgit 1.4.1-3-g733a5 From bcebea65c1b4640113358dd2150ada3bf6bdd850 Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Tue, 25 May 2021 06:05:52 +0000 Subject: Run `cargo fmt` --- clippy_lints/src/eq_op.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 47fe4e0de0d..a3a8e748d99 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { }, ); } - }, + }, // foo == &bar (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let rty = cx.typeck_results().expr_ty(r); -- cgit 1.4.1-3-g733a5 From 5cc6635fcfe9606729dc8a3cd26bf8012d3cdd55 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 08:40:43 -0500 Subject: Add macro_use clippy_utils --- clippy_lints/src/lib.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 60029d17374..90b260e5d5b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -41,6 +41,9 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate rustc_typeck; +#[macro_use] +extern crate clippy_utils; + use clippy_utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; @@ -145,25 +148,10 @@ macro_rules! declare_clippy_lint { }; } -#[macro_export] -macro_rules! sym { - ( $($x:tt)* ) => { clippy_utils::sym!($($x)*) } -} - -#[macro_export] -macro_rules! unwrap_cargo_metadata { - ( $($x:tt)* ) => { clippy_utils::unwrap_cargo_metadata!($($x)*) } -} - -macro_rules! extract_msrv_attr { - ( $($x:tt)* ) => { clippy_utils::extract_msrv_attr!($($x)*); } -} - mod consts; -#[macro_use] -mod utils; #[cfg(feature = "metadata-collector-lint")] mod deprecated_lints; +mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; -- cgit 1.4.1-3-g733a5 From c21b965d43d5584c3e0df219e6638494d2dc3ea9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 16:13:57 -0500 Subject: Fix missing_docs_in_private_items FP --- clippy_lints/src/missing_doc.rs | 6 +++--- tests/ui/missing-doc-impl.rs | 5 ++++- tests/ui/missing-doc-impl.stderr | 8 +++++--- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index dfab3e8a931..ec1572c26c2 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -93,9 +93,9 @@ impl MissingDoc { return; } - let has_doc = attrs.iter().any(|a| { - a.is_doc_comment() || a.doc_str().is_some() || a.value_str().is_some() || Self::has_include(a.meta()) - }); + let has_doc = attrs + .iter() + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); if !has_doc { span_lint( cx, diff --git a/tests/ui/missing-doc-impl.rs b/tests/ui/missing-doc-impl.rs index 57af84dcdf4..bfa9ef01b0e 100644 --- a/tests/ui/missing-doc-impl.rs +++ b/tests/ui/missing-doc-impl.rs @@ -67,7 +67,10 @@ impl PubFoo { pub fn foo() {} /// dox pub fn foo1() {} - fn foo2() {} + #[must_use = "yep"] + fn foo2() -> u32 { + 1 + } #[allow(clippy::missing_docs_in_private_items)] pub fn foo3() {} } diff --git a/tests/ui/missing-doc-impl.stderr b/tests/ui/missing-doc-impl.stderr index 7e10404ca00..d33d512475b 100644 --- a/tests/ui/missing-doc-impl.stderr +++ b/tests/ui/missing-doc-impl.stderr @@ -94,10 +94,12 @@ LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/missing-doc-impl.rs:70:5 + --> $DIR/missing-doc-impl.rs:71:5 | -LL | fn foo2() {} - | ^^^^^^^^^^^^ +LL | / fn foo2() -> u32 { +LL | | 1 +LL | | } + | |_____^ error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 2e2021bbda7ce7b7095b79f07fe6a408a6cd8f07 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 12:41:58 -0500 Subject: Add avoid_breaking_exported_api config option --- README.md | 1 + clippy_lints/src/utils/conf.rs | 2 ++ tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/README.md b/README.md index c6b67bb6221..6c556f579ca 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml value` mapping eg. ```toml +avoid-breaking-exported-api = false blacklisted-names = ["toto", "tata", "titi"] cognitive-complexity-threshold = 30 ``` diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index fd2dddb3b96..d65270a9450 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -122,6 +122,8 @@ macro_rules! define_Conf { // N.B., this macro is parsed by util/lintlib.py define_Conf! { + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates. + (avoid_breaking_exported_api: bool = true), /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports (msrv: Option = None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses 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 d83080b69f5..a7be00426c4 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From d7f47f280ec267c0583f7d38fc149a2351b923e6 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 16:39:39 -0500 Subject: Use break api config for wrong_pub_self_convention --- clippy_lints/src/deprecated_lints.rs | 9 +++++ clippy_lints/src/lib.rs | 9 +++-- clippy_lints/src/methods/mod.rs | 40 +++++-------------- clippy_lints/src/methods/wrong_self_convention.rs | 13 ++---- tests/ui/def_id_nocore.rs | 2 +- tests/ui/module_name_repetitions.rs | 8 ---- tests/ui/wrong_self_convention.rs | 1 - tests/ui/wrong_self_convention.stderr | 48 +++++++++++------------ tests/ui/wrong_self_convention2.rs | 1 - tests/ui/wrong_self_convention2.stderr | 4 +- 10 files changed, 55 insertions(+), 80 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 50ffc2e3f19..dd780ff87fe 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -141,3 +141,12 @@ declare_deprecated_lint! { pub FILTER_MAP, "this lint has been replaced by `manual_filter_map`, a more specific lint" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which + /// enables the `wrong_self_conversion` lint for public items. + pub WRONG_PUB_SELF_CONVENTION, + "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cf54021202f..6dd2486afae 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -505,6 +505,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::filter_map", "this lint has been replaced by `manual_filter_map`, a more specific lint", ); + store.register_removed( + "clippy::wrong_pub_self_convention", + "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items", + ); // 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` @@ -802,7 +806,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::UNNECESSARY_LAZY_EVALUATIONS, methods::UNWRAP_USED, methods::USELESS_ASREF, - methods::WRONG_PUB_SELF_CONVENTION, methods::WRONG_SELF_CONVENTION, methods::ZST_OFFSET, minmax::MIN_MAX, @@ -1026,7 +1029,6 @@ 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::UNWRAP_USED), - LintId::of(methods::WRONG_PUB_SELF_CONVENTION), LintId::of(misc::FLOAT_CMP_CONST), LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), @@ -1862,7 +1864,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }) }); - store.register_late_pass(move || box methods::Methods::new(msrv)); + let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + store.register_late_pass(move || box methods::Methods::new(avoid_breaking_exported_api, msrv)); store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cabfe8023ef..0b998dbf86c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -282,30 +282,6 @@ declare_clippy_lint! { "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" } -declare_clippy_lint! { - /// **What it does:** This is the same as - /// [`wrong_self_convention`](#wrong_self_convention), but for public items. - /// - /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention). - /// - /// **Known problems:** Actually *renaming* the function may break clients if - /// the function is part of the public interface. In that case, be mindful of - /// the stability guarantees you've given your users. - /// - /// **Example:** - /// ```rust - /// # struct X; - /// impl<'a> X { - /// pub fn as_str(self) -> &'a str { - /// "foo" - /// } - /// } - /// ``` - pub WRONG_PUB_SELF_CONVENTION, - restriction, - "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" -} - declare_clippy_lint! { /// **What it does:** Checks for usage of `ok().expect(..)`. /// @@ -1658,13 +1634,17 @@ declare_clippy_lint! { } pub struct Methods { + avoid_breaking_exported_api: bool, msrv: Option, } impl Methods { #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } + pub fn new(avoid_breaking_exported_api: bool, msrv: Option) -> Self { + Self { + avoid_breaking_exported_api, + msrv, + } } } @@ -1673,7 +1653,6 @@ impl_lint_pass!(Methods => [ EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, - WRONG_PUB_SELF_CONVENTION, OK_EXPECT, MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, @@ -1838,11 +1817,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - if sig.decl.implicit_self.has_implicit_self() { + if sig.decl.implicit_self.has_implicit_self() + && !(self.avoid_breaking_exported_api + && cx.access_levels.is_exported(impl_item.hir_id())) + { wrong_self_convention::check( cx, &name, - item.vis.node.is_pub(), self_ty, first_arg_ty, first_arg.pat.span, @@ -1915,7 +1896,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { wrong_self_convention::check( cx, &item.ident.name.as_str(), - false, self_ty, first_arg_ty, first_arg_span, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 1773c26c251..a2e09e5ecec 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -6,7 +6,6 @@ use rustc_middle::ty::TyS; use rustc_span::source_map::Span; use std::fmt; -use super::WRONG_PUB_SELF_CONVENTION; use super::WRONG_SELF_CONVENTION; #[rustfmt::skip] @@ -21,9 +20,9 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; @@ -85,18 +84,12 @@ impl fmt::Display for Convention { pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: &str, - is_pub: bool, self_ty: &'tcx TyS<'tcx>, first_arg_ty: &'tcx TyS<'tcx>, first_arg_span: Span, implements_trait: bool, is_trait_item: bool, ) { - let lint = if is_pub { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() @@ -142,7 +135,7 @@ pub(super) fn check<'tcx>( span_lint_and_help( cx, - lint, + WRONG_SELF_CONVENTION, first_arg_span, &format!( "{} usually take {}", diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index 2a948d60b10..cba7666c2d8 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -20,7 +20,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } -pub struct A; +struct A; impl A { pub fn as_ref(self) -> &'static str { diff --git a/tests/ui/module_name_repetitions.rs b/tests/ui/module_name_repetitions.rs index 669bf01a84c..f5908cb5701 100644 --- a/tests/ui/module_name_repetitions.rs +++ b/tests/ui/module_name_repetitions.rs @@ -15,12 +15,4 @@ mod foo { pub struct Foobar; } -#[cfg(test)] -mod test { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} - fn main() {} diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index cdfbdb8b0db..151dd0c27d5 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -1,6 +1,5 @@ // edition:2018 #![warn(clippy::wrong_self_convention)] -#![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] fn main() {} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 29f5ba82695..ce23317abf6 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` - --> $DIR/wrong_self_convention.rs:18:17 + --> $DIR/wrong_self_convention.rs:17:17 | LL | fn from_i32(self) {} | ^^^^ @@ -8,7 +8,7 @@ LL | fn from_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:24:21 + --> $DIR/wrong_self_convention.rs:23:21 | LL | pub fn from_i64(self) {} | ^^^^ @@ -16,7 +16,7 @@ LL | pub fn from_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:36:15 + --> $DIR/wrong_self_convention.rs:35:15 | LL | fn as_i32(self) {} | ^^^^ @@ -24,7 +24,7 @@ LL | fn as_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:38:17 + --> $DIR/wrong_self_convention.rs:37:17 | LL | fn into_i32(&self) {} | ^^^^^ @@ -32,7 +32,7 @@ LL | fn into_i32(&self) {} = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:40:15 + --> $DIR/wrong_self_convention.rs:39:15 | LL | fn is_i32(self) {} | ^^^^ @@ -40,7 +40,7 @@ LL | fn is_i32(self) {} = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:42:15 + --> $DIR/wrong_self_convention.rs:41:15 | LL | fn to_i32(self) {} | ^^^^ @@ -48,7 +48,7 @@ LL | fn to_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:44:17 + --> $DIR/wrong_self_convention.rs:43:17 | LL | fn from_i32(self) {} | ^^^^ @@ -56,7 +56,7 @@ LL | fn from_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:46:19 + --> $DIR/wrong_self_convention.rs:45:19 | LL | pub fn as_i64(self) {} | ^^^^ @@ -64,7 +64,7 @@ LL | pub fn as_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:47:21 + --> $DIR/wrong_self_convention.rs:46:21 | LL | pub fn into_i64(&self) {} | ^^^^^ @@ -72,7 +72,7 @@ LL | pub fn into_i64(&self) {} = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:48:19 + --> $DIR/wrong_self_convention.rs:47:19 | LL | pub fn is_i64(self) {} | ^^^^ @@ -80,7 +80,7 @@ LL | pub fn is_i64(self) {} = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:49:19 + --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn to_i64(self) {} | ^^^^ @@ -88,7 +88,7 @@ LL | pub fn to_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:50:21 + --> $DIR/wrong_self_convention.rs:49:21 | LL | pub fn from_i64(self) {} | ^^^^ @@ -96,7 +96,7 @@ LL | pub fn from_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:95:19 + --> $DIR/wrong_self_convention.rs:94:19 | LL | fn as_i32(self) {} | ^^^^ @@ -104,7 +104,7 @@ LL | fn as_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:98:25 + --> $DIR/wrong_self_convention.rs:97:25 | LL | fn into_i32_ref(&self) {} | ^^^^^ @@ -112,7 +112,7 @@ LL | fn into_i32_ref(&self) {} = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:100:19 + --> $DIR/wrong_self_convention.rs:99:19 | LL | fn is_i32(self) {} | ^^^^ @@ -120,7 +120,7 @@ LL | fn is_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:104:21 + --> $DIR/wrong_self_convention.rs:103:21 | LL | fn from_i32(self) {} | ^^^^ @@ -128,7 +128,7 @@ LL | fn from_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:119:19 + --> $DIR/wrong_self_convention.rs:118:19 | LL | fn as_i32(self); | ^^^^ @@ -136,7 +136,7 @@ LL | fn as_i32(self); = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:122:25 + --> $DIR/wrong_self_convention.rs:121:25 | LL | fn into_i32_ref(&self); | ^^^^^ @@ -144,7 +144,7 @@ LL | fn into_i32_ref(&self); = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:124:19 + --> $DIR/wrong_self_convention.rs:123:19 | LL | fn is_i32(self); | ^^^^ @@ -152,7 +152,7 @@ LL | fn is_i32(self); = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:128:21 + --> $DIR/wrong_self_convention.rs:127:21 | LL | fn from_i32(self); | ^^^^ @@ -160,7 +160,7 @@ LL | fn from_i32(self); = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:146:25 + --> $DIR/wrong_self_convention.rs:145:25 | LL | fn into_i32_ref(&self); | ^^^^^ @@ -168,7 +168,7 @@ LL | fn into_i32_ref(&self); = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:152:21 + --> $DIR/wrong_self_convention.rs:151:21 | LL | fn from_i32(self); | ^^^^ @@ -176,7 +176,7 @@ LL | fn from_i32(self); = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value - --> $DIR/wrong_self_convention.rs:176:22 + --> $DIR/wrong_self_convention.rs:175:22 | LL | fn to_u64_v2(&self) -> u64 { | ^^^^^ @@ -184,7 +184,7 @@ LL | fn to_u64_v2(&self) -> u64 { = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:185:19 + --> $DIR/wrong_self_convention.rs:184:19 | LL | fn to_u64(self) -> u64 { | ^^^^ diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 3a72174d03d..501bc1e6a85 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -1,6 +1,5 @@ // edition:2018 #![warn(clippy::wrong_self_convention)] -#![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] fn main() {} diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr index d2d74ce099e..0e0d066d656 100644 --- a/tests/ui/wrong_self_convention2.stderr +++ b/tests/ui/wrong_self_convention2.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention2.rs:56:29 + --> $DIR/wrong_self_convention2.rs:55:29 | LL | pub fn from_be_self(self) -> Self { | ^^^^ @@ -8,7 +8,7 @@ LL | pub fn from_be_self(self) -> Self { = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention2.rs:65:25 + --> $DIR/wrong_self_convention2.rs:64:25 | LL | fn from_be_self(self) -> Self; | ^^^^ -- cgit 1.4.1-3-g733a5 From ee79077d8008006f25fb591946bdc17e99c0b91a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 12:45:21 -0500 Subject: Use break api config for pass by value or ref --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/pass_by_ref_or_value.rs | 13 +++++++++++-- tests/ui/trivially_copy_pass_by_ref.stderr | 8 +------- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6dd2486afae..30db45a34f9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1947,6 +1947,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( conf.trivial_copy_size_limit, conf.pass_by_value_size_limit, + conf.avoid_breaking_exported_api, &sess.target, ); store.register_late_pass(move || box pass_by_ref_or_value); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6b64846c24d..f6a70478559 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -102,10 +102,16 @@ declare_clippy_lint! { pub struct PassByRefOrValue { ref_min_size: u64, value_max_size: u64, + avoid_breaking_exported_api: bool, } impl<'tcx> PassByRefOrValue { - pub fn new(ref_min_size: Option, value_max_size: u64, target: &Target) -> Self { + pub fn new( + ref_min_size: Option, + value_max_size: u64, + avoid_breaking_exported_api: bool, + 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 @@ -120,10 +126,14 @@ impl<'tcx> PassByRefOrValue { Self { ref_min_size, value_max_size, + avoid_breaking_exported_api, } } fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { + if self.avoid_breaking_exported_api && cx.access_levels.is_exported(hir_id) { + return; + } let fn_def_id = cx.tcx.hir().local_def_id(hir_id); let fn_sig = cx.tcx.fn_sig(fn_def_id); @@ -184,7 +194,6 @@ impl<'tcx> PassByRefOrValue { } 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()); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index ccc3cdb2b74..2b0005bbff1 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -88,12 +88,6 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` -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:80:37 - | -LL | fn trait_method2(&self, _color: &Color); - | ^^^^^^ help: consider passing by value instead: `Color` - 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 | @@ -106,5 +100,5 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 3d77a2b8617f914b1e5503dd93f67a8334693ce0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 14:02:24 -0500 Subject: Use break api config for enum_variant_names --- clippy_lints/src/deprecated_lints.rs | 10 +++++ clippy_lints/src/enum_variants.rs | 80 +++++++++++++----------------------- clippy_lints/src/lib.rs | 8 ++-- tests/ui/enum_variants.rs | 7 ++-- tests/ui/enum_variants.stderr | 37 ++++++----------- 5 files changed, 59 insertions(+), 83 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index dd780ff87fe..04f3d77464f 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -142,6 +142,16 @@ declare_deprecated_lint! { "this lint has been replaced by `manual_filter_map`, a more specific lint" } +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which + /// enables the `enum_variant_names` lint for public items. + /// ``` + pub PUB_ENUM_VARIANT_NAMES, + "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items" +} + declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 0ecc0bc3eb6..b1a105a51c1 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -3,8 +3,8 @@ use clippy_utils::camel_case; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::is_present_in_source; -use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_hir::{EnumDef, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -39,36 +39,6 @@ declare_clippy_lint! { "enums where all variants share a prefix/postfix" } -declare_clippy_lint! { - /// **What it does:** Detects public enumeration variants that are - /// prefixed or suffixed by the same characters. - /// - /// **Why is this bad?** Public enumeration variant names should specify their variant, - /// not repeat the enumeration name. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// pub enum Cake { - /// BlackForestCake, - /// HummingbirdCake, - /// BattenbergCake, - /// } - /// ``` - /// Could be written as: - /// ```rust - /// pub enum Cake { - /// BlackForest, - /// Hummingbird, - /// Battenberg, - /// } - /// ``` - pub PUB_ENUM_VARIANT_NAMES, - pedantic, - "public enums where all variants share a prefix/postfix" -} - declare_clippy_lint! { /// **What it does:** Detects type names that are prefixed or suffixed by the /// containing module's name. @@ -127,21 +97,22 @@ declare_clippy_lint! { pub struct EnumVariantNames { modules: Vec<(Symbol, String)>, threshold: u64, + avoid_breaking_exported_api: bool, } impl EnumVariantNames { #[must_use] - pub fn new(threshold: u64) -> Self { + pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self { Self { modules: Vec::new(), threshold, + avoid_breaking_exported_api, } } } impl_lint_pass!(EnumVariantNames => [ ENUM_VARIANT_NAMES, - PUB_ENUM_VARIANT_NAMES, MODULE_NAME_REPETITIONS, MODULE_INCEPTION ]); @@ -167,33 +138,42 @@ fn partial_rmatch(post: &str, name: &str) -> usize { } fn check_variant( - cx: &EarlyContext<'_>, + cx: &LateContext<'_>, threshold: u64, - def: &EnumDef, + def: &EnumDef<'_>, item_name: &str, item_name_chars: usize, span: Span, - lint: &'static Lint, ) { if (def.variants.len() as u64) < threshold { return; } - for var in &def.variants { + for var in def.variants { let name = var.ident.name.as_str(); if partial_match(item_name, &name) == item_name_chars && 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, + ENUM_VARIANT_NAMES, + 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, + ENUM_VARIANT_NAMES, + var.span, + "variant name ends with the enum's name", + ); } } let first = &def.variants[0].ident.name.as_str(); let mut pre = &first[..camel_case::until(&*first)]; let mut post = &first[camel_case::from(&*first)..]; - for var in &def.variants { + for var in def.variants { let name = var.ident.name.as_str(); let pre_match = partial_match(pre, &name); @@ -226,7 +206,7 @@ fn check_variant( }; span_lint_and_help( cx, - lint, + ENUM_VARIANT_NAMES, span, &format!("all variants have the same {}fix: `{}`", what, value), None, @@ -261,14 +241,14 @@ fn to_camel_case(item_name: &str) -> String { s } -impl EarlyLintPass for EnumVariantNames { - fn check_item_post(&mut self, _cx: &EarlyContext<'_>, _item: &Item) { +impl LateLintPass<'_> for EnumVariantNames { + fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) { let last = self.modules.pop(); assert!(last.is_some()); } #[allow(clippy::similar_names)] - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { let item_name = item.ident.name.as_str(); let item_name_chars = item_name.chars().count(); let item_camel = to_camel_case(&item_name); @@ -286,7 +266,7 @@ impl EarlyLintPass for EnumVariantNames { ); } } - if item.vis.kind.is_pub() { + if item.vis.node.is_pub() { let matching = partial_match(mod_camel, &item_camel); let rmatching = partial_rmatch(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); @@ -317,11 +297,9 @@ impl EarlyLintPass for EnumVariantNames { } } if let ItemKind::Enum(ref def, _) = item.kind { - let lint = match item.vis.kind { - VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES, - _ => ENUM_VARIANT_NAMES, - }; - check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint); + if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.hir_id())) { + check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span); + } } self.modules.push((item.ident.name, item_camel)); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 30db45a34f9..bab389d2c05 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -505,6 +505,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::filter_map", "this lint has been replaced by `manual_filter_map`, a more specific lint", ); + store.register_removed( + "clippy::pub_enum_variant_names", + "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items", + ); store.register_removed( "clippy::wrong_pub_self_convention", "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items", @@ -622,7 +626,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: enum_variants::ENUM_VARIANT_NAMES, enum_variants::MODULE_INCEPTION, enum_variants::MODULE_NAME_REPETITIONS, - enum_variants::PUB_ENUM_VARIANT_NAMES, eq_op::EQ_OP, eq_op::OP_REF, erasing_op::ERASING_OP, @@ -1080,7 +1083,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(doc::MISSING_PANICS_DOC), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(enum_variants::PUB_ENUM_VARIANT_NAMES), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), @@ -2015,7 +2017,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); let enum_variant_name_threshold = conf.enum_variant_name_threshold; - store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); + store.register_late_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive)); diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 4fefc0b43f1..083f5143e6e 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -1,5 +1,4 @@ -#![feature(non_ascii_idents)] -#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] +#![warn(clippy::enum_variant_names)] #![allow(non_camel_case_types, clippy::upper_case_acronyms)] enum FakeCallType { @@ -97,8 +96,8 @@ pub enum PubSeall { WithOut, } -#[allow(clippy::pub_enum_variant_names)] -mod allowed { +#[allow(clippy::enum_variant_names)] +pub mod allowed { pub enum PubAllowed { SomeThis, SomeThat, diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index ab7fff4507a..447fbb9e1bf 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -1,5 +1,5 @@ error: variant name ends with the enum's name - --> $DIR/enum_variants.rs:16:5 + --> $DIR/enum_variants.rs:15:5 | LL | cFoo, | ^^^^ @@ -7,25 +7,25 @@ LL | cFoo, = note: `-D clippy::enum-variant-names` implied by `-D warnings` error: variant name starts with the enum's name - --> $DIR/enum_variants.rs:27:5 + --> $DIR/enum_variants.rs:26:5 | LL | FoodGood, | ^^^^^^^^ error: variant name starts with the enum's name - --> $DIR/enum_variants.rs:28:5 + --> $DIR/enum_variants.rs:27:5 | LL | FoodMiddle, | ^^^^^^^^^^ error: variant name starts with the enum's name - --> $DIR/enum_variants.rs:29:5 + --> $DIR/enum_variants.rs:28:5 | LL | FoodBad, | ^^^^^^^ error: all variants have the same prefix: `Food` - --> $DIR/enum_variants.rs:26:1 + --> $DIR/enum_variants.rs:25:1 | LL | / enum Food { LL | | FoodGood, @@ -37,7 +37,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` - --> $DIR/enum_variants.rs:36:1 + --> $DIR/enum_variants.rs:35:1 | LL | / enum BadCallType { LL | | CallTypeCall, @@ -49,7 +49,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` - --> $DIR/enum_variants.rs:48:1 + --> $DIR/enum_variants.rs:47:1 | LL | / enum Consts { LL | | ConstantInt, @@ -61,7 +61,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` - --> $DIR/enum_variants.rs:82:1 + --> $DIR/enum_variants.rs:81:1 | LL | / enum Seallll { LL | | WithOutCake, @@ -73,7 +73,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` - --> $DIR/enum_variants.rs:88:1 + --> $DIR/enum_variants.rs:87:1 | LL | / enum NonCaps { LL | | Prefix的, @@ -84,21 +84,8 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: all variants have the same prefix: `With` - --> $DIR/enum_variants.rs:94:1 - | -LL | / pub enum PubSeall { -LL | | WithOutCake, -LL | | WithOutTea, -LL | | WithOut, -LL | | } - | |_^ - | - = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` - = help: remove the prefixes and use full paths to the variants instead of glob imports - error: all variants have the same postfix: `IData` - --> $DIR/enum_variants.rs:137:1 + --> $DIR/enum_variants.rs:136:1 | LL | / enum IDataRequest { LL | | PutIData(String), @@ -110,7 +97,7 @@ LL | | } = help: remove the postfixes and use full paths to the variants instead of glob imports error: all variants have the same postfix: `HIData` - --> $DIR/enum_variants.rs:143:1 + --> $DIR/enum_variants.rs:142:1 | LL | / enum HIDataRequest { LL | | PutHIData(String), @@ -121,5 +108,5 @@ LL | | } | = help: remove the postfixes and use full paths to the variants instead of glob imports -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 1ce581d70698466879dc7acec0629032fb9006fd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 14:33:23 -0500 Subject: Use break api config for unnecessary_wraps --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 21 ++++++++++++++++----- tests/ui/unnecessary_wraps.rs | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bab389d2c05..9c9eba0d21e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1976,7 +1976,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); + store.register_late_pass(move || box unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index f2f1410aed7..a85ffa6aa95 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -8,7 +8,7 @@ use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -52,7 +52,19 @@ declare_clippy_lint! { "functions that only return `Ok` or `Some`" } -declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); +pub struct UnnecessaryWraps { + avoid_breaking_exported_api: bool, +} + +impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); + +impl UnnecessaryWraps { + pub fn new(avoid_breaking_exported_api: bool) -> Self { + Self { + avoid_breaking_exported_api, + } + } +} impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn check_fn( @@ -66,13 +78,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) { // Abort if public function/method or closure. match fn_kind { - FnKind::ItemFn(.., visibility) | FnKind::Method(.., Some(visibility)) => { - if visibility.node.is_pub() { + FnKind::ItemFn(..) | FnKind::Method(..) => { + if self.avoid_breaking_exported_api && cx.access_levels.is_exported(hir_id) { return; } }, FnKind::Closure => return, - FnKind::Method(..) => (), } // Abort if the method is implementing a trait or of it a trait method. diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index 54f22e3ee6a..63648ef5826 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -65,7 +65,7 @@ fn func10() -> Option<()> { unimplemented!() } -struct A; +pub struct A; impl A { // should not be linted -- cgit 1.4.1-3-g733a5 From 55ccc7a8c6c353c3350332114c76b79f3108a9c2 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 14:42:55 -0500 Subject: Use break api config for upper_case_acronyms --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/upper_case_acronyms.rs | 32 +++++++++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9c9eba0d21e..5139b1464c4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -2020,7 +2020,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; - store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive)); + store.register_late_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)); store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index debbd86a59e..0b58c6c0917 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use itertools::Itertools; -use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Ident; @@ -38,12 +38,14 @@ declare_clippy_lint! { #[derive(Default)] pub struct UpperCaseAcronyms { + avoid_breaking_exported_api: bool, upper_case_acronyms_aggressive: bool, } impl UpperCaseAcronyms { - pub fn new(aggressive: bool) -> Self { + pub fn new(avoid_breaking_exported_api: bool, aggressive: bool) -> Self { Self { + avoid_breaking_exported_api, upper_case_acronyms_aggressive: aggressive, } } @@ -72,7 +74,7 @@ fn correct_ident(ident: &str) -> String { ident } -fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { +fn check_ident(cx: &LateContext<'_>, ident: &Ident, be_aggressive: bool) { let span = ident.span; let ident = &ident.as_str(); let corrected = correct_ident(ident); @@ -96,23 +98,27 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { } } -impl EarlyLintPass for UpperCaseAcronyms { - fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) { +impl LateLintPass<'_> for UpperCaseAcronyms { + fn check_item(&mut self, cx: &LateContext<'_>, it: &Item<'_>) { // do not lint public items or in macros - if !in_external_macro(cx.sess(), it.span) && !matches!(it.vis.kind, VisibilityKind::Public) { - if matches!( - it.kind, - ItemKind::TyAlias(..) | ItemKind::Struct(..) | ItemKind::Trait(..) - ) { + if in_external_macro(cx.sess(), it.span) + || (self.avoid_breaking_exported_api && cx.access_levels.is_exported(it.hir_id())) + { + return; + } + match it.kind { + ItemKind::TyAlias(..) | ItemKind::Struct(..) | ItemKind::Trait(..) => { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); - } else if let ItemKind::Enum(ref enumdef, _) = it.kind { + }, + ItemKind::Enum(ref enumdef, _) => { // check enum variants seperately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants enumdef .variants .iter() .for_each(|variant| check_ident(cx, &variant.ident, self.upper_case_acronyms_aggressive)); - } + }, + _ => {}, } } } -- cgit 1.4.1-3-g733a5 From 6eea598645be5489f518f91e4b80a0f04a315fda Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 7 May 2021 12:07:59 -0500 Subject: Fix config file lookup --- clippy_lints/src/lib.rs | 11 ----------- clippy_lints/src/utils/conf.rs | 16 +++++++--------- 2 files changed, 7 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5139b1464c4..b08a57a09b3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -405,7 +405,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { #[doc(hidden)] pub fn read_conf(sess: &Session) -> Conf { - use std::path::Path; let file_name = match utils::conf::lookup_conf_file() { Ok(Some(path)) => path, Ok(None) => return Conf::default(), @@ -416,16 +415,6 @@ pub fn read_conf(sess: &Session) -> Conf { }, }; - let file_name = if file_name.is_relative() { - sess.local_crate_source_file - .as_deref() - .and_then(Path::parent) - .unwrap_or_else(|| Path::new("")) - .join(file_name) - } else { - file_name - }; - let TryConf { conf, errors } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index d65270a9450..1bd38dc042c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -210,15 +210,13 @@ pub fn lookup_conf_file() -> io::Result> { .map_or_else(|| PathBuf::from("."), PathBuf::from); loop { for config_file_name in &CONFIG_FILE_NAMES { - let config_file = current.join(config_file_name); - match fs::metadata(&config_file) { - // Only return if it's a file to handle the unlikely situation of a directory named - // `clippy.toml`. - Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)), - // Return the error if it's something other than `NotFound`; otherwise we didn't - // find the project file yet, and continue searching. - Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e), - _ => {}, + if let Ok(config_file) = current.join(config_file_name).canonicalize() { + match fs::metadata(&config_file) { + Err(e) if e.kind() == io::ErrorKind::NotFound => {}, + Err(e) => return Err(e), + Ok(md) if md.is_dir() => {}, + Ok(_) => return Ok(Some(config_file)), + } } } -- cgit 1.4.1-3-g733a5 From f3e77a454aef617998a016ca70c8dc0d281f208a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 22:07:53 -0500 Subject: Fix allow on some statement lints --- clippy_lints/src/loops/needless_collect.rs | 5 +++-- clippy_lints/src/misc.rs | 4 +++- clippy_lints/src/no_effect.rs | 13 +++++++------ clippy_utils/src/diagnostics.rs | 2 +- tests/ui/needless_collect_indirect.rs | 8 +++++++- tests/ui/no_effect.rs | 3 +++ 6 files changed, 24 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index d3406780888..eb82c9c27c3 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,5 +1,5 @@ use super::NEEDLESS_COLLECT; -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; @@ -116,9 +116,10 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(collect_span); span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); - span_lint_and_then( + span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, + init_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b5d2549242b..5976b060ad2 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -355,8 +355,10 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; if let Some(sugg) = Sugg::hir_opt(cx, a); then { - span_lint_and_then(cx, + span_lint_hir_and_then( + cx, SHORT_CIRCUIT_STATEMENT, + expr.hir_id, stmt.span, "boolean short circuit operator in statement may be clearer using an explicit test", |diag| { diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index cfcaf509471..b2206a82208 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { - span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect"); + span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { let mut snippet = String::new(); for e in reduced { @@ -106,14 +106,15 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { return; } } - span_lint_and_sugg( + span_lint_hir_and_then( cx, UNNECESSARY_OPERATION, + expr.hir_id, stmt.span, "statement can be reduced", - "replace it with", - snippet, - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable); + }, ); } } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index a1f5f5f3338..7c94474cb35 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -167,7 +167,7 @@ pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, - sp: Span, + sp: impl Into, msg: &str, f: impl FnOnce(&mut DiagnosticBuilder<'_>), ) { diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 2458bf1e490..2c94235b8f5 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque}; +use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -75,3 +75,9 @@ mod issue7110 { buffer.len() } } + +fn allow_test() { + #[allow(clippy::needless_collect)] + let v = [1].iter().collect::>(); + v.into_iter().collect::>(); +} diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 8fbfcb79860..7ec845adfaa 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -91,6 +91,9 @@ fn main() { let s: String = "foo".into(); FooString { s: s }; + #[allow(clippy::no_effect)] + 0; + // Do not warn get_number(); unsafe { unsafe_fn() }; -- cgit 1.4.1-3-g733a5 From d39a11cbe151d9578432e0c7c64364b7eaffe89c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 08:49:37 -0500 Subject: Remove clippy_utils::consts re-export --- clippy_lints/src/absurd_extreme_comparisons.rs | 3 +-- clippy_lints/src/arithmetic.rs | 2 +- clippy_lints/src/assertions_on_constants.rs | 2 +- clippy_lints/src/bit_mask.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/consts.rs | 1 - clippy_lints/src/duration_subsec.rs | 2 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/erasing_op.rs | 3 +-- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/identity_op.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/invalid_upcast_comparisons.rs | 3 +-- clippy_lints/src/lib.rs | 1 - clippy_lints/src/loops/while_immutable_condition.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/manual_unwrap_or.rs | 2 +- clippy_lints/src/matches.rs | 2 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/methods/iterator_step_by_zero.rs | 2 +- clippy_lints/src/minmax.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/neg_multiply.rs | 3 +-- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/regex.rs | 2 +- clippy_lints/src/repeat_once.rs | 2 +- clippy_lints/src/transmuting_null.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 2 +- clippy_lints/src/vec.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 2 +- 31 files changed, 29 insertions(+), 35 deletions(-) delete mode 100644 clippy_lints/src/consts.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/absurd_extreme_comparisons.rs index 5fbf4bdbd18..49d4350123f 100644 --- a/clippy_lints/src/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/absurd_extreme_comparisons.rs @@ -3,9 +3,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::consts::{constant, Constant}; - use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::ty::is_isize_or_usize; diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index c560f545d6a..24c2a972811 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -1,4 +1,4 @@ -use crate::consts::constant_simple; +use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 6c9ceaa45c2..5235b2642d1 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call}; diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 0ac6dc28e73..991ed94572c 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 040e0ca8864..c9c111a2847 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{method_chain_args, sext}; use if_chain::if_chain; diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs deleted file mode 100644 index 7e87f53e3fb..00000000000 --- a/clippy_lints/src/consts.rs +++ /dev/null @@ -1 +0,0 @@ -pub use clippy_utils::consts::*; diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 529807770f3..94b09bf7173 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths; diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 7a98ae39d3a..021136ac5e0 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -1,7 +1,7 @@ //! lint on C-like enums that are `repr(isize/usize)` and have values that //! don't fit into an `i32` -use crate::consts::{miri_to_const, Constant}; +use clippy_utils::consts::{miri_to_const, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index f95ca86a2d0..4aa9c25b1b0 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -1,11 +1,10 @@ +use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::consts::{constant_simple, Constant}; - declare_clippy_lint! { /// **What it does:** Checks for erasing operations, e.g., `x * 0`. /// diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 08f28cd54c5..e38384b01d4 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,4 +1,4 @@ -use crate::consts::{ +use clippy_utils::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 366b3b46a8a..99c461930e4 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{clip, unsext}; diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 1c54599abc4..bfa284f333a 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,6 +1,6 @@ //! lint on indexing and slicing operations -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher; use rustc_ast::ast::RangeLimits; diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index aafdebef248..37011f5578d 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -7,9 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_target::abi::LayoutOf; -use crate::consts::{constant, Constant}; - use clippy_utils::comparisons::Rel; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; use clippy_utils::{comparisons, sext}; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb91fac659e..4c9c44f55d1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -148,7 +148,6 @@ macro_rules! declare_clippy_lint { }; } -mod consts; #[cfg(feature = "metadata-collector-lint")] mod deprecated_lints; mod utils; diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 55404b87ec9..5f9ebad25e8 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -1,5 +1,5 @@ use super::WHILE_IMMUTABLE_CONDITION; -use crate::consts::constant; +use clippy_utils::consts::constant; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use if_chain::if_chain; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 6a06090f6e2..61b5fe81fa9 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 2f579edd6ad..18038dd7819 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,4 +1,4 @@ -use crate::consts::constant_simple; +use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e07d8ea56e4..cd3e3b97928 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, miri_to_const, Constant}; +use clippy_utils::consts::{constant, miri_to_const, Constant}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 52d7c15332e..68d906c3ea3 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 06b12998b1a..64c09214a76 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir as hir; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 45948f4d926..ff3473b744e 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{match_def_path, match_trait_method, paths}; use if_chain::if_chain; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 5976b060ad2..804c04fe1b8 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -17,7 +17,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use rustc_span::symbol::sym; -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 64e9dc85466..1414fdc1b11 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; use if_chain::if_chain; diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 34fd012572f..d5e1ea6d242 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -1,3 +1,4 @@ +use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -5,8 +6,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::consts::{self, Constant}; - declare_clippy_lint! { /// **What it does:** Checks for multiplication by -1 as a form of negation. /// diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 7169f96eaf1..ae5f0627fd6 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 4b5306de58e..75151167454 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 560a5e7c920..b479c40bca6 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; use clippy_utils::source::snippet; diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 888ecab1046..b57d158293d 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{is_expr_path_def_path, paths}; use if_chain::if_chain; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 2c73c3d929b..b1523e032af 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index febd4b6ff7b..1d5b7c98d31 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,5 +1,5 @@ -use crate::consts::{constant, Constant}; use crate::rustc_target::abi::LayoutOf; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 350b1cf25ff..a1ea743ba80 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; -- cgit 1.4.1-3-g733a5 From d3c20c835f63f1953c3940d9b1f9ce8a943a0bc8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 16:10:35 -0500 Subject: Some cleanup for use_self --- clippy_lints/src/use_self.rs | 178 +++++++++++++------------------------------ 1 file changed, 51 insertions(+), 127 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 254b104bdef..e79983134e4 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,23 +1,21 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; use clippy_utils::ty::same_type_and_consts; use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ self as hir, - def::{self, DefKind}, + def::{CtorOf, DefKind, Res}, def_id::LocalDefId, intravisit::{walk_ty, NestedVisitorMap, Visitor}, - Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment, - QPath, TyKind, + Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{AssocKind, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { @@ -234,111 +232,58 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if in_macro(hir_ty.span) - || in_impl(cx, hir_ty) - || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) - { - return; - } - - let lint_dependend_on_expr_kind = if let Some(StackItem::Check { - hir_id, - types_to_lint, - types_to_skip, - .. - }) = self.stack.last() - { - if types_to_skip.contains(&hir_ty.hir_id) { - false - } else if types_to_lint.contains(&hir_ty.hir_id) { - true - } else { - let self_ty = ty_from_hir_id(cx, *hir_id); - should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) - } - } else { - false - }; - - if lint_dependend_on_expr_kind { - // FIXME: this span manipulation should not be necessary - // @flip1995 found an ast lowering issue in - // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 + if_chain! { + if !in_macro(hir_ty.span) && !in_impl(cx, hir_ty); + if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if let Some(StackItem::Check { + hir_id, + types_to_lint, + types_to_skip, + .. + }) = self.stack.last(); + if !types_to_skip.contains(&hir_ty.hir_id); + if types_to_lint.contains(&hir_ty.hir_id) + || { + let self_ty = ty_from_hir_id(cx, *hir_id); + should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) + }; let hir = cx.tcx.hir(); let id = hir.get_parent_node(hir_ty.hir_id); - - if !hir.opt_span(id).map_or(false, in_macro) { - match hir.find(id) { - Some(Node::Expr(Expr { - kind: ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - })) => span_lint_until_last_segment(cx, hir_ty.span, segment), - _ => span_lint(cx, hir_ty.span), - } + if !hir.opt_span(id).map_or(false, in_macro); + then { + span_lint(cx, hir_ty.span); } } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool { - let def_id = expr.hir_id.owner; - if cx.tcx.has_typeck_results(def_id) { - cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty) - } else { - false - } - } - - if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) { - return; + if_chain! { + if !in_macro(expr.span); + if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if let Some(StackItem::Check { hir_id, .. }) = self.stack.last(); + if cx.typeck_results().expr_ty(expr) == ty_from_hir_id(cx, *hir_id); + then {} else { return; } } - - if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { - let self_ty = ty_from_hir_id(cx, *hir_id); - - match &expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => { - if expr_ty_matches(cx, expr, self_ty) { - match path.res { - def::Res::SelfTy(..) => (), - def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path), - _ => { - span_lint(cx, path.span); - }, - } - } - }, - // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) - ExprKind::Call(fun, _) => { - if let Expr { - kind: ExprKind::Path(ref qpath), - .. - } = fun - { - if expr_ty_matches(cx, expr, self_ty) { - let res = cx.qpath_res(qpath, fun.hir_id); - - if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res { - match ctor_of { - def::CtorOf::Variant => { - span_lint_on_qpath_resolved(cx, qpath, true); - }, - def::CtorOf::Struct => { - span_lint_on_qpath_resolved(cx, qpath, false); - }, - } - } + match expr.kind { + ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res { + Res::SelfTy(..) => (), + Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path), + _ => span_lint(cx, path.span), + }, + // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) + ExprKind::Call(fun, _) => { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { + if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res { + match ctor_of { + CtorOf::Variant => lint_path_to_variant(cx, path), + CtorOf::Struct => span_lint(cx, path.span), } } - }, - // unit enum variants (`Enum::A`) - ExprKind::Path(qpath) => { - if expr_ty_matches(cx, expr, self_ty) { - span_lint_on_qpath_resolved(cx, qpath, true); - } - }, - _ => (), - } + } + }, + // unit enum variants (`Enum::A`) + ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path), + _ => (), } } @@ -405,33 +350,12 @@ fn span_lint(cx: &LateContext<'_>, span: Span) { ); } -#[allow(clippy::cast_possible_truncation)] -fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) { - let sp = span.with_hi(segment.ident.span.lo()); - // remove the trailing :: - let span_without_last_segment = match snippet_opt(cx, sp) { - Some(snippet) => match snippet.rfind("::") { - Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)), - None => sp, - }, - None => sp, - }; - span_lint(cx, span_without_last_segment); -} - -fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) { - if path.segments.len() > 1 { - span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap()); - } -} - -fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) { - if let QPath::Resolved(_, path) = qpath { - if until_last_segment { - span_lint_on_path_until_last_segment(cx, path); - } else { - span_lint(cx, path.span); - } +fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { + if let [.., self_seg, _variant] = path.segments { + let span = path + .span + .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi()); + span_lint(cx, span); } } -- cgit 1.4.1-3-g733a5 From 29b4b4c10d89b2278485ac0e24a393ef58290672 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 16:25:37 -0500 Subject: Do not lint use_self on type parameters --- clippy_lints/src/use_self.rs | 2 +- tests/ui/use_self.fixed | 23 +++++++++++++++++++++++ tests/ui/use_self.rs | 25 ++++++++++++++++++++++++- tests/ui/use_self.stderr | 4 ++-- 4 files changed, 50 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e79983134e4..f71dfd02499 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -386,7 +386,7 @@ fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if same_type_and_consts(ty, self_ty); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; then { - !matches!(path.res, def::Res::SelfTy(..)) + !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _)) } else { false } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 631da6fe066..e2c28542efc 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -492,3 +492,26 @@ mod issue7206 { } } } + +mod self_is_ty_param { + trait Trait { + type Type; + type Hi; + + fn test(); + } + + impl Trait for I + where + I: Iterator, + I::Item: Trait, // changing this to Self would require + { + type Type = I; + type Hi = I::Item; + + fn test() { + let _: I::Item; + let _: I; // this could lint, but is questionable + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 7a10d755faa..3cd99b9f5cd 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -279,7 +279,7 @@ mod generics { impl Foo { // `Self` is applicable here fn foo(value: T) -> Foo { - Foo { value } + Foo:: { value } } // `Cannot` use `Self` as a return type as the generic types are different @@ -492,3 +492,26 @@ mod issue7206 { } } } + +mod self_is_ty_param { + trait Trait { + type Type; + type Hi; + + fn test(); + } + + impl Trait for I + where + I: Iterator, + I::Item: Trait, // changing this to Self would require + { + type Type = I; + type Hi = I::Item; + + fn test() { + let _: I::Item; + let _: I; // this could lint, but is questionable + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index cf6222c9b45..6ac26c9e5a9 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -153,8 +153,8 @@ LL | fn foo(value: T) -> Foo { error: unnecessary structure name repetition --> $DIR/use_self.rs:282:13 | -LL | Foo { value } - | ^^^ help: use the applicable keyword: `Self` +LL | Foo:: { value } + | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition --> $DIR/use_self.rs:454:13 -- cgit 1.4.1-3-g733a5 From 6c1ba7c1bc1a1113b68c7100894f33f2fb5f7ebf Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 20:39:20 -0500 Subject: Fix needless_collect with binding shadowing --- clippy_lints/src/loops/needless_collect.rs | 79 +++++++++++++----------------- 1 file changed, 35 insertions(+), 44 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index eb82c9c27c3..b3992157cd4 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -7,10 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; +use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, PatKind, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::sym; use rustc_span::{MultiSpan, Span}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; @@ -88,24 +88,23 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ExprKind::Block(block, _) = expr.kind { for stmt in block.stmts { if_chain! { - if let StmtKind::Local( - Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(init_expr), ty, .. } - ) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; + if let PatKind::Binding(_, id, ..) = local.pat.kind; + if let Some(init_expr) = local.init; if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - if let Some(hir_id) = get_hir_id(*ty, method_name.args); + if let Some(hir_id) = get_hir_id(local.ty, method_name.args); if let Some(ty) = cx.typeck_results().node_type_opt(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || is_type_diagnostic_item(cx, ty, sym::LinkedList); - if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); + if let Some(iter_calls) = detect_iter_and_into_iters(block, id); if let [iter_call] = &*iter_calls; then { let mut used_count_visitor = UsedCountVisitor { cx, - id: *pat_id, + id, count: 0, }; walk_block(&mut used_count_visitor, block); @@ -187,48 +186,40 @@ enum IterFunctionKind { struct IterFunctionVisitor { uses: Vec, seen_other: bool, - target: Ident, + target: HirId, } impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { // Check function calls on our collection - if_chain! { - if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; - if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0); - if let &[name] = &path.segments; - if name.ident == self.target; - then { - let len = sym!(len); - let is_empty = sym!(is_empty); - let contains = sym!(contains); - match method_name.ident.name { - sym::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 } - ), + if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind { + if path_to_local_id(recv, self.target) { + match &*method_name.ident.name.as_str() { + "into_iter" => self.uses.push(IterFunction { + func: IterFunctionKind::IntoIter, + span: expr.span, + }), + "len" => self.uses.push(IterFunction { + func: IterFunctionKind::Len, + span: expr.span, + }), + "is_empty" => self.uses.push(IterFunction { + func: IterFunctionKind::IsEmpty, + span: expr.span, + }), + "contains" => self.uses.push(IterFunction { + func: IterFunctionKind::Contains(args[0].span), + span: expr.span, + }), _ => self.seen_other = true, } - return + return; } } // Check if the collection is used for anything else - if_chain! { - if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr; - if let &[name] = &path.segments; - if name.ident == self.target; - then { - self.seen_other = true; - } else { - walk_expr(self, expr); - } + if path_to_local_id(expr, self.target) { + self.seen_other = true; + } else { + walk_expr(self, expr); } } @@ -262,10 +253,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { /// Detect the occurrences of calls to `iter` or `into_iter` for the /// given identifier -fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option> { let mut visitor = IterFunctionVisitor { uses: Vec::new(), - target: identifier, + target: id, seen_other: false, }; visitor.visit_block(block); -- cgit 1.4.1-3-g733a5 From 21c829e0c81697bbace7c325b47856d32fad33df Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 20:54:56 -0500 Subject: Simplify collect expr_ty --- clippy_lints/src/loops/needless_collect.rs | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index b3992157cd4..51d7def137e 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -7,7 +7,7 @@ use clippy_utils::{is_trait_method, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, PatKind, StmtKind, Ty}; +use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::sym; @@ -24,10 +24,8 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let ExprKind::MethodCall(method, _, args, _) = expr.kind; if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind; if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); - if let Some(generic_args) = chain_method.args; - if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id); then { + let ty = cx.typeck_results().expr_ty(&args[0]); let mut applicability = Applicability::MachineApplicable; let is_empty_sugg = "next().is_none()".to_string(); let method_name = &*method.ident.name.as_str(); @@ -72,19 +70,6 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont } fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option { - if let Some(ty) = ty { - return Some(ty.hir_id); - } - - if let Some(generic_args) = method_args { - if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) { - return Some(ty.hir_id); - } - } - - None - } if let ExprKind::Block(block, _) = expr.kind { for stmt in block.stmts { if_chain! { @@ -93,8 +78,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(init_expr) = local.init; if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - if let Some(hir_id) = get_hir_id(local.ty, method_name.args); - if let Some(ty) = cx.typeck_results().node_type_opt(hir_id); + let ty = cx.typeck_results().expr_ty(init_expr); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || -- cgit 1.4.1-3-g733a5 From 4ba6afd1922b679eb533b8523f6e47a0da152afb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 27 May 2021 15:16:26 -0400 Subject: Fix ICE in `too_many_lines` due to wrong assumptions on braces. --- clippy_lints/src/functions/too_many_lines.rs | 22 ++++++++++++++++------ tests/ui/crashes/auxiliary/ice-7272-aux.rs | 14 ++++++++++++++ tests/ui/crashes/ice-7272.rs | 12 ++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 tests/ui/crashes/auxiliary/ice-7272-aux.rs create mode 100644 tests/ui/crashes/ice-7272.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index c02c343dc58..a666fee1a4a 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -4,7 +4,7 @@ use rustc_middle::lint::in_external_macro; use rustc_span::Span; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_opt; use super::TOO_MANY_LINES; @@ -13,15 +13,25 @@ pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<' return; } - let code_snippet = snippet(cx, body.value.span, ".."); + let code_snippet = match snippet_opt(cx, body.value.span) { + Some(s) => s, + _ => return, + }; let mut line_count: u64 = 0; let mut in_comment = false; let mut code_in_line; - // Skip the surrounding function decl. - let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); - let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); - let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..)) + && code_snippet.as_bytes().first().copied() == Some(b'{') + && code_snippet.as_bytes().last().copied() == Some(b'}') + { + // Removing the braces from the enclosing block + &code_snippet[1..code_snippet.len() - 1] + } else { + &code_snippet + } + .trim() // Remove leading and trailing blank lines + .lines(); for mut line in function_lines { code_in_line = false; diff --git a/tests/ui/crashes/auxiliary/ice-7272-aux.rs b/tests/ui/crashes/auxiliary/ice-7272-aux.rs new file mode 100644 index 00000000000..780797e3c6a --- /dev/null +++ b/tests/ui/crashes/auxiliary/ice-7272-aux.rs @@ -0,0 +1,14 @@ +pub fn warn(_: T) {} + +macro_rules! define_macro { + ($d:tt $lower:ident $upper:ident) => { + #[macro_export] + macro_rules! $upper { + ($arg:tt) => { + $crate::$lower($arg) + }; + } + }; +} + +define_macro! {$ warn WARNING} diff --git a/tests/ui/crashes/ice-7272.rs b/tests/ui/crashes/ice-7272.rs new file mode 100644 index 00000000000..57ab6ca14f8 --- /dev/null +++ b/tests/ui/crashes/ice-7272.rs @@ -0,0 +1,12 @@ +// aux-build:ice-7272-aux.rs + +#![allow(clippy::no_effect)] + +extern crate ice_7272_aux; + +use ice_7272_aux::*; + +pub fn main() { + || WARNING!("Style changed!"); + || "}{"; +} -- cgit 1.4.1-3-g733a5 From 898b6a0e072c4f8ba741e83bd7fd12a4c2ccb1a2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 May 2021 09:35:06 -0400 Subject: Add lint `suspicious_splitn` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 36 +++++++++++++++- clippy_lints/src/methods/suspicious_splitn.rs | 52 +++++++++++++++++++++++ tests/ui/single_char_pattern.fixed | 4 +- tests/ui/single_char_pattern.rs | 4 +- tests/ui/single_char_pattern.stderr | 4 +- tests/ui/suspicious_splitn.rs | 16 ++++++++ tests/ui/suspicious_splitn.stderr | 59 +++++++++++++++++++++++++++ 9 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 clippy_lints/src/methods/suspicious_splitn.rs create mode 100644 tests/ui/suspicious_splitn.rs create mode 100644 tests/ui/suspicious_splitn.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index abfe7f91f4b..59daa074282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2671,6 +2671,7 @@ Released 2018-09-13 [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4c9c44f55d1..0e815be9bd5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -779,6 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::SKIP_WHILE_NEXT, methods::STRING_EXTEND_CHARS, methods::SUSPICIOUS_MAP, + methods::SUSPICIOUS_SPLITN, methods::UNINIT_ASSUMED_INIT, methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FOLD, @@ -1312,6 +1313,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::STRING_EXTEND_CHARS), LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::SUSPICIOUS_SPLITN), LintId::of(methods::UNINIT_ASSUMED_INIT), LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FOLD), @@ -1688,6 +1690,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::SUSPICIOUS_SPLITN), LintId::of(methods::UNINIT_ASSUMED_INIT), LintId::of(methods::ZST_OFFSET), LintId::of(minmax::MIN_MAX), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0b998dbf86c..3904572c627 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -48,6 +48,7 @@ mod single_char_push_string; mod skip_while_next; mod string_extend_chars; mod suspicious_map; +mod suspicious_splitn; mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_fold; @@ -1633,6 +1634,35 @@ declare_clippy_lint! { "replace `.iter().count()` with `.len()`" } +declare_clippy_lint! { + /// **What it does:** Checks for calls to `splitn` and related functions with + /// either zero or one splits. + /// + /// **Why is this bad?** These calls don't actually split the value and are + /// likely to be intended as a different number. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let s = ""; + /// for x in s.splitn(1, ":") { + /// // use x + /// } + /// + /// // Good + /// let s = ""; + /// for x in s.splitn(2, ":") { + /// // use x + /// } + /// ``` + pub SUSPICIOUS_SPLITN, + correctness, + "checks for `.splitn(0, ..)` and `.splitn(1, ..)`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1705,7 +1735,8 @@ impl_lint_pass!(Methods => [ MAP_COLLECT_RESULT_UNIT, FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, - IMPLICIT_CLONE + IMPLICIT_CLONE, + SUSPICIOUS_SPLITN ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2024,6 +2055,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, + ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => { + suspicious_splitn::check(cx, name, expr, recv, count_arg); + }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv, span); diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs new file mode 100644 index 00000000000..43affe20dc5 --- /dev/null +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::SUSPICIOUS_SPLITN; + +pub(super) fn check( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + count_arg: &Expr<'_>, +) { + if_chain! { + // Ignore empty slice literal + if !matches!(self_arg.kind, ExprKind::Array([])); + // Ignore empty string literal + if !matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()); + if let ExprKind::Lit(count_lit) = &count_arg.kind; + if let LitKind::Int(count, _) = count_lit.node; + if count <= 1; + if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(call_id); + let lang_items = cx.tcx.lang_items(); + if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); + then { + let (msg, note_msg) = if count == 0 { + (format!("`{}` called with `0` splits", method_name), + "the resulting iterator will always return `None`") + } else { + (format!("`{}` called with `1` split", method_name), + if lang_items.slice_impl() == Some(impl_id) { + "the resulting iterator will always return the entire slice followed by `None`" + } else { + "the resulting iterator will always return the entire string followed by `None`" + }) + }; + + span_lint_and_note( + cx, + SUSPICIOUS_SPLITN, + expr.span, + &msg, + None, + note_msg, + ); + } + } +} diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index fcbe9af9f56..1abd2b7883d 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -25,8 +25,8 @@ fn main() { x.rsplit('x'); x.split_terminator('x'); x.rsplit_terminator('x'); - x.splitn(0, 'x'); - x.rsplitn(0, 'x'); + x.splitn(2, 'x'); + x.rsplitn(2, 'x'); x.matches('x'); x.rmatches('x'); x.match_indices('x'); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index b8bc20f4070..e662bf34be2 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -25,8 +25,8 @@ fn main() { x.rsplit("x"); x.split_terminator("x"); x.rsplit_terminator("x"); - x.splitn(0, "x"); - x.rsplitn(0, "x"); + x.splitn(2, "x"); + x.rsplitn(2, "x"); x.matches("x"); x.rmatches("x"); x.match_indices("x"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index 6d94d8a34e3..22d4b2d460f 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -75,13 +75,13 @@ LL | x.rsplit_terminator("x"); error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:28:17 | -LL | x.splitn(0, "x"); +LL | x.splitn(2, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:29:18 | -LL | x.rsplitn(0, "x"); +LL | x.rsplitn(2, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern diff --git a/tests/ui/suspicious_splitn.rs b/tests/ui/suspicious_splitn.rs new file mode 100644 index 00000000000..a944aa79cfe --- /dev/null +++ b/tests/ui/suspicious_splitn.rs @@ -0,0 +1,16 @@ +#![warn(clippy::suspicious_splitn)] + +fn main() { + let _ = "a,b,c".splitn(3, ','); + let _ = [0, 1, 2, 1, 3].splitn(3, |&x| x == 1); + let _ = "".splitn(0, ','); + let _ = [].splitn(0, |&x: &u32| x == 1); + + let _ = "a,b".splitn(0, ','); + let _ = "a,b".rsplitn(0, ','); + let _ = "a,b".splitn(1, ','); + let _ = [0, 1, 2].splitn(0, |&x| x == 1); + let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1); + let _ = [0, 1, 2].splitn(1, |&x| x == 1); + let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); +} diff --git a/tests/ui/suspicious_splitn.stderr b/tests/ui/suspicious_splitn.stderr new file mode 100644 index 00000000000..2e4bfe93b94 --- /dev/null +++ b/tests/ui/suspicious_splitn.stderr @@ -0,0 +1,59 @@ +error: `splitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:9:13 + | +LL | let _ = "a,b".splitn(0, ','); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::suspicious-splitn` implied by `-D warnings` + = note: the resulting iterator will always return `None` + +error: `rsplitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:10:13 + | +LL | let _ = "a,b".rsplitn(0, ','); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: `splitn` called with `1` split + --> $DIR/suspicious_splitn.rs:11:13 + | +LL | let _ = "a,b".splitn(1, ','); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire string followed by `None` + +error: `splitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:12:13 + | +LL | let _ = [0, 1, 2].splitn(0, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: `splitn_mut` called with `0` splits + --> $DIR/suspicious_splitn.rs:13:13 + | +LL | let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: `splitn` called with `1` split + --> $DIR/suspicious_splitn.rs:14:13 + | +LL | let _ = [0, 1, 2].splitn(1, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire slice followed by `None` + +error: `rsplitn_mut` called with `1` split + --> $DIR/suspicious_splitn.rs:15:13 + | +LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire slice followed by `None` + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 5fa08eaf53f0c895e73f4841c240389fda951554 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 May 2021 13:25:24 -0400 Subject: Evaluate constant expressions in `suspicious_splitn` --- clippy_lints/src/methods/mod.rs | 5 +++-- clippy_lints/src/methods/suspicious_splitn.rs | 16 ++++++++++------ tests/ui/suspicious_splitn.rs | 4 ++++ tests/ui/suspicious_splitn.stderr | 18 +++++++++++++++++- 4 files changed, 34 insertions(+), 9 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3904572c627..a6e2e0baadb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1635,8 +1635,9 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for calls to `splitn` and related functions with - /// either zero or one splits. + /// **What it does:** Checks for calls to [`splitn`] + /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and + /// related functions with either zero or one splits. /// /// **Why is this bad?** These calls don't actually split the value and are /// likely to be intended as a different number. diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 43affe20dc5..a271df60572 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -1,3 +1,4 @@ +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use if_chain::if_chain; use rustc_ast::LitKind; @@ -15,18 +16,21 @@ pub(super) fn check( count_arg: &Expr<'_>, ) { if_chain! { - // Ignore empty slice literal - if !matches!(self_arg.kind, ExprKind::Array([])); - // Ignore empty string literal - if !matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()); - if let ExprKind::Lit(count_lit) = &count_arg.kind; - if let LitKind::Int(count, _) = count_lit.node; + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg); if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); let lang_items = cx.tcx.lang_items(); if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); then { + // Ignore empty slice and string literals when used with a literal count. + if (matches!(self_arg.kind, ExprKind::Array([])) + || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) + ) && matches!(count_arg.kind, ExprKind::Lit(_)) + { + return; + } + let (msg, note_msg) = if count == 0 { (format!("`{}` called with `0` splits", method_name), "the resulting iterator will always return `None`") diff --git a/tests/ui/suspicious_splitn.rs b/tests/ui/suspicious_splitn.rs index a944aa79cfe..a21d94cf20b 100644 --- a/tests/ui/suspicious_splitn.rs +++ b/tests/ui/suspicious_splitn.rs @@ -13,4 +13,8 @@ fn main() { let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1); let _ = [0, 1, 2].splitn(1, |&x| x == 1); let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); + + const X: usize = 0; + let _ = "a,b".splitn(X + 1, ','); + let _ = "a,b".splitn(X, ','); } diff --git a/tests/ui/suspicious_splitn.stderr b/tests/ui/suspicious_splitn.stderr index 2e4bfe93b94..b6220ae2393 100644 --- a/tests/ui/suspicious_splitn.stderr +++ b/tests/ui/suspicious_splitn.stderr @@ -55,5 +55,21 @@ LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); | = note: the resulting iterator will always return the entire slice followed by `None` -error: aborting due to 7 previous errors +error: `splitn` called with `1` split + --> $DIR/suspicious_splitn.rs:18:13 + | +LL | let _ = "a,b".splitn(X + 1, ','); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire string followed by `None` + +error: `splitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:19:13 + | +LL | let _ = "a,b".splitn(X, ','); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 58491d386a35b7103dc6ab93f9c69eb1b8891fb9 Mon Sep 17 00:00:00 2001 From: Steven Engler Date: Sun, 30 May 2021 20:19:57 -0400 Subject: Update message for 'not_unsafe_ptr_arg_deref' lint --- clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs | 2 +- tests/ui/functions.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index b8ea6990866..af759a48e10 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -116,7 +116,7 @@ impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { self.cx, NOT_UNSAFE_PTR_ARG_DEREF, ptr.span, - "this public function dereferences a raw pointer but is not marked `unsafe`", + "this public function might dereference a raw pointer but is not marked `unsafe`", ); } } diff --git a/tests/ui/functions.stderr b/tests/ui/functions.stderr index 0a86568b18d..a2b8c2a384b 100644 --- a/tests/ui/functions.stderr +++ b/tests/ui/functions.stderr @@ -30,7 +30,7 @@ error: this function has too many arguments (8/7) LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:63:34 | LL | println!("{}", unsafe { *p }); @@ -38,49 +38,49 @@ LL | println!("{}", unsafe { *p }); | = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings` -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:64:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:65:33 | LL | unsafe { std::ptr::read(p) }; | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:76:30 | LL | println!("{}", unsafe { *p }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:77:31 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:78:29 | LL | unsafe { std::ptr::read(p) }; | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:87:34 | LL | println!("{}", unsafe { *p }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:88:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:89:33 | LL | unsafe { std::ptr::read(p) }; -- cgit 1.4.1-3-g733a5 From 97311f0906ca89656f5942b326a665fe98d84c17 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 22 May 2021 17:08:17 -0400 Subject: Add lint `manual_str_repeat` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/mem_discriminant.rs | 3 +- clippy_lints/src/methods/clone_on_copy.rs | 5 +- clippy_lints/src/methods/manual_str_repeat.rs | 89 +++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 35 ++++++++++- clippy_utils/src/msrvs.rs | 1 + tests/ui/manual_str_repeat.fixed | 31 ++++++++++ tests/ui/manual_str_repeat.rs | 31 ++++++++++ tests/ui/manual_str_repeat.stderr | 46 ++++++++++++++ 10 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 clippy_lints/src/methods/manual_str_repeat.rs create mode 100644 tests/ui/manual_str_repeat.fixed create mode 100644 tests/ui/manual_str_repeat.rs create mode 100644 tests/ui/manual_str_repeat.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 59daa074282..41af8e190dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2493,6 +2493,7 @@ Released 2018-09-13 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0e815be9bd5..e7dd3952b3a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -762,6 +762,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::MANUAL_FILTER_MAP, methods::MANUAL_FIND_MAP, methods::MANUAL_SATURATING_ARITHMETIC, + methods::MANUAL_STR_REPEAT, methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_FLATTEN, methods::MAP_UNWRAP_OR, @@ -1298,6 +1299,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -1735,6 +1737,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(loops::NEEDLESS_COLLECT), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::ITER_NTH), + LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::OR_FUN_CALL), LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(misc::CMP_OWNED), diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index a735c616f6e..aca96e06ef2 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -7,7 +7,6 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::iter; declare_clippy_lint! { /// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type. @@ -67,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { } } - let derefs: String = iter::repeat('*').take(derefs_needed).collect(); + let derefs = "*".repeat(derefs_needed); diag.span_suggestion( param.span, "try dereferencing", diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index ce2e8fa8b10..1a32af5dc7a 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -8,7 +8,6 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, adjustment::Adjust}; use rustc_span::symbol::{sym, Symbol}; -use std::iter; use super::CLONE_DOUBLE_REF; use super::CLONE_ON_COPY; @@ -54,8 +53,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, ty = inner; n += 1; } - let refs: String = iter::repeat('&').take(n + 1).collect(); - let derefs: String = iter::repeat('*').take(n).collect(); + let refs = "&".repeat(n + 1); + let derefs = "*".repeat(n); let explicit = format!("<{}{}>::clone({})", refs, ty, snip); diag.span_suggestion( expr.span, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs new file mode 100644 index 00000000000..3f28412fbf7 --- /dev/null +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -0,0 +1,89 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +use clippy_utils::{is_expr_path_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::symbol::{sym, Symbol}; + +use super::MANUAL_STR_REPEAT; + +enum RepeatKind { + Str, + String, + Char, +} + +fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit) = &e.kind { + match lit.node { + LitKind::Str(..) => Some(RepeatKind::Str), + LitKind::Char(_) => Some(RepeatKind::Char), + _ => None, + } + } else { + let ty = cx.typeck_results().expr_ty(e); + if is_type_diagnostic_item(cx, ty, sym::string_type) + || is_type_lang_item(cx, ty, LangItem::OwnedBox) + || match_type(cx, ty, &paths::COW) + { + Some(RepeatKind::String) + } else { + let ty = ty.peel_refs(); + (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::string_type)).then(|| RepeatKind::Str) + } + } +} + +pub(super) fn check( + cx: &LateContext<'_>, + collect_expr: &Expr<'_>, + take_expr: &Expr<'_>, + take_self_arg: &Expr<'_>, + take_arg: &Expr<'_>, +) { + if_chain! { + if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; + if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::string_type); + if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); + if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); + if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id); + if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); + if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); + let ctxt = collect_expr.span.ctxt(); + if ctxt == take_expr.span.ctxt(); + if ctxt == take_self_arg.span.ctxt(); + then { + let mut app = Applicability::MachineApplicable; + let (val_snip, val_is_mac) = snippet_with_context(cx, repeat_arg.span, ctxt, "..", &mut app); + let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; + + let val_str = match repeat_kind { + RepeatKind::String => format!("(&{})", val_snip), + RepeatKind::Str if !val_is_mac && repeat_arg.precedence().order() < PREC_POSTFIX => { + format!("({})", val_snip) + }, + RepeatKind::Str => val_snip.into(), + RepeatKind::Char if val_snip == r#"'"'"# => r#""\"""#.into(), + RepeatKind::Char if val_snip == r#"'\''"# => r#""'""#.into(), + RepeatKind::Char => format!("\"{}\"", &val_snip[1..val_snip.len() - 1]), + }; + + span_lint_and_sugg( + cx, + MANUAL_STR_REPEAT, + collect_expr.span, + "manual implementation of `str::repeat` using iterators", + "try this", + format!("{}.repeat({})", val_str, count_snip), + app + ) + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a6e2e0baadb..62a56434b3c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,6 +32,7 @@ mod iter_nth_zero; mod iter_skip_next; mod iterator_step_by_zero; mod manual_saturating_arithmetic; +mod manual_str_repeat; mod map_collect_result_unit; mod map_flatten; mod map_unwrap_or; @@ -60,9 +61,12 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty}; +use clippy_utils::{ + diagnostics::{span_lint, span_lint_and_help}, + meets_msrv, msrvs, +}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -1664,6 +1668,27 @@ declare_clippy_lint! { "checks for `.splitn(0, ..)` and `.splitn(1, ..)`" } +declare_clippy_lint! { + /// **What it does:** Checks for manual implementations of `str::repeat` + /// + /// **Why is this bad?** These are both harder to read, as well as less performant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let x: String = std::iter::repeat('x').take(10).collect(); + /// + /// // Good + /// let x: String = "x".repeat(10); + /// ``` + pub MANUAL_STR_REPEAT, + perf, + "manual implementation of `str::repeat`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1737,7 +1762,8 @@ impl_lint_pass!(Methods => [ FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, IMPLICIT_CLONE, - SUSPICIOUS_SPLITN + SUSPICIOUS_SPLITN, + MANUAL_STR_REPEAT ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -1981,6 +2007,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio Some(("map", [m_recv, m_arg], _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); }, + Some(("take", [take_self_arg, take_arg], _)) => { + if meets_msrv(msrv, &msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, _ => {}, }, ("count", []) => match method_call!(recv) { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 00df04c0144..4a9c4fd0276 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -26,4 +26,5 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } + 1,16,0 { STR_REPEAT } } diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed new file mode 100644 index 00000000000..62225e7a7f8 --- /dev/null +++ b/tests/ui/manual_str_repeat.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::manual_str_repeat)] + +use std::iter::repeat; + +fn main() { + let _: String = "test".repeat(10); + let _: String = "x".repeat(10); + let _: String = "'".repeat(10); + let _: String = "\"".repeat(10); + + let x = "test"; + let count = 10; + let _ = x.repeat(count + 2); + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + + let _: String = m!("test").repeat(m!(count)); + + let x = &x; + let _: String = (*x).repeat(count); + + macro_rules! repeat_m { + ($e:expr) => {{ repeat($e) }}; + } + // Don't lint, repeat is from a macro. + let _: String = repeat_m!("test").take(count).collect(); +} diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs new file mode 100644 index 00000000000..1acd66da275 --- /dev/null +++ b/tests/ui/manual_str_repeat.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::manual_str_repeat)] + +use std::iter::repeat; + +fn main() { + let _: String = std::iter::repeat("test").take(10).collect(); + let _: String = std::iter::repeat('x').take(10).collect(); + let _: String = std::iter::repeat('\'').take(10).collect(); + let _: String = std::iter::repeat('"').take(10).collect(); + + let x = "test"; + let count = 10; + let _ = repeat(x).take(count + 2).collect::(); + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + + let _: String = repeat(m!("test")).take(m!(count)).collect(); + + let x = &x; + let _: String = repeat(*x).take(count).collect(); + + macro_rules! repeat_m { + ($e:expr) => {{ repeat($e) }}; + } + // Don't lint, repeat is from a macro. + let _: String = repeat_m!("test").take(count).collect(); +} diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr new file mode 100644 index 00000000000..ef67ad2a1fd --- /dev/null +++ b/tests/ui/manual_str_repeat.stderr @@ -0,0 +1,46 @@ +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:8:21 + | +LL | let _: String = std::iter::repeat("test").take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` + | + = note: `-D clippy::manual-str-repeat` implied by `-D warnings` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:9:21 + | +LL | let _: String = std::iter::repeat('x').take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:10:21 + | +LL | let _: String = std::iter::repeat('/'').take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:11:21 + | +LL | let _: String = std::iter::repeat('"').take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:15:13 + | +LL | let _ = repeat(x).take(count + 2).collect::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:21:21 + | +LL | let _: String = repeat(m!("test")).take(m!(count)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `m!("test").repeat(m!(count))` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:24:21 + | +LL | let _: String = repeat(*x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From cfddf0927bd71b859bc1e749ec159285433a3849 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 23 May 2021 13:16:09 -0400 Subject: Fix type checks for `manual_str_repeat` --- clippy_lints/src/methods/manual_str_repeat.rs | 48 ++++++++++++++++----------- clippy_lints/src/methods/mod.rs | 7 ++-- clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/sugg.rs | 27 +++++++++++++-- tests/ui/manual_str_repeat.fixed | 41 +++++++++++++++++++++-- tests/ui/manual_str_repeat.rs | 39 ++++++++++++++++++++-- tests/ui/manual_str_repeat.stderr | 42 ++++++++++++++++------- 7 files changed, 162 insertions(+), 44 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 3f28412fbf7..919e2628c52 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,40 +1,49 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; use clippy_utils::{is_expr_path_def_path, paths}; use if_chain::if_chain; -use rustc_ast::util::parser::PREC_POSTFIX; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::symbol::{sym, Symbol}; +use rustc_middle::ty::{self, Ty, TyS}; +use rustc_span::symbol::sym; +use std::borrow::Cow; use super::MANUAL_STR_REPEAT; enum RepeatKind { - Str, String, - Char, + Char(char), +} + +fn get_ty_param(ty: Ty<'_>) -> Option> { + if let ty::Adt(_, subs) = ty.kind() { + subs.types().next() + } else { + None + } } fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { if let ExprKind::Lit(lit) = &e.kind { match lit.node { - LitKind::Str(..) => Some(RepeatKind::Str), - LitKind::Char(_) => Some(RepeatKind::Char), + LitKind::Str(..) => Some(RepeatKind::String), + LitKind::Char(c) => Some(RepeatKind::Char(c)), _ => None, } } else { let ty = cx.typeck_results().expr_ty(e); if is_type_diagnostic_item(cx, ty, sym::string_type) - || is_type_lang_item(cx, ty, LangItem::OwnedBox) - || match_type(cx, ty, &paths::COW) + || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, TyS::is_str)) + || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, TyS::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::string_type)).then(|| RepeatKind::Str) + (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::string_type)).then(|| RepeatKind::String) } } } @@ -61,18 +70,19 @@ pub(super) fn check( if ctxt == take_self_arg.span.ctxt(); then { let mut app = Applicability::MachineApplicable; - let (val_snip, val_is_mac) = snippet_with_context(cx, repeat_arg.span, ctxt, "..", &mut app); let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; let val_str = match repeat_kind { - RepeatKind::String => format!("(&{})", val_snip), - RepeatKind::Str if !val_is_mac && repeat_arg.precedence().order() < PREC_POSTFIX => { - format!("({})", val_snip) - }, - RepeatKind::Str => val_snip.into(), - RepeatKind::Char if val_snip == r#"'"'"# => r#""\"""#.into(), - RepeatKind::Char if val_snip == r#"'\''"# => r#""'""#.into(), - RepeatKind::Char => format!("\"{}\"", &val_snip[1..val_snip.len() - 1]), + RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return, + RepeatKind::Char('\'') => r#""'""#.into(), + RepeatKind::Char('"') => r#""\"""#.into(), + RepeatKind::Char(_) => + match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) { + Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])), + s @ Cow::Borrowed(_) => s, + }, + RepeatKind::String => + Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(), }; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 62a56434b3c..c8ae972f18c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -61,12 +61,9 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty}; -use clippy_utils::{ - diagnostics::{span_lint, span_lint_and_help}, - meets_msrv, msrvs, -}; +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1bd38dc042c..0e33ae740d9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -124,7 +124,7 @@ macro_rules! define_Conf { define_Conf! { /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports + /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports (msrv: Option = None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e5a70f0beac..efc0ec50fdc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -2,7 +2,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::higher; -use crate::source::{snippet, snippet_opt, snippet_with_macro_callsite}; +use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ast, token}; use rustc_ast_pretty::pprust::token_kind_to_string; @@ -10,7 +10,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_span::source_map::{CharPos, Span}; -use rustc_span::{BytePos, Pos}; +use rustc_span::{BytePos, Pos, SyntaxContext}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; @@ -90,6 +90,29 @@ impl<'a> Sugg<'a> { Self::hir_from_snippet(expr, snippet) } + /// Same as `hir`, but first walks the span up to the given context. This will result in the + /// macro call, rather then the expansion, if the span is from a child context. If the span is + /// not from a child context, it will be used directly instead. + /// + /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR + /// node would result in `box []`. If given the context of the address of expression, this + /// function will correctly get a snippet of `vec![]`. + pub fn hir_with_context( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + ctxt: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, + ) -> Self { + let (snippet, in_macro) = snippet_with_context(cx, expr.span, ctxt, default, applicability); + + if in_macro { + Sugg::NonParen(snippet) + } else { + Self::hir_from_snippet(expr, snippet) + } + } + /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index 62225e7a7f8..dc140257f32 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -1,8 +1,10 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] -use std::iter::repeat; +use std::borrow::Cow; +use std::iter::{repeat, FromIterator}; fn main() { let _: String = "test".repeat(10); @@ -17,8 +19,8 @@ fn main() { macro_rules! m { ($e:expr) => {{ $e }}; } - - let _: String = m!("test").repeat(m!(count)); + // FIXME: macro args are fine + let _: String = repeat(m!("test")).take(m!(count)).collect(); let x = &x; let _: String = (*x).repeat(count); @@ -28,4 +30,37 @@ fn main() { } // Don't lint, repeat is from a macro. let _: String = repeat_m!("test").take(count).collect(); + + let x: Box = Box::from("test"); + let _: String = x.repeat(count); + + #[derive(Clone)] + struct S; + impl FromIterator> for String { + fn from_iter>>(_: T) -> Self { + Self::new() + } + } + // Don't lint, wrong box type + let _: String = repeat(Box::new(S)).take(count).collect(); + + let _: String = Cow::Borrowed("test").repeat(count); + + let x = "x".to_owned(); + let _: String = x.repeat(count); + + let x = 'x'; + // Don't lint, not char literal + let _: String = repeat(x).take(count).collect(); +} + +fn _msrv_1_15() { + #![clippy::msrv = "1.15"] + // `str::repeat` was stabilized in 1.16. Do not lint this + let _: String = std::iter::repeat("test").take(10).collect(); +} + +fn _msrv_1_16() { + #![clippy::msrv = "1.16"] + let _: String = "test".repeat(10); } diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index 1acd66da275..0d69c989b2e 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -1,8 +1,10 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] -use std::iter::repeat; +use std::borrow::Cow; +use std::iter::{repeat, FromIterator}; fn main() { let _: String = std::iter::repeat("test").take(10).collect(); @@ -17,7 +19,7 @@ fn main() { macro_rules! m { ($e:expr) => {{ $e }}; } - + // FIXME: macro args are fine let _: String = repeat(m!("test")).take(m!(count)).collect(); let x = &x; @@ -28,4 +30,37 @@ fn main() { } // Don't lint, repeat is from a macro. let _: String = repeat_m!("test").take(count).collect(); + + let x: Box = Box::from("test"); + let _: String = repeat(x).take(count).collect(); + + #[derive(Clone)] + struct S; + impl FromIterator> for String { + fn from_iter>>(_: T) -> Self { + Self::new() + } + } + // Don't lint, wrong box type + let _: String = repeat(Box::new(S)).take(count).collect(); + + let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); + + let x = "x".to_owned(); + let _: String = repeat(x).take(count).collect(); + + let x = 'x'; + // Don't lint, not char literal + let _: String = repeat(x).take(count).collect(); +} + +fn _msrv_1_15() { + #![clippy::msrv = "1.15"] + // `str::repeat` was stabilized in 1.16. Do not lint this + let _: String = std::iter::repeat("test").take(10).collect(); +} + +fn _msrv_1_16() { + #![clippy::msrv = "1.16"] + let _: String = std::iter::repeat("test").take(10).collect(); } diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr index ef67ad2a1fd..c6511689716 100644 --- a/tests/ui/manual_str_repeat.stderr +++ b/tests/ui/manual_str_repeat.stderr @@ -1,5 +1,5 @@ error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:8:21 + --> $DIR/manual_str_repeat.rs:10:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` @@ -7,40 +7,58 @@ LL | let _: String = std::iter::repeat("test").take(10).collect(); = note: `-D clippy::manual-str-repeat` implied by `-D warnings` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:9:21 + --> $DIR/manual_str_repeat.rs:11:21 | LL | let _: String = std::iter::repeat('x').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:10:21 + --> $DIR/manual_str_repeat.rs:12:21 | LL | let _: String = std::iter::repeat('/'').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:11:21 + --> $DIR/manual_str_repeat.rs:13:21 | LL | let _: String = std::iter::repeat('"').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:15:13 + --> $DIR/manual_str_repeat.rs:17:13 | LL | let _ = repeat(x).take(count + 2).collect::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:21:21 + --> $DIR/manual_str_repeat.rs:26:21 | -LL | let _: String = repeat(m!("test")).take(m!(count)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `m!("test").repeat(m!(count))` +LL | let _: String = repeat(*x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:24:21 + --> $DIR/manual_str_repeat.rs:35:21 | -LL | let _: String = repeat(*x).take(count).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` +LL | let _: String = repeat(x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:47:21 + | +LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:50:21 + | +LL | let _: String = repeat(x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:65:21 + | +LL | let _: String = std::iter::repeat("test").take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` -error: aborting due to 7 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 84c511facf6534394eaab76299c2953fcf47e76f Mon Sep 17 00:00:00 2001 From: lyj Date: Wed, 2 Jun 2021 13:31:23 +0800 Subject: rc_mutex --- clippy_lints/src/types/mod.rs | 11 ++++++++++- clippy_lints/src/types/rc_mutex.rs | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/types/rc_mutex.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 70b9e8adef8..acf00825b7a 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -7,6 +7,7 @@ mod redundant_allocation; mod type_complexity; mod utils; mod vec_box; +mod rc_mutex; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -250,12 +251,19 @@ declare_clippy_lint! { "usage of very complex types that might be better factored into `type` definitions" } +declare_clippy_lint! { + /// TODO + pub RC_MUTEX, + restriction, + "usage of Mutex inside Rc" +} + pub struct Types { vec_box_size_threshold: u64, type_complexity_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY,RC_MUTEX]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { @@ -375,6 +383,7 @@ impl Types { triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); triggered |= option_option::check(cx, hir_ty, qpath, def_id); triggered |= linked_list::check(cx, hir_ty, def_id); + triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); if triggered { return; diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs new file mode 100644 index 00000000000..b53b55fd01c --- /dev/null +++ b/clippy_lints/src/types/rc_mutex.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{ get_qpath_generic_tys,is_ty_param_diagnostic_item}; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; +// use rustc_middle::ty::Adt; + +use super::RC_MUTEX; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) { + let mut applicability = Applicability::MachineApplicable; + + let inner_span = match get_qpath_generic_tys(qpath).skip(1).next() { + Some(ty) => ty.span, + None => return false, + }; + + span_lint_and_sugg( + cx, + RC_MUTEX, + hir_ty.span, + "you seem to be trying to use `Rc>`. Consider using `Rc>`", + "try", + format!( + "Rc>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + applicability, + ); + return true; + } + } + + false +} -- cgit 1.4.1-3-g733a5 From c0f3c2fe277102f22efc21ce64dc3e3423b777d4 Mon Sep 17 00:00:00 2001 From: lyj Date: Thu, 3 Jun 2021 14:56:34 +0800 Subject: correct lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/types/mod.rs | 40 ++++++++++++++++++++++++++++++++++---- clippy_lints/src/types/rc_mutex.rs | 16 +++++++++------ tests/ui/rc_mutex.fixed | 28 ++++++++++++++++++++++++++ tests/ui/rc_mutex.rs | 28 ++++++++++++++++++++++++++ tests/ui/rc_mutex.stderr | 22 +++++++++++++++++++++ 7 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 tests/ui/rc_mutex.fixed create mode 100644 tests/ui/rc_mutex.rs create mode 100644 tests/ui/rc_mutex.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 41af8e190dd..4fb2ce8dfc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2611,6 +2611,7 @@ Released 2018-09-13 [`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 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex [`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 e7dd3952b3a..2b999da15ca 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -931,6 +931,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: types::LINKEDLIST, types::OPTION_OPTION, types::RC_BUFFER, + types::RC_MUTEX, types::REDUNDANT_ALLOCATION, types::TYPE_COMPLEXITY, types::VEC_BOX, @@ -1027,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), LintId::of(types::RC_BUFFER), + LintId::of(types::RC_MUTEX), LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(verbose_file_reads::VERBOSE_FILE_READS), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index acf00825b7a..07dec2de827 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -3,11 +3,11 @@ mod box_vec; mod linked_list; mod option_option; mod rc_buffer; +mod rc_mutex; mod redundant_allocation; mod type_complexity; mod utils; mod vec_box; -mod rc_mutex; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -252,10 +252,42 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// TODO + /// **What it does:** Checks for `Rc>`. + /// + /// **Why is this bad?** `Rc>` may introduce a deadlock in single thread. Consider + /// using `Rc>` instead. + /// ```rust + /// fn main() { + /// use std::rc::Rc; + /// use std::sync::Mutex; + /// + /// let a: Rc> = Rc::new(Mutex::new(1)); + /// let a_clone = a.clone(); + /// let mut data = a.lock().unwrap(); + /// println!("{:?}", *a_clone.lock().unwrap()); + /// *data = 10; + /// } + /// ``` + /// + /// **Known problems:** `Rc>` may panic in runtime. + /// + /// **Example:** + /// ```rust,ignore + /// use std::rc::Rc; + /// use std::sync::Mutex; + /// fn foo(interned: Rc>) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// use std::rc::Rc; + /// use std::cell::RefCell + /// fn foo(interned: Rc>) { ... } + /// ``` pub RC_MUTEX, restriction, - "usage of Mutex inside Rc" + "usage of `Rc>`" } pub struct Types { @@ -263,7 +295,7 @@ pub struct Types { type_complexity_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY,RC_MUTEX]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index b53b55fd01c..122df01d153 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,20 +1,24 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{ get_qpath_generic_tys,is_ty_param_diagnostic_item}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; +use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -// use rustc_middle::ty::Adt; use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) { + if_chain! { + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; + if let TyKind::Path(ref qpath_inner)=ty.kind; + + then{ let mut applicability = Applicability::MachineApplicable; - let inner_span = match get_qpath_generic_tys(qpath).skip(1).next() { + let inner_span = match get_qpath_generic_tys(qpath_inner).next() { Some(ty) => ty.span, None => return false, }; diff --git a/tests/ui/rc_mutex.fixed b/tests/ui/rc_mutex.fixed new file mode 100644 index 00000000000..b36b1d0914b --- /dev/null +++ b/tests/ui/rc_mutex.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_mutex)] +#![allow(unused_imports)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Mutex; + +pub struct MyStruct {} + +pub struct SubT { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +pub fn test1(foo: Rc>) {} + +pub fn test2(foo: Rc>) {} + +pub fn test3(foo: Rc>>) {} + +fn main() {} diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs new file mode 100644 index 00000000000..e6ec4549de9 --- /dev/null +++ b/tests/ui/rc_mutex.rs @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_mutex)] +#![allow(unused_imports)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Mutex; + +pub struct MyStruct {} + +pub struct SubT { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +pub fn test1(foo: Rc>) {} + +pub fn test2(foo: Rc>) {} + +pub fn test3(foo: Rc>>) {} + +fn main() {} diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr new file mode 100644 index 00000000000..ad0340dcf55 --- /dev/null +++ b/tests/ui/rc_mutex.stderr @@ -0,0 +1,22 @@ +error: you seem to be trying to use `Rc>`. Consider using `Rc>` + --> $DIR/rc_mutex.rs:22:22 + | +LL | pub fn test1(foo: Rc>) {} + | ^^^^^^^^^^^^ help: try: `Rc>` + | + = note: `-D clippy::rc-mutex` implied by `-D warnings` + +error: you seem to be trying to use `Rc>`. Consider using `Rc>` + --> $DIR/rc_mutex.rs:24:19 + | +LL | pub fn test2(foo: Rc>) {} + | ^^^^^^^^^^^^^^^^^ help: try: `Rc>` + +error: you seem to be trying to use `Rc>`. Consider using `Rc>` + --> $DIR/rc_mutex.rs:26:19 + | +LL | pub fn test3(foo: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc>>` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From a5ced1fc2bab6c23888a85b339f93dd4c6b40dac Mon Sep 17 00:00:00 2001 From: lyj Date: Sat, 5 Jun 2021 20:53:24 +0800 Subject: rc_mutex use span_lint instead of span_lint_and_sugg --- clippy_lints/src/types/rc_mutex.rs | 28 ++++++---------------------- tests/ui/rc_mutex.fixed | 28 ---------------------------- tests/ui/rc_mutex.rs | 1 - tests/ui/rc_mutex.stderr | 18 +++++++++--------- 4 files changed, 15 insertions(+), 60 deletions(-) delete mode 100644 tests/ui/rc_mutex.fixed (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index 122df01d153..e8109f12324 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,9 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_ty_param_diagnostic_item; use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -12,28 +10,14 @@ use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if_chain! { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; - if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; - if let TyKind::Path(ref qpath_inner)=ty.kind; + if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; then{ - let mut applicability = Applicability::MachineApplicable; - - let inner_span = match get_qpath_generic_tys(qpath_inner).next() { - Some(ty) => ty.span, - None => return false, - }; - - span_lint_and_sugg( + span_lint( cx, RC_MUTEX, hir_ty.span, - "you seem to be trying to use `Rc>`. Consider using `Rc>`", - "try", - format!( - "Rc>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - applicability, + "found `Rc>`. Consider using `Rc>` instead", ); return true; } diff --git a/tests/ui/rc_mutex.fixed b/tests/ui/rc_mutex.fixed deleted file mode 100644 index b36b1d0914b..00000000000 --- a/tests/ui/rc_mutex.fixed +++ /dev/null @@ -1,28 +0,0 @@ -// run-rustfix -#![warn(clippy::rc_mutex)] -#![allow(unused_imports)] -#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::blacklisted_name, unused_variables, dead_code)] - -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::Mutex; - -pub struct MyStruct {} - -pub struct SubT { - foo: T, -} - -pub enum MyEnum { - One, - Two, -} - -pub fn test1(foo: Rc>) {} - -pub fn test2(foo: Rc>) {} - -pub fn test3(foo: Rc>>) {} - -fn main() {} diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs index e6ec4549de9..922ffbf8da1 100644 --- a/tests/ui/rc_mutex.rs +++ b/tests/ui/rc_mutex.rs @@ -1,4 +1,3 @@ -// run-rustfix #![warn(clippy::rc_mutex)] #![allow(unused_imports)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index ad0340dcf55..efd8abacd38 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,22 +1,22 @@ -error: you seem to be trying to use `Rc>`. Consider using `Rc>` - --> $DIR/rc_mutex.rs:22:22 +error: Found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:21:22 | LL | pub fn test1(foo: Rc>) {} - | ^^^^^^^^^^^^ help: try: `Rc>` + | ^^^^^^^^^^^^ | = note: `-D clippy::rc-mutex` implied by `-D warnings` -error: you seem to be trying to use `Rc>`. Consider using `Rc>` - --> $DIR/rc_mutex.rs:24:19 +error: Found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:23:19 | LL | pub fn test2(foo: Rc>) {} - | ^^^^^^^^^^^^^^^^^ help: try: `Rc>` + | ^^^^^^^^^^^^^^^^^ -error: you seem to be trying to use `Rc>`. Consider using `Rc>` - --> $DIR/rc_mutex.rs:26:19 +error: Found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:25:19 | LL | pub fn test3(foo: Rc>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc>>` + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 896c19e2cf314b4d1111afb3d89722a1e2878bd3 Mon Sep 17 00:00:00 2001 From: lyj Date: Sat, 5 Jun 2021 21:28:52 +0800 Subject: rc_mutex: update doc --- clippy_lints/src/types/mod.rs | 18 +++--------------- clippy_lints/src/types/rc_mutex.rs | 2 +- tests/ui/rc_mutex.stderr | 8 ++++---- 3 files changed, 8 insertions(+), 20 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 07dec2de827..30b04e4174c 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -254,22 +254,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for `Rc>`. /// - /// **Why is this bad?** `Rc>` may introduce a deadlock in single thread. Consider - /// using `Rc>` instead. - /// ```rust - /// fn main() { - /// use std::rc::Rc; - /// use std::sync::Mutex; - /// - /// let a: Rc> = Rc::new(Mutex::new(1)); - /// let a_clone = a.clone(); - /// let mut data = a.lock().unwrap(); - /// println!("{:?}", *a_clone.lock().unwrap()); - /// *data = 10; - /// } - /// ``` + /// **Why is this bad?** `Rc` is used in single thread and `Mutex` is used in multi thread. + /// Consider using `Rc>` in single thread or `Arc>` in multi thread. /// - /// **Known problems:** `Rc>` may panic in runtime. + /// **Known problems:** None. /// /// **Example:** /// ```rust,ignore diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index e8109f12324..bd7a0ee6408 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, RC_MUTEX, hir_ty.span, - "found `Rc>`. Consider using `Rc>` instead", + "found `Rc>`. Consider using `Rc>` or `Arc>` instead", ); return true; } diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index ab59337a960..c32780acf9f 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,4 +1,4 @@ -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:9:10 | LL | foo: Rc>, @@ -6,19 +6,19 @@ LL | foo: Rc>, | = note: `-D clippy::rc-mutex` implied by `-D warnings` -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:21:22 | LL | pub fn test1(foo: Rc>) {} | ^^^^^^^^^^^^ -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:23:19 | LL | pub fn test2(foo: Rc>) {} | ^^^^^^^^^^^^^^^^^ -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:25:19 | LL | pub fn test3(foo: Rc>>) {} -- cgit 1.4.1-3-g733a5 From 96747c1a460a241c42a9aa262eeb37b910f06760 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sat, 5 Jun 2021 14:27:36 +0200 Subject: Enhance semicolon_if_nothing_returned according to #7324 --- clippy_lints/src/semicolon_if_nothing_returned.rs | 20 +++++++++-- clippy_utils/src/lib.rs | 5 +++ tests/ui/semicolon_if_nothing_returned.rs | 44 +++++++++++++++++++++++ tests/ui/semicolon_if_nothing_returned.stderr | 14 +++++++- 4 files changed, 80 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 16e4d73851f..9e5d5b6e956 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{in_macro, sugg}; +use clippy_utils::{get_parent_expr_for_hir, in_macro, spans_on_same_line, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Block, ExprKind}; +use rustc_hir::{Block, BlockCheckMode, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -46,6 +46,22 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); if !snippet.ends_with('}'); then { + // check if the block is inside a closure or an unsafe block and don't + // emit if the block is on the same line + if_chain! { + if let Some(parent) = get_parent_expr_for_hir(cx, block.hir_id); + + if !matches!(block.rules, BlockCheckMode::DefaultBlock) || + matches!(parent.kind, ExprKind::Closure(..) | ExprKind::Block(..)); + + if block.stmts.len() == 0; + + if spans_on_same_line(cx, parent.span, expr.span); + then { + return; + } + } + // filter out the desugared `for` loop if let ExprKind::DropTemps(..) = &expr.kind { return; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 769836aaf18..be625eb2655 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -820,6 +820,11 @@ fn line_span(cx: &T, span: Span) -> Span { Span::new(line_start, span.hi(), span.ctxt()) } +/// Checks if two spans begin on the same line. +pub fn spans_on_same_line(cx: &T, left_span: Span, right_span: Span) -> bool { + line_span(cx, left_span).lo() == line_span(cx, right_span).lo() +} + /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { tcx.hir().parent_iter(id).next().map(|(_, node)| node) diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index 0abe2cca267..79ba7402f1f 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -17,6 +17,24 @@ fn basic101(x: i32) { y = x + 1 } +#[rustfmt::skip] +fn closure_error() { + let _d = || { + hello() + }; +} + +#[rustfmt::skip] +fn unsafe_checks_error() { + use std::mem::MaybeUninit; + use std::ptr; + + let mut s = MaybeUninit::::uninit(); + let _d = || unsafe { + ptr::drop_in_place(s.as_mut_ptr()) + }; +} + // this is fine fn print_sum(a: i32, b: i32) { println!("{}", a + b); @@ -53,3 +71,29 @@ fn loop_test(x: i32) { println!("{}", ext); } } + +fn closure() { + let _d = || hello(); +} + +#[rustfmt::skip] +fn closure_block() { + let _d = || { hello() }; +} + +unsafe fn some_unsafe_op() {} +unsafe fn some_other_unsafe_fn() {} + +fn do_something() { + unsafe { some_unsafe_op() }; + + unsafe { some_other_unsafe_fn() }; +} + +fn unsafe_checks() { + use std::mem::MaybeUninit; + use std::ptr; + + let mut s = MaybeUninit::::uninit(); + let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) }; +} diff --git a/tests/ui/semicolon_if_nothing_returned.stderr b/tests/ui/semicolon_if_nothing_returned.stderr index b73f8967538..e88ebe2ad35 100644 --- a/tests/ui/semicolon_if_nothing_returned.stderr +++ b/tests/ui/semicolon_if_nothing_returned.stderr @@ -18,5 +18,17 @@ error: consider adding a `;` to the last statement for consistent formatting LL | y = x + 1 | ^^^^^^^^^ help: add a `;` here: `y = x + 1;` -error: aborting due to 3 previous errors +error: consider adding a `;` to the last statement for consistent formatting + --> $DIR/semicolon_if_nothing_returned.rs:23:9 + | +LL | hello() + | ^^^^^^^ help: add a `;` here: `hello();` + +error: consider adding a `;` to the last statement for consistent formatting + --> $DIR/semicolon_if_nothing_returned.rs:34:9 + | +LL | ptr::drop_in_place(s.as_mut_ptr()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 790888d520859543cf8fce8b5a3226d71e719539 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sat, 5 Jun 2021 15:38:38 +0200 Subject: Fixed failing tests --- clippy_lints/src/semicolon_if_nothing_returned.rs | 38 +++++++++++++---------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 9e5d5b6e956..3698e548bd2 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -3,6 +3,7 @@ use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::{get_parent_expr_for_hir, in_macro, spans_on_same_line, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::Expr; use rustc_hir::{Block, BlockCheckMode, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -45,23 +46,8 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { if t_expr.is_unit(); if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); if !snippet.ends_with('}'); + if !check_if_inside_block_on_same_line(cx, block, expr); then { - // check if the block is inside a closure or an unsafe block and don't - // emit if the block is on the same line - if_chain! { - if let Some(parent) = get_parent_expr_for_hir(cx, block.hir_id); - - if !matches!(block.rules, BlockCheckMode::DefaultBlock) || - matches!(parent.kind, ExprKind::Closure(..) | ExprKind::Block(..)); - - if block.stmts.len() == 0; - - if spans_on_same_line(cx, parent.span, expr.span); - then { - return; - } - } - // filter out the desugared `for` loop if let ExprKind::DropTemps(..) = &expr.kind { return; @@ -82,3 +68,23 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { } } } + +/// Check if this block is inside a closure or an unsafe block or a normal on the same line. +fn check_if_inside_block_on_same_line<'tcx>( + cx: &LateContext<'tcx>, + block: &'tcx Block<'tcx>, + last_expr: &'tcx Expr<'_>, +) -> bool { + if_chain! { + if let Some(parent) = get_parent_expr_for_hir(cx, block.hir_id); + + if !matches!(block.rules, BlockCheckMode::DefaultBlock) || + matches!(parent.kind, ExprKind::Closure(..) | ExprKind::Block(..)); + + if block.stmts.is_empty(); + then { + return spans_on_same_line(cx, parent.span, last_expr.span); + } + } + false +} -- cgit 1.4.1-3-g733a5 From d7a380e4b9808cd62d3774f027ecf2395ef2b286 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 5 Jun 2021 15:11:44 +0900 Subject: Fix FP in `default_numeric_fallback` with external macro expansion --- clippy_lints/src/default_numeric_fallback.rs | 4 +- tests/ui/auxiliary/macro_rules.rs | 7 ++++ tests/ui/default_numeric_fallback.rs | 23 +++++++++++ tests/ui/default_numeric_fallback.stderr | 61 ++++++++++++++++------------ 4 files changed, 69 insertions(+), 26 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 759f7d4062d..a125376bffa 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -7,9 +7,10 @@ use rustc_hir::{ intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ hir::map::Map, + lint::in_external_macro, ty::{self, FloatTy, IntTy, PolyFnSig, Ty}, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -73,6 +74,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { /// Check whether a passed literal has potential to cause fallback or not. fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { if_chain! { + if !in_external_macro(self.cx.sess(), lit.span); if let Some(ty_bound) = self.ty_bounds.last(); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index d4470d3f407..170955e726c 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -106,3 +106,10 @@ macro_rules! field_reassign_with_default { } }; } + +#[macro_export] +macro_rules! default_numeric_fallback { + () => { + let x = 22; + }; +} diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 43468872db0..c0625fd1b75 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -1,3 +1,5 @@ +// aux-build:macro_rules.rs + #![warn(clippy::default_numeric_fallback)] #![allow(unused)] #![allow(clippy::never_loop)] @@ -5,6 +7,9 @@ #![allow(clippy::unnecessary_operation)] #![allow(clippy::branches_sharing_code)] +#[macro_use] +extern crate macro_rules; + mod basic_expr { fn test() { // Should lint unsuffixed literals typed `i32`. @@ -133,4 +138,22 @@ mod method_calls { } } +mod in_macro { + macro_rules! internal_macro { + () => { + let x = 22; + }; + } + + // Should lint in internal macro. + fn internal() { + internal_macro!(); + } + + // Should NOT lint in external macro. + fn external() { + default_numeric_fallback!(); + } +} + fn main() {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index d1c4c8203dd..5862cd936ac 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:17 + --> $DIR/default_numeric_fallback.rs:16:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,142 +7,153 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:18 + --> $DIR/default_numeric_fallback.rs:17:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:21 + --> $DIR/default_numeric_fallback.rs:17:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:24 + --> $DIR/default_numeric_fallback.rs:17:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:28 + --> $DIR/default_numeric_fallback.rs:18:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:31 + --> $DIR/default_numeric_fallback.rs:18:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:44 + --> $DIR/default_numeric_fallback.rs:18:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:47 + --> $DIR/default_numeric_fallback.rs:18:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:23 + --> $DIR/default_numeric_fallback.rs:19:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:13 + --> $DIR/default_numeric_fallback.rs:20:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:18 + --> $DIR/default_numeric_fallback.rs:20:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:16:18 + --> $DIR/default_numeric_fallback.rs:21:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:20:17 + --> $DIR/default_numeric_fallback.rs:25:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:38:21 + --> $DIR/default_numeric_fallback.rs:43:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:46:21 + --> $DIR/default_numeric_fallback.rs:51:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:52:21 + --> $DIR/default_numeric_fallback.rs:57:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:64:9 + --> $DIR/default_numeric_fallback.rs:69:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:70:27 + --> $DIR/default_numeric_fallback.rs:75:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:74:29 + --> $DIR/default_numeric_fallback.rs:79:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:88:21 + --> $DIR/default_numeric_fallback.rs:93:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:91:32 + --> $DIR/default_numeric_fallback.rs:96:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:109:28 + --> $DIR/default_numeric_fallback.rs:114:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:112:36 + --> $DIR/default_numeric_fallback.rs:117:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:132:23 + --> $DIR/default_numeric_fallback.rs:137:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` -error: aborting due to 24 previous errors +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:144:21 + | +LL | let x = 22; + | ^^ help: consider adding suffix: `22_i32` +... +LL | internal_macro!(); + | ------------------ in this macro invocation + | + = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From 6bf8303c4768e91deb3ba02f4c8a846166b5600b Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Mon, 7 Jun 2021 21:44:04 +0200 Subject: Refactored the check for two spans on the same line --- clippy_lints/src/semicolon_if_nothing_returned.rs | 6 ++++-- clippy_utils/src/lib.rs | 5 ----- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 3698e548bd2..eff6cdf36b4 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,6 +1,7 @@ +use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{get_parent_expr_for_hir, in_macro, spans_on_same_line, sugg}; +use clippy_utils::{get_parent_expr_for_hir, in_macro, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -83,7 +84,8 @@ fn check_if_inside_block_on_same_line<'tcx>( if block.stmts.is_empty(); then { - return spans_on_same_line(cx, parent.span, last_expr.span); + let source_map = cx.sess().source_map(); + return !source_map.is_multiline(parent.span.to(last_expr.span)); } } false diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index be625eb2655..769836aaf18 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -820,11 +820,6 @@ fn line_span(cx: &T, span: Span) -> Span { Span::new(line_start, span.hi(), span.ctxt()) } -/// Checks if two spans begin on the same line. -pub fn spans_on_same_line(cx: &T, left_span: Span, right_span: Span) -> bool { - line_span(cx, left_span).lo() == line_span(cx, right_span).lo() -} - /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { tcx.hir().parent_iter(id).next().map(|(_, node)| node) -- cgit 1.4.1-3-g733a5 From 967d815a426dd16354dd42c96ad4b3f2eb036f09 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 7 Jun 2021 22:21:56 +0200 Subject: Extracting `is_expr_identity_function` into `clippy_utils` for reusability --- clippy_lints/src/map_identity.rs | 54 ++-------------------------------------- clippy_utils/src/lib.rs | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 52 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 41cda23510e..9bcb010ff6d 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks}; +use clippy_utils::{is_expr_identity_function, is_trait_method}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -74,53 +74,3 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a } } } - -/// 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(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::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(ret_val)) => match_expr_param(cx, ret_val, params[0].pat), - ExprKind::Block(block, _) => { - if_chain! { - if block.stmts.len() == 1; - if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind; - if let ExprKind::Ret(Some(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.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr)) - } else { - false - } -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 769836aaf18..a765abe6b76 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1399,6 +1399,60 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some()) } +/// Checks if an expression represents the identity function +/// Only examines closures and `std::convert::identity` +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + /// Returns true if the expression is a binding to the given pattern + fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool { + if let PatKind::Binding(_, _, ident, _) = pat.kind { + if match_var(expr, ident.name) { + return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr)); + } + } + + 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 }` + /// * `|x| { return x; }` + fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { + let body = remove_blocks(&func.value); + + let value_pat = if let [value_param] = func.params { + value_param.pat + } else { + return false; + }; + + match body.kind { + ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat), + ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat), + ExprKind::Block(block, _) => { + if_chain! { + if let &[block_stmt] = &block.stmts; + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind; + if let ExprKind::Ret(Some(ret_val)) = expr.kind; + then { + is_expr_pat_binding(cx, ret_val, value_pat) + } else { + false + } + } + }, + _ => false, + } + } + + match expr.kind { + ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), + ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY), + _ => false, + } +} + /// Gets the node where an expression is either used, or it's type is unified with another branch. pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { let map = tcx.hir(); -- cgit 1.4.1-3-g733a5 From bb3b58cfccdfbc4f95a2f03dc800aa87fd3fdd2c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 7 Jun 2021 23:10:42 +0200 Subject: Reuse `is_expr_identity_function` for `flat_map_identity` --- clippy_lints/src/methods/flat_map_identity.rs | 44 +++++++-------------------- tests/ui/flat_map_identity.fixed | 5 ++- tests/ui/flat_map_identity.rs | 5 ++- tests/ui/flat_map_identity.stderr | 12 ++++++-- 4 files changed, 28 insertions(+), 38 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 25f8434cb94..6f911d79d0b 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_path_def_path, is_trait_method, paths}; -use if_chain::if_chain; +use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -15,36 +14,15 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) { - let apply_lint = |message: &str| { - span_lint_and_sugg( - cx, - FLAT_MAP_IDENTITY, - flat_map_span.with_hi(expr.span.hi()), - message, - "try", - "flatten()".to_string(), - Applicability::MachineApplicable, - ); - }; - - if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind; - let body = cx.tcx.hir().body(body_id); - - if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; - - if path.segments.len() == 1; - if path.segments[0].ident.name == binding_ident.name; - - then { - apply_lint("called `flat_map(|x| x)` on an `Iterator`"); - } - } - - if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } + if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) { + span_lint_and_sugg( + cx, + FLAT_MAP_IDENTITY, + flat_map_span.with_hi(expr.span.hi()), + "use of `flat_map` with an identity function", + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/tests/ui/flat_map_identity.fixed b/tests/ui/flat_map_identity.fixed index dfe3bd47e13..1f4b880ef5b 100644 --- a/tests/ui/flat_map_identity.fixed +++ b/tests/ui/flat_map_identity.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::flat_map_identity)] use std::convert; @@ -11,4 +11,7 @@ fn main() { let iterator = [[0, 1], [2, 3], [4, 5]].iter(); let _ = iterator.flatten(); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); } diff --git a/tests/ui/flat_map_identity.rs b/tests/ui/flat_map_identity.rs index 393b9569255..de14a06d4e6 100644 --- a/tests/ui/flat_map_identity.rs +++ b/tests/ui/flat_map_identity.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::flat_map_identity)] use std::convert; @@ -11,4 +11,7 @@ fn main() { let iterator = [[0, 1], [2, 3], [4, 5]].iter(); let _ = iterator.flat_map(convert::identity); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(|x| return x); } diff --git a/tests/ui/flat_map_identity.stderr b/tests/ui/flat_map_identity.stderr index e4686ae5a54..e776c9fdf51 100644 --- a/tests/ui/flat_map_identity.stderr +++ b/tests/ui/flat_map_identity.stderr @@ -1,4 +1,4 @@ -error: called `flat_map(|x| x)` on an `Iterator` +error: use of `flat_map` with an identity function --> $DIR/flat_map_identity.rs:10:22 | LL | let _ = iterator.flat_map(|x| x); @@ -6,11 +6,17 @@ LL | let _ = iterator.flat_map(|x| x); | = note: `-D clippy::flat-map-identity` implied by `-D warnings` -error: called `flat_map(std::convert::identity)` on an `Iterator` +error: use of `flat_map` with an identity function --> $DIR/flat_map_identity.rs:13:22 | LL | let _ = iterator.flat_map(convert::identity); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: aborting due to 2 previous errors +error: use of `flat_map` with an identity function + --> $DIR/flat_map_identity.rs:16:22 + | +LL | let _ = iterator.flat_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 9e54ce865c67af65370e1ec3822742a22fd20dfe Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 7 Jun 2021 23:31:17 +0200 Subject: Reuse `is_expr_identity_function` for `filter_map_identity` --- clippy_lints/src/methods/filter_map_identity.rs | 40 +++++++------------------ tests/ui/filter_map_identity.fixed | 5 +++- tests/ui/filter_map_identity.rs | 5 +++- tests/ui/filter_map_identity.stderr | 14 ++++++--- 4 files changed, 29 insertions(+), 35 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 403fe8d3546..d1b5e945dfd 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths}; -use if_chain::if_chain; +use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,32 +8,15 @@ use rustc_span::{source_map::Span, sym}; use super::FILTER_MAP_IDENTITY; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) { - let apply_lint = |message: &str| { - span_lint_and_sugg( - cx, - FILTER_MAP_IDENTITY, - filter_map_span.with_hi(expr.span.hi()), - message, - "try", - "flatten()".to_string(), - Applicability::MachineApplicable, - ); - }; - - if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind; - let body = cx.tcx.hir().body(body_id); - - if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind; - if path_to_local_id(&body.value, binding_id); - then { - apply_lint("called `filter_map(|x| x)` on an `Iterator`"); - } - } - - if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) { - apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); - } + if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) { + span_lint_and_sugg( + cx, + FILTER_MAP_IDENTITY, + filter_map_span.with_hi(expr.span.hi()), + "use of `filter_map` with an identity function", + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/tests/ui/filter_map_identity.fixed b/tests/ui/filter_map_identity.fixed index 23ce28d8e9b..a5860aa49b3 100644 --- a/tests/ui/filter_map_identity.fixed +++ b/tests/ui/filter_map_identity.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::filter_map_identity)] fn main() { @@ -13,4 +13,7 @@ fn main() { use std::convert::identity; let iterator = vec![Some(1), None, Some(2)].into_iter(); let _ = iterator.flatten(); + + let iterator = vec![Some(1), None, Some(2)].into_iter(); + let _ = iterator.flatten(); } diff --git a/tests/ui/filter_map_identity.rs b/tests/ui/filter_map_identity.rs index e698df13eea..7e998b9cdf7 100644 --- a/tests/ui/filter_map_identity.rs +++ b/tests/ui/filter_map_identity.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::filter_map_identity)] fn main() { @@ -13,4 +13,7 @@ fn main() { use std::convert::identity; let iterator = vec![Some(1), None, Some(2)].into_iter(); let _ = iterator.filter_map(identity); + + let iterator = vec![Some(1), None, Some(2)].into_iter(); + let _ = iterator.filter_map(|x| return x); } diff --git a/tests/ui/filter_map_identity.stderr b/tests/ui/filter_map_identity.stderr index 596a6320608..43c9fdca4fb 100644 --- a/tests/ui/filter_map_identity.stderr +++ b/tests/ui/filter_map_identity.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(|x| x)` on an `Iterator` +error: use of `filter_map` with an identity function --> $DIR/filter_map_identity.rs:8:22 | LL | let _ = iterator.filter_map(|x| x); @@ -6,17 +6,23 @@ LL | let _ = iterator.filter_map(|x| x); | = note: `-D clippy::filter-map-identity` implied by `-D warnings` -error: called `filter_map(std::convert::identity)` on an `Iterator` +error: use of `filter_map` with an identity function --> $DIR/filter_map_identity.rs:11:22 | LL | let _ = iterator.filter_map(std::convert::identity); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: called `filter_map(std::convert::identity)` on an `Iterator` +error: use of `filter_map` with an identity function --> $DIR/filter_map_identity.rs:15:22 | LL | let _ = iterator.filter_map(identity); | ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: aborting due to 3 previous errors +error: use of `filter_map` with an identity function + --> $DIR/filter_map_identity.rs:18:22 + | +LL | let _ = iterator.filter_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 5336f88403aae270e1e8bbf0aee31707311c590f Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 8 Jun 2021 00:14:43 +0200 Subject: Move `map_identity` into the `methods` module --- clippy_lints/src/lib.rs | 8 ++-- clippy_lints/src/map_identity.rs | 76 -------------------------------- clippy_lints/src/methods/map_identity.rs | 38 ++++++++++++++++ clippy_lints/src/methods/mod.rs | 26 +++++++++++ 4 files changed, 67 insertions(+), 81 deletions(-) delete mode 100644 clippy_lints/src/map_identity.rs create mode 100644 clippy_lints/src/methods/map_identity.rs (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7dd3952b3a..c6d9b378603 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -254,7 +254,6 @@ mod manual_strip; mod manual_unwrap_or; mod map_clone; mod map_err_ignore; -mod map_identity; mod map_unit_fn; mod match_on_vec_items; mod matches; @@ -705,7 +704,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: manual_unwrap_or::MANUAL_UNWRAP_OR, 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, match_on_vec_items::MATCH_ON_VEC_ITEMS, @@ -765,6 +763,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::MANUAL_STR_REPEAT, methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_FLATTEN, + methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -1260,7 +1259,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), @@ -1301,6 +1299,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), + LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), LintId::of(methods::OPTION_AS_REF_DEREF), @@ -1586,7 +1585,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), LintId::of(matches::MATCH_AS_REF), @@ -1601,6 +1599,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::ITER_COUNT), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::MAP_IDENTITY), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::SEARCH_IS_SOME), @@ -2039,7 +2038,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: single_char_binding_names_threshold, }); 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); diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs deleted file mode 100644 index 9bcb010ff6d..00000000000 --- a/clippy_lints/src/map_identity.rs +++ /dev/null @@ -1,76 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_identity_function, is_trait_method}; -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::sym; - -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<'tcx> LateLintPass<'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(method, _, args, _) = expr.kind; - if args.len() == 2 && method.ident.name == sym::map; - let caller_ty = cx.typeck_results().expr_ty(&args[0]); - if is_trait_method(cx, expr, sym::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 - } - } -} diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs new file mode 100644 index 00000000000..538a12566e3 --- /dev/null +++ b/clippy_lints/src/methods/map_identity.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_expr_identity_function, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::{source_map::Span, sym}; + +use super::MAP_IDENTITY; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + caller: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, + _map_span: Span, +) { + let caller_ty = cx.typeck_results().expr_ty(caller); + + if_chain! { + if is_trait_method(cx, expr, sym::Iterator) + || is_type_diagnostic_item(cx, caller_ty, sym::result_type) + || is_type_diagnostic_item(cx, caller_ty, sym::option_type); + if is_expr_identity_function(cx, map_arg); + if let Some(sugg_span) = expr.span.trim_start(caller.span); + then { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + sugg_span, + "unnecessary map of the identity function", + "remove the call to `map`", + String::new(), + Applicability::MachineApplicable, + ) + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c8ae972f18c..d4f8cef4f37 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -35,6 +35,7 @@ mod manual_saturating_arithmetic; mod manual_str_repeat; mod map_collect_result_unit; mod map_flatten; +mod map_identity; mod map_unwrap_or; mod ok_expect; mod option_as_ref_deref; @@ -1561,6 +1562,29 @@ declare_clippy_lint! { "call to `filter_map` where `flatten` is sufficient" } +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_clippy_lint! { /// **What it does:** Checks for the use of `.bytes().nth()`. /// @@ -1728,6 +1752,7 @@ impl_lint_pass!(Methods => [ FILTER_NEXT, SKIP_WHILE_NEXT, FILTER_MAP_IDENTITY, + MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, @@ -2058,6 +2083,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio _ => {}, } } + map_identity::check(cx, expr, recv, m_arg, span); }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), ("next", []) => { -- cgit 1.4.1-3-g733a5 From fe549f711a002ddf6a509ff0bd4b17d271a7c1df Mon Sep 17 00:00:00 2001 From: lyj Date: Wed, 9 Jun 2021 15:25:48 +0800 Subject: redundant_clone: fix comment --- clippy_lints/src/redundant_clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 19650c41b84..380557c81a1 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -132,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { } } - // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }` + // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }` let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); let loc = mir::Location { -- cgit 1.4.1-3-g733a5 From 5ec80f32c75eae1fd27389ce0556ec4011144498 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Wed, 9 Jun 2021 09:39:11 +0200 Subject: Refactored multiline check --- clippy_lints/src/semicolon_if_nothing_returned.rs | 28 +++-------------------- 1 file changed, 3 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index eff6cdf36b4..da3e30af35c 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,11 +1,10 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{get_parent_expr_for_hir, in_macro, sugg}; +use clippy_utils::{in_macro, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_hir::{Block, BlockCheckMode, ExprKind}; +use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -47,7 +46,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { if t_expr.is_unit(); if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); if !snippet.ends_with('}'); - if !check_if_inside_block_on_same_line(cx, block, expr); + if cx.sess().source_map().is_multiline(block.span); then { // filter out the desugared `for` loop if let ExprKind::DropTemps(..) = &expr.kind { @@ -69,24 +68,3 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { } } } - -/// Check if this block is inside a closure or an unsafe block or a normal on the same line. -fn check_if_inside_block_on_same_line<'tcx>( - cx: &LateContext<'tcx>, - block: &'tcx Block<'tcx>, - last_expr: &'tcx Expr<'_>, -) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr_for_hir(cx, block.hir_id); - - if !matches!(block.rules, BlockCheckMode::DefaultBlock) || - matches!(parent.kind, ExprKind::Closure(..) | ExprKind::Block(..)); - - if block.stmts.is_empty(); - then { - let source_map = cx.sess().source_map(); - return !source_map.is_multiline(parent.span.to(last_expr.span)); - } - } - false -} -- cgit 1.4.1-3-g733a5 From ea45e2a9cf13b59e6366a6cacb7f0088f9cadeda Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Wed, 2 Jun 2021 07:20:45 -0400 Subject: Add disallowed_types lint, this adds a field to the Conf struct Replace use of node_type -> node_type_opt, fix clippy warnings Don't walk the hir unnecessarily let the visitor do it --- CHANGELOG.md | 1 + clippy_lints/src/disallowed_type.rs | 122 +++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + clippy_lints/src/utils/conf.rs | 2 + tests/ui-toml/toml_disallowed_type/clippy.toml | 9 ++ .../toml_disallowed_type/conf_disallowed_type.rs | 35 ++++++ .../conf_disallowed_type.stderr | 88 +++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 8 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/disallowed_type.rs create mode 100644 tests/ui-toml/toml_disallowed_type/clippy.toml create mode 100644 tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs create mode 100644 tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 41af8e190dd..13f0fc8f609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2358,6 +2358,7 @@ Released 2018-09-13 [`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 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`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_type.rs b/clippy_lints/src/disallowed_type.rs new file mode 100644 index 00000000000..f8ac7d76d8f --- /dev/null +++ b/clippy_lints/src/disallowed_type.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{def::Res, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{Span, Symbol}; + +declare_clippy_lint! { + /// **What it does:** Denies the configured types in clippy.toml. + /// + /// **Why is this bad?** Some types are undesirable in certain contexts. + /// + /// **Known problems:** The fully qualified path must be used. This lint + /// doesn't support aliases or reexported names; be aware that many types + /// in `std` are actually reexports. + /// + /// For example, if you want to disallow `BTreeMap`, your clippy.toml + /// configuration would look like + /// `disallowed-methods = ["alloc::collections::btree::map::BTreeMap"]` and not + /// `disallowed-methods = ["std::collections::BTreeMap"]` as you might expect. + /// + /// N.B. There is no way to ban primitive types. + /// + /// **Example:** + /// + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-methods = ["alloc::collections::btree::map::BTreeMap"] + /// ``` + /// + /// ```rust,ignore + /// use std::collections::BTreeMap; + /// // or its use + /// let x = std::collections::BTreeMap::new(); + /// ``` + /// Use instead: + /// ```rust,ignore + /// // A similar type that is allowed by the config + /// use std::collections::HashMap; + /// ``` + pub DISALLOWED_TYPE, + nursery, + "use of a disallowed type" +} +#[derive(Clone, Debug)] +pub struct DisallowedType { + disallowed: FxHashSet>, +} + +impl DisallowedType { + pub fn new(disallowed: &FxHashSet) -> Self { + Self { + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); + +impl<'tcx> LateLintPass<'tcx> for DisallowedType { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if_chain! { + if let ItemKind::Use(path, UseKind::Single) = &item.kind; + if let Res::Def(_, id) = path.res; + let use_path = cx.get_def_path(id); + if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + then { + emit(cx, name, item.span,); + } + } + } + + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + if_chain! { + if let TyKind::Path(path) = &ty.kind; + if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id(); + let use_path = cx.get_def_path(did); + if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + then { + emit(cx, name, path.span()); + } + } + } + + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) { + if_chain! { + if let Res::Def(_, did) = poly.trait_ref.path.res; + let use_path = cx.get_def_path(did); + if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + then { + emit(cx, name, poly.trait_ref.path.span); + } + } + } + + // TODO: if non primitive const generics are a thing + // fn check_generic_arg(&mut self, cx: &LateContext<'tcx>, arg: &'tcx GenericArg<'tcx>) { + // match arg { + // GenericArg::Const(c) => {}, + // } + // } + // fn check_generic_param(&mut self, cx: &LateContext<'tcx>, param: &'tcx GenericParam<'tcx>) { + // match param.kind { + // GenericParamKind::Const { .. } => {}, + // } + // } +} + +fn emit(cx: &LateContext<'_>, name: &[Symbol], span: Span) { + let name = name.iter().map(|s| s.to_ident_string()).collect::>().join("::"); + span_lint( + cx, + DISALLOWED_TYPE, + span, + &format!("`{}` is not allowed according to config", name), + ); +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7dd3952b3a..41c635d9e93 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -187,6 +187,7 @@ mod default_numeric_fallback; mod dereference; mod derive; mod disallowed_method; +mod disallowed_type; mod doc; mod double_comparison; mod double_parens; @@ -583,6 +584,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, disallowed_method::DISALLOWED_METHOD, + disallowed_type::DISALLOWED_TYPE, doc::DOC_MARKDOWN, doc::MISSING_ERRORS_DOC, doc::MISSING_PANICS_DOC, @@ -1761,6 +1763,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(disallowed_method::DISALLOWED_METHOD), + LintId::of(disallowed_type::DISALLOWED_TYPE), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), @@ -2066,6 +2069,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_late_pass(|| box unused_async::UnusedAsync); + let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types)); } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0e33ae740d9..e7d901aa9f9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -190,6 +190,8 @@ define_Conf! { (warn_on_all_wildcard_imports: bool = false), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. (disallowed_methods: Vec = Vec::new()), + /// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths. + (disallowed_types: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. (unreadable_literal_lint_fractions: bool = true), /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other diff --git a/tests/ui-toml/toml_disallowed_type/clippy.toml b/tests/ui-toml/toml_disallowed_type/clippy.toml new file mode 100644 index 00000000000..57dfdc584bb --- /dev/null +++ b/tests/ui-toml/toml_disallowed_type/clippy.toml @@ -0,0 +1,9 @@ +disallowed-types = [ + "std::collections::hash::map::HashMap", + "core::sync::atomic::AtomicU32", + "syn::ty::TypePath", + "proc_macro2::Ident", + "std::thread::Thread", + "std::time::Instant", + "std::io::Read", +] diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs new file mode 100644 index 00000000000..567afb5aec1 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs @@ -0,0 +1,35 @@ +#![warn(clippy::disallowed_type)] + +extern crate quote; +extern crate syn; + +use std::sync as foo; +use std::sync::atomic::AtomicU32; +use std::time::Instant as Sneaky; + +struct HashMap; + +fn bad_return_type() -> fn() -> Sneaky { + todo!() +} + +fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { + todo!() +} + +fn trait_obj(_: &dyn std::io::Read) { + todo!() +} + +static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut()); + +#[allow(clippy::diverging_sub_expression)] +fn main() { + let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); + let _ = Sneaky::now(); + let _ = foo::atomic::AtomicU32::new(0); + static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); + let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); + let _ = syn::Ident::new("", todo!()); + let _ = HashMap; +} diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr new file mode 100644 index 00000000000..e5ea23cc0bc --- /dev/null +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr @@ -0,0 +1,88 @@ +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:7:1 + | +LL | use std::sync::atomic::AtomicU32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-type` implied by `-D warnings` + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:8:1 + | +LL | use std::time::Instant as Sneaky; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:12:33 + | +LL | fn bad_return_type() -> fn() -> Sneaky { + | ^^^^^^ + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:16:28 + | +LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { + | ^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:16:39 + | +LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::io::Read` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:20:22 + | +LL | fn trait_obj(_: &dyn std::io::Read) { + | ^^^^^^^^^^^^^ + +error: `std::collections::hash::map::HashMap` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:28:48 + | +LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::collections::hash::map::HashMap` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:28:12 + | +LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:29:13 + | +LL | let _ = Sneaky::now(); + | ^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:30:13 + | +LL | let _ = foo::atomic::AtomicU32::new(0); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:31:17 + | +LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:31:48 + | +LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `syn::ty::TypePath` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:32:43 + | +LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); + | ^^^^^^^^^^^^^ + +error: `proc_macro2::Ident` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:33:13 + | +LL | let _ = syn::Ident::new("", todo!()); + | ^^^^^^^^^^ + +error: aborting due to 14 previous errors + 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 a7be00426c4..06d70b66fda 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 `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From b792bb301c66e271d88d2e579ec79fec153b7b89 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 9 Jun 2021 09:17:34 -0500 Subject: Factor out match_var --- clippy_lints/src/bytecount.rs | 70 ++++++++++---------------- clippy_lints/src/collapsible_match.rs | 20 ++------ clippy_lints/src/manual_map.rs | 20 ++++---- clippy_utils/src/lib.rs | 94 +++++++++++++++-------------------- clippy_utils/src/ptr.rs | 32 ++++++------ 5 files changed, 92 insertions(+), 144 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 877ae002d36..4f7ffdcdfb4 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,15 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; -use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path}; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_span::Symbol; declare_clippy_lint! { /// **What it does:** Checks for naive byte counts @@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]); impl<'tcx> LateLintPass<'tcx> for ByteCount { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind; + if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind; if count.ident.name == sym!(count); - if count_args.len() == 1; - if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind; + if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind; if filter.ident.name == sym!(filter); - if filter_args.len() == 2; - if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind; + if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind; let body = cx.tcx.hir().body(body_id); - if body.params.len() == 1; - if let Some(argname) = get_pat_name(body.params[0].pat); + if let [param] = body.params; + if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind; if let ExprKind::Binary(ref op, l, r) = body.value.kind; if op.node == BinOpKind::Eq; if match_type(cx, - cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), + cx.typeck_results().expr_ty(filter_recv).peel_refs(), &paths::SLICE_ITER); + let operand_is_arg = |expr| { + let expr = peel_ref_operators(cx, remove_blocks(expr)); + path_to_local_id(expr, arg_id) + }; + let needle = if operand_is_arg(l) { + r + } else if operand_is_arg(r) { + l + } else { + return; + }; + if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); + if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle); then { - let needle = match get_path_name(l) { - Some(name) if check_arg(name, argname, r) => r, - _ => match get_path_name(r) { - Some(name) if check_arg(name, argname, l) => l, - _ => { return; } - } - }; - if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() { - return; - } let haystack = if let ExprKind::MethodCall(path, _, args, _) = - filter_args[0].kind { + filter_recv.kind { let p = path.ident.name; if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { &args[0] } else { - &filter_args[0] + &filter_recv } } else { - &filter_args[0] + &filter_recv }; let mut applicability = Applicability::MaybeIncorrect; span_lint_and_sugg( @@ -91,24 +92,3 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { }; } } - -fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { - name == arg && !contains_name(name, needle) -} - -fn get_path_name(expr: &Expr<'_>) -> Option { - match expr.kind { - ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => { - get_path_name(e) - }, - ExprKind::Block(b, _) => { - if b.stmts.is_empty() { - b.expr.as_ref().and_then(|p| get_path_name(p)) - } else { - None - } - }, - ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), - _ => None, - } -} diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index ab22578abd6..a6c3a5b0e83 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq}; +use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -73,7 +72,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext if arms_inner.iter().all(|arm| arm.guard.is_none()); // match expression must be a local binding // match { .. } - if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results())); + if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in)); // one of the branches must be "wild-like" if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner)); let (wild_inner_arm, non_wild_inner_arm) = @@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool { }); result } - -/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is -/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed. -fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> { - loop { - match expr.kind { - ExprKind::AddrOf(_, _, e) => expr = e, - ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e, - _ => break, - } - } - expr -} diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 0b873534f2c..97e4a983f32 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -3,19 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ - can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs, + can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id, + peel_hir_expr_refs, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{ - symbol::{sym, Ident}, - SyntaxContext, -}; +use rustc_span::{sym, SyntaxContext}; declare_clippy_lint! { /// **What it does:** Checks for usages of `match` which could be implemented using `map` @@ -141,13 +139,13 @@ impl LateLintPass<'_> for ManualMap { scrutinee_str.into() }; - let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind { - match can_pass_as_func(cx, some_binding, some_expr) { + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + match can_pass_as_func(cx, id, some_expr) { Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() }, _ => { - if match_var(some_expr, some_binding.name) + if path_to_local_id(some_expr, id) && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { @@ -199,10 +197,10 @@ impl LateLintPass<'_> for ManualMap { // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. -fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() => + if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() => { Some(func) }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d3efd32d727..66e75b0c206 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, - PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -326,16 +326,6 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) .map_or(false, |did| is_diag_trait_item(cx, did, diag_item)) } -/// Checks if an expression references a variable of the given name. -pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { - if let [p] = path.segments { - return p.ident.name == var; - } - } - false -} - pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), @@ -707,16 +697,6 @@ 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 { - match pat.kind { - PatKind::Binding(.., ref spname, _) => Some(spname.name), - PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), - PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p), - _ => None, - } -} - pub struct ContainsName { pub name: Symbol, pub result: bool, @@ -1404,47 +1384,42 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks if an expression represents the identity function /// Only examines closures and `std::convert::identity` pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Returns true if the expression is a binding to the given pattern - fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool { - if let PatKind::Binding(_, _, ident, _) = pat.kind { - if match_var(expr, ident.name) { - return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr)); - } - } - - 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 }` /// * `|x| { return x; }` fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { - let body = remove_blocks(&func.value); - - let value_pat = if let [value_param] = func.params { - value_param.pat - } else { - return false; + let id = if_chain! { + if let [param] = func.params; + if let PatKind::Binding(_, id, _, _) = param.pat.kind; + then { + id + } else { + return false; + } }; - match body.kind { - ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat), - ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat), - ExprKind::Block(block, _) => { - if_chain! { - if let &[block_stmt] = &block.stmts; - if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind; - if let ExprKind::Ret(Some(ret_val)) = expr.kind; - then { - is_expr_pat_binding(cx, ret_val, value_pat) - } else { - false + let mut expr = &func.value; + loop { + match expr.kind { + #[rustfmt::skip] + ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, ) + | ExprKind::Ret(Some(e)) => expr = e, + #[rustfmt::skip] + ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => { + if_chain! { + if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind; + if let ExprKind::Ret(Some(ret_val)) = e.kind; + then { + expr = ret_val; + } else { + return false; + } } - } - }, - _ => false, + }, + _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(), + } } } @@ -1710,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { (e, count) } +/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is +/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed. +pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { + loop { + match expr.kind { + ExprKind::AddrOf(_, _, e) => expr = e, + ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e, + _ => break, + } + } + expr +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 791688cd194..8adb6915952 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,10 +1,10 @@ use crate::source::snippet; -use crate::{get_pat_name, match_var}; +use crate::{path_to_local_id, strip_pat_refs}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; +use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use std::borrow::Cow; pub fn get_spans( @@ -14,10 +14,11 @@ pub fn get_spans( replacements: &[(&'static str, &'static str)], ) -> Option)>> { if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) { - get_binding_name(&body.params[idx]).map_or_else( - || Some(vec![]), - |name| extract_clone_suggestions(cx, name, replacements, body), - ) + if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { + extract_clone_suggestions(cx, binding_id, replacements, body) + } else { + Some(vec![]) + } } else { Some(vec![]) } @@ -25,13 +26,13 @@ pub fn get_spans( fn extract_clone_suggestions<'tcx>( cx: &LateContext<'tcx>, - name: Symbol, + id: HirId, replace: &[(&'static str, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { let mut visitor = PtrCloneVisitor { cx, - name, + id, replace, spans: vec![], abort: false, @@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>( struct PtrCloneVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - name: Symbol, + id: HirId, replace: &'a [(&'static str, &'static str)], spans: Vec<(Span, Cow<'static, str>)>, abort: bool, @@ -55,16 +56,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { if self.abort { return; } - if let ExprKind::MethodCall(seg, _, args, _) = expr.kind { - if args.len() == 1 && match_var(&args[0], self.name) { + if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind { + if path_to_local_id(recv, self.id) { if seg.ident.name.as_str() == "capacity" { self.abort = true; return; } for &(fn_name, suffix) in self.replace { if seg.ident.name.as_str() == fn_name { - self.spans - .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix)); + self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix)); return; } } @@ -77,7 +77,3 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { NestedVisitorMap::None } } - -fn get_binding_name(arg: &Param<'_>) -> Option { - get_pat_name(arg.pat) -} -- cgit 1.4.1-3-g733a5 From 0d3f289b66791912b976c6757a91842843b2c217 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Wed, 9 Jun 2021 17:16:10 +0200 Subject: Add FreeBSD as identifier not needing ticks For the doc-markdown lint. --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/doc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0e33ae740d9..d7744c09c11 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -149,7 +149,7 @@ define_Conf! { "WebGL", "TensorFlow", "TrueType", - "iOS", "macOS", + "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index c946a047f1b..8afef6b23d4 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -64,7 +64,7 @@ fn test_units() { /// WebGL /// TensorFlow /// TrueType -/// iOS macOS +/// iOS macOS FreeBSD /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) -- cgit 1.4.1-3-g733a5 From 44608b1857951ba82b630853da6fc6c2f40fde53 Mon Sep 17 00:00:00 2001 From: valentine-mario Date: Thu, 10 Jun 2021 09:12:06 +0100 Subject: added lint to check for full range of vector and suggest append --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ .../src/methods/append_instead_of_extend.rs | 41 ++++++++++++++++ clippy_lints/src/methods/mod.rs | 33 ++++++++++++- tests/ui/append_instead_of_extend.fixed | 55 ++++++++++++++++++++++ tests/ui/append_instead_of_extend.rs | 55 ++++++++++++++++++++++ tests/ui/append_instead_of_extend.stderr | 22 +++++++++ 7 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/methods/append_instead_of_extend.rs create mode 100644 tests/ui/append_instead_of_extend.fixed create mode 100644 tests/ui/append_instead_of_extend.rs create mode 100644 tests/ui/append_instead_of_extend.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f0fc8f609..52d0e8c7cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2295,6 +2295,7 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped +[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 591bf68f927..5ab333f8aa1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -730,6 +730,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::APPEND_INSTEAD_OF_EXTEND, methods::BIND_INSTEAD_OF_MAP, methods::BYTES_NTH, methods::CHARS_LAST_CMP, @@ -1276,6 +1277,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::APPEND_INSTEAD_OF_EXTEND), LintId::of(methods::BIND_INSTEAD_OF_MAP), LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), @@ -1736,6 +1738,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(methods::APPEND_INSTEAD_OF_EXTEND), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::ITER_NTH), LintId::of(methods::MANUAL_STR_REPEAT), diff --git a/clippy_lints/src/methods/append_instead_of_extend.rs b/clippy_lints/src/methods/append_instead_of_extend.rs new file mode 100644 index 00000000000..e39a5a1efd1 --- /dev/null +++ b/clippy_lints/src/methods/append_instead_of_extend.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::APPEND_INSTEAD_OF_EXTEND; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if_chain! { + if is_type_diagnostic_item(cx, ty, sym::vec_type); + //check source object + if let ExprKind::MethodCall(src_method, _, [drain_vec, drain_arg], _) = &arg.kind; + if src_method.ident.as_str() == "drain"; + if let src_ty = cx.typeck_results().expr_ty(drain_vec).peel_refs(); + if is_type_diagnostic_item(cx, src_ty, sym::vec_type); + //check drain range + if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs(); + if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + APPEND_INSTEAD_OF_EXTEND, + expr.span, + "use of `extend` instead of `append` for adding the full range of a second vector", + "try this", + format!( + "{}.append(&mut {})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) + ), + applicability, + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d4f8cef4f37..b556dcb4dfe 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,3 +1,4 @@ +mod append_instead_of_extend; mod bind_instead_of_map; mod bytes_nth; mod chars_cmp; @@ -1032,6 +1033,30 @@ declare_clippy_lint! { "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead" } +declare_clippy_lint! { + /// **What it does:** Checks for occurrences where one vector gets extended instead of append + /// + /// **Why is this bad?** Using `append` instead of `extend` is more concise and faster + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut a = vec![1, 2, 3]; + /// let mut b = vec![4, 5, 6]; + /// + /// // Bad + /// a.extend(b.drain(..)); + /// + /// // Good + /// a.append(&mut b); + /// ``` + pub APPEND_INSTEAD_OF_EXTEND, + perf, + "using vec.append(&mut vec) to move the full range of a vecor to another" +} + declare_clippy_lint! { /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a /// `&str` or `String`. @@ -1785,7 +1810,8 @@ impl_lint_pass!(Methods => [ INSPECT_FOR_EACH, IMPLICIT_CLONE, SUSPICIOUS_SPLITN, - MANUAL_STR_REPEAT + MANUAL_STR_REPEAT, + APPEND_INSTEAD_OF_EXTEND ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2047,7 +2073,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv), }, - ("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg), + ("extend", [arg]) => { + string_extend_chars::check(cx, expr, recv, arg); + append_instead_of_extend::check(cx, expr, recv, arg); + }, ("filter_map", [arg]) => { unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); diff --git a/tests/ui/append_instead_of_extend.fixed b/tests/ui/append_instead_of_extend.fixed new file mode 100644 index 00000000000..283358333cd --- /dev/null +++ b/tests/ui/append_instead_of_extend.fixed @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::append_instead_of_extend)] +use std::collections::BinaryHeap; +fn main() { + //gets linted + let mut vec1 = vec![0u8; 1024]; + let mut vec2: std::vec::Vec = Vec::new(); + + vec2.append(&mut vec1); + + let mut vec3 = vec![0u8; 1024]; + let mut vec4: std::vec::Vec = Vec::new(); + + vec4.append(&mut vec3); + + let mut vec11: std::vec::Vec = Vec::new(); + + vec11.append(&mut return_vector()); + + //won't get linted it dosen't move the entire content of a vec into another + let mut test1 = vec![0u8, 10]; + let mut test2: std::vec::Vec = Vec::new(); + + test2.extend(test1.drain(4..10)); + + let mut vec3 = vec![0u8; 104]; + let mut vec7: std::vec::Vec = Vec::new(); + + vec3.append(&mut vec7); + + let mut vec5 = vec![0u8; 1024]; + let mut vec6: std::vec::Vec = Vec::new(); + + vec5.extend(vec6.drain(..4)); + + let mut vec9: std::vec::Vec = Vec::new(); + + return_vector().append(&mut vec9); + + //won't get linted because it is not a vec + + let mut heap = BinaryHeap::from(vec![1, 3]); + let mut heap2 = BinaryHeap::from(vec![]); + heap2.extend(heap.drain()) +} + +fn return_vector() -> Vec { + let mut new_vector = vec![]; + + for i in 1..10 { + new_vector.push(i) + } + + new_vector +} diff --git a/tests/ui/append_instead_of_extend.rs b/tests/ui/append_instead_of_extend.rs new file mode 100644 index 00000000000..abde5cdac5c --- /dev/null +++ b/tests/ui/append_instead_of_extend.rs @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::append_instead_of_extend)] +use std::collections::BinaryHeap; +fn main() { + //gets linted + let mut vec1 = vec![0u8; 1024]; + let mut vec2: std::vec::Vec = Vec::new(); + + vec2.extend(vec1.drain(..)); + + let mut vec3 = vec![0u8; 1024]; + let mut vec4: std::vec::Vec = Vec::new(); + + vec4.extend(vec3.drain(..)); + + let mut vec11: std::vec::Vec = Vec::new(); + + vec11.extend(return_vector().drain(..)); + + //won't get linted it dosen't move the entire content of a vec into another + let mut test1 = vec![0u8, 10]; + let mut test2: std::vec::Vec = Vec::new(); + + test2.extend(test1.drain(4..10)); + + let mut vec3 = vec![0u8; 104]; + let mut vec7: std::vec::Vec = Vec::new(); + + vec3.append(&mut vec7); + + let mut vec5 = vec![0u8; 1024]; + let mut vec6: std::vec::Vec = Vec::new(); + + vec5.extend(vec6.drain(..4)); + + let mut vec9: std::vec::Vec = Vec::new(); + + return_vector().append(&mut vec9); + + //won't get linted because it is not a vec + + let mut heap = BinaryHeap::from(vec![1, 3]); + let mut heap2 = BinaryHeap::from(vec![]); + heap2.extend(heap.drain()) +} + +fn return_vector() -> Vec { + let mut new_vector = vec![]; + + for i in 1..10 { + new_vector.push(i) + } + + new_vector +} diff --git a/tests/ui/append_instead_of_extend.stderr b/tests/ui/append_instead_of_extend.stderr new file mode 100644 index 00000000000..9d309d981de --- /dev/null +++ b/tests/ui/append_instead_of_extend.stderr @@ -0,0 +1,22 @@ +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/append_instead_of_extend.rs:9:5 + | +LL | vec2.extend(vec1.drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)` + | + = note: `-D clippy::append-instead-of-extend` implied by `-D warnings` + +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/append_instead_of_extend.rs:14:5 + | +LL | vec4.extend(vec3.drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)` + +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/append_instead_of_extend.rs:18:5 + | +LL | vec11.extend(return_vector().drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 683c557afe9de3288478112a0db6d4092f0248ea Mon Sep 17 00:00:00 2001 From: lyj Date: Fri, 11 Jun 2021 14:07:11 +0800 Subject: rc_mutex: add known problems --- clippy_lints/src/types/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 30b04e4174c..5850a10d80f 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -257,7 +257,7 @@ declare_clippy_lint! { /// **Why is this bad?** `Rc` is used in single thread and `Mutex` is used in multi thread. /// Consider using `Rc>` in single thread or `Arc>` in multi thread. /// - /// **Known problems:** None. + /// **Known problems:** Maybe false positive on trait generic. /// /// **Example:** /// ```rust,ignore -- cgit 1.4.1-3-g733a5 From 70ce0c2c55b18f7c9a4bc8d767c626f8d5948fae Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Fri, 11 Jun 2021 17:25:32 -0400 Subject: Remove requirement of fully qualified path for disallowed_method/type --- clippy_lints/src/disallowed_method.rs | 18 ++++++++++++--- clippy_lints/src/disallowed_type.rs | 27 +++++++++++++++------- tests/ui-toml/toml_disallowed_method/clippy.toml | 6 ++++- tests/ui-toml/toml_disallowed_type/clippy.toml | 6 ++--- .../conf_disallowed_type.stderr | 16 ++++++------- 5 files changed, 50 insertions(+), 23 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index ded0a0fff54..76a0cba30e5 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::fn_def_id; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::Expr; +use rustc_hir::{def::Res, def_id::DefId, Crate, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; @@ -52,6 +52,7 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethod { disallowed: FxHashSet>, + def_ids: FxHashSet<(DefId, Vec)>, } impl DisallowedMethod { @@ -61,6 +62,7 @@ impl DisallowedMethod { .iter() .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), + def_ids: FxHashSet::default(), } } } @@ -68,10 +70,20 @@ impl DisallowedMethod { impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + for path in &self.disallowed { + let segs = path.iter().map(ToString::to_string).collect::>(); + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::>()) + { + self.def_ids.insert((id, path.clone())); + } + } + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(def_id) = fn_def_id(cx, expr) { - let func_path = cx.get_def_path(def_id); - if self.disallowed.contains(&func_path) { + if self.def_ids.iter().any(|(id, _)| def_id == *id) { + let func_path = cx.get_def_path(def_id); let func_path_string = func_path .into_iter() .map(Symbol::to_ident_string) diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index f8ac7d76d8f..ab775d99b08 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{def::Res, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind}; +use rustc_hir::{ + def::Res, def_id::DefId, Crate, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, Symbol}; @@ -47,6 +49,7 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedType { disallowed: FxHashSet>, + def_ids: FxHashSet<(DefId, Vec)>, } impl DisallowedType { @@ -56,6 +59,7 @@ impl DisallowedType { .iter() .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), + def_ids: FxHashSet::default(), } } } @@ -63,12 +67,21 @@ impl DisallowedType { impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); impl<'tcx> LateLintPass<'tcx> for DisallowedType { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + for path in &self.disallowed { + let segs = path.iter().map(ToString::to_string).collect::>(); + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::>()) + { + self.def_ids.insert((id, path.clone())); + } + } + } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if_chain! { if let ItemKind::Use(path, UseKind::Single) = &item.kind; - if let Res::Def(_, id) = path.res; - let use_path = cx.get_def_path(id); - if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + if let Res::Def(_, did) = path.res; + if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did); then { emit(cx, name, item.span,); } @@ -79,8 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType { if_chain! { if let TyKind::Path(path) = &ty.kind; if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id(); - let use_path = cx.get_def_path(did); - if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did); then { emit(cx, name, path.span()); } @@ -90,8 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType { fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) { if_chain! { if let Res::Def(_, did) = poly.trait_ref.path.res; - let use_path = cx.get_def_path(did); - if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did); then { emit(cx, name, poly.trait_ref.path.span); } diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml index c0df3b6e8af..a3245da6825 100644 --- a/tests/ui-toml/toml_disallowed_method/clippy.toml +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -1 +1,5 @@ -disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match", "regex::re_unicode::Regex::new"] +disallowed-methods = [ + "std::iter::Iterator::sum", + "regex::Regex::is_match", + "regex::Regex::new" +] diff --git a/tests/ui-toml/toml_disallowed_type/clippy.toml b/tests/ui-toml/toml_disallowed_type/clippy.toml index 57dfdc584bb..2eff854c22c 100644 --- a/tests/ui-toml/toml_disallowed_type/clippy.toml +++ b/tests/ui-toml/toml_disallowed_type/clippy.toml @@ -1,7 +1,7 @@ disallowed-types = [ - "std::collections::hash::map::HashMap", - "core::sync::atomic::AtomicU32", - "syn::ty::TypePath", + "std::collections::HashMap", + "std::sync::atomic::AtomicU32", + "syn::TypePath", "proc_macro2::Ident", "std::thread::Thread", "std::time::Instant", diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr index e5ea23cc0bc..4e6fd91fba1 100644 --- a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr @@ -1,4 +1,4 @@ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:7:1 | LL | use std::sync::atomic::AtomicU32; @@ -24,7 +24,7 @@ error: `std::time::Instant` is not allowed according to config LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { | ^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:16:39 | LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { @@ -36,13 +36,13 @@ error: `std::io::Read` is not allowed according to config LL | fn trait_obj(_: &dyn std::io::Read) { | ^^^^^^^^^^^^^ -error: `std::collections::hash::map::HashMap` is not allowed according to config +error: `std::collections::HashMap` is not allowed according to config --> $DIR/conf_disallowed_type.rs:28:48 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `std::collections::hash::map::HashMap` is not allowed according to config +error: `std::collections::HashMap` is not allowed according to config --> $DIR/conf_disallowed_type.rs:28:12 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); @@ -54,25 +54,25 @@ error: `std::time::Instant` is not allowed according to config LL | let _ = Sneaky::now(); | ^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:30:13 | LL | let _ = foo::atomic::AtomicU32::new(0); | ^^^^^^^^^^^^^^^^^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:31:17 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:31:48 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^ -error: `syn::ty::TypePath` is not allowed according to config +error: `syn::TypePath` is not allowed according to config --> $DIR/conf_disallowed_type.rs:32:43 | LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); -- cgit 1.4.1-3-g733a5 From d4eff81282d4784951ae52ffc3b8fe1c948f872e Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Sun, 13 Jun 2021 15:52:54 -0400 Subject: fixup! Remove requirement of fully qualified path for disallowed_method/type --- clippy_lints/src/disallowed_method.rs | 11 ++--------- clippy_lints/src/disallowed_type.rs | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 76a0cba30e5..aa1a609afed 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -13,21 +13,14 @@ declare_clippy_lint! { /// **Why is this bad?** Some methods are undesirable in certain contexts, /// and it's beneficial to lint for them as needed. /// - /// **Known problems:** Currently, you must write each function as a - /// fully-qualified path. This lint doesn't support aliases or reexported - /// names; be aware that many types in `std` are actually reexports. - /// - /// For example, if you want to disallow `Duration::as_secs`, your clippy.toml - /// configuration would look like - /// `disallowed-methods = ["core::time::Duration::as_secs"]` and not - /// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect. + /// **Known problems:** None. /// /// **Example:** /// /// An example clippy.toml configuration: /// ```toml /// # clippy.toml - /// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"] + /// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"] /// ``` /// /// ```rust,ignore diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index ab775d99b08..e4a88c6324e 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -13,14 +13,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** Some types are undesirable in certain contexts. /// - /// **Known problems:** The fully qualified path must be used. This lint - /// doesn't support aliases or reexported names; be aware that many types - /// in `std` are actually reexports. - /// - /// For example, if you want to disallow `BTreeMap`, your clippy.toml - /// configuration would look like - /// `disallowed-methods = ["alloc::collections::btree::map::BTreeMap"]` and not - /// `disallowed-methods = ["std::collections::BTreeMap"]` as you might expect. + /// **Known problems:** None. /// /// N.B. There is no way to ban primitive types. /// @@ -29,7 +22,7 @@ declare_clippy_lint! { /// An example clippy.toml configuration: /// ```toml /// # clippy.toml - /// disallowed-methods = ["alloc::collections::btree::map::BTreeMap"] + /// disallowed-methods = ["std::collections::BTreeMap"] /// ``` /// /// ```rust,ignore -- cgit 1.4.1-3-g733a5 From a557f37bb4e03bc9ce75e4e22dea68c17f73f01b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 14 Jun 2021 14:10:19 -0500 Subject: Improve metadata code block parsing --- .../src/utils/internal_lints/metadata_collector.rs | 37 ++++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 46af03663b8..c980a0246fd 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -9,6 +9,7 @@ //! a simple mistake) use if_chain::if_chain; +use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, @@ -485,16 +486,32 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option, item: &Item<'_>) -> Option { - cx.tcx - .hir() - .attrs(item.hir_id()) - .iter() - .filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string())) - .reduce(|mut acc, sym| { - acc.push_str(&sym); - acc.push('\n'); - acc - }) + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); + let mut docs = String::from(&*lines.next()?.as_str()); + let mut in_code_block = false; + for line in lines { + docs.push('\n'); + let line = line.as_str(); + let line = &*line; + if let Some(info) = line.trim_start().strip_prefix("```") { + in_code_block = !in_code_block; + if in_code_block { + let lang = info + .trim() + .split(',') + // remove rustdoc directives + .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic")) + // if no language is present, fill in "rust" + .unwrap_or("rust"); + docs.push_str("```"); + docs.push_str(lang); + continue; + } + } + docs.push_str(line); + } + Some(docs) } fn get_lint_group_and_level_or_lint( -- cgit 1.4.1-3-g733a5 From 723f515b6006706b88d22c8edb05330df6365a63 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Sun, 30 May 2021 17:58:32 -0400 Subject: Add macro_braces lint to check for irregular brace use in certain macros Rename unconventional -> nonstandard, add config field Add standard_macro_braces fields so users can specify macro names and brace combinations to lint for in the clippy.toml file. Fix errors caused by nonstandard_macro_braces in other lint tests Fix users ability to override the default nonstandard macro braces Add type position macros impl `check_ty` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 6 + clippy_lints/src/nonstandard_macro_braces.rs | 276 +++++++++++++++++++++ clippy_lints/src/utils/conf.rs | 16 +- tests/ui-toml/nonstandard_macro_braces/clippy.toml | 6 + .../conf_nonstandard_macro_braces.rs | 44 ++++ .../conf_nonstandard_macro_braces.stderr | 94 +++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 2 +- tests/ui/unnecessary_cast_fixable.rs | 2 +- tests/ui/vec.fixed | 2 +- tests/ui/vec.rs | 2 +- 12 files changed, 443 insertions(+), 10 deletions(-) create mode 100644 clippy_lints/src/nonstandard_macro_braces.rs create mode 100644 tests/ui-toml/nonstandard_macro_braces/clippy.toml create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f0fc8f609..c30a0849348 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2575,6 +2575,7 @@ Released 2018-09-13 [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options +[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces [`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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 591bf68f927..e3fbd1adee9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod no_effect; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; +mod nonstandard_macro_braces; mod open_options; mod option_env_unwrap; mod option_if_let_else; @@ -844,6 +845,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: non_expressive_names::MANY_SINGLE_CHAR_NAMES, non_expressive_names::SIMILAR_NAMES, non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, + nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, open_options::NONSENSICAL_OPEN_OPTIONS, option_env_unwrap::OPTION_ENV_UNWRAP, option_if_let_else::OPTION_IF_LET_ELSE, @@ -1360,6 +1362,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), @@ -1536,6 +1539,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(ptr::CMP_NULL), LintId::of(ptr::PTR_ARG), LintId::of(ptr_eq::PTR_EQ), @@ -2040,6 +2044,8 @@ 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, }); + let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::>(); + store.register_early_pass(move || box nonstandard_macro_braces::MacroBraces::new(¯o_matcher)); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs new file mode 100644 index 00000000000..1adad5be6dd --- /dev/null +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -0,0 +1,276 @@ +use std::{ + fmt, + hash::{Hash, Hasher}, +}; + +use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; +use serde::{de, Deserialize}; + +declare_clippy_lint! { + /// **What it does:** Checks that common macros are used with consistent bracing. + /// + /// **Why is this bad?** This is mostly a consistency lint although using () or [] + /// doesn't give you a semicolon in item position, which can be unexpected. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// + /// ```rust + /// vec!{1, 2, 3}; + /// ``` + /// Use instead: + /// ```rust + /// vec![1, 2, 3]; + /// ``` + pub NONSTANDARD_MACRO_BRACES, + style, + "check consistent use of braces in macro" +} + +const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")]; + +/// The (name, (open brace, close brace), source snippet) +type MacroInfo<'a> = (&'a str, &'a (String, String), String); + +#[derive(Clone, Debug, Default)] +pub struct MacroBraces { + macro_braces: FxHashMap, + done: FxHashSet, +} + +impl MacroBraces { + pub fn new(conf: &FxHashSet) -> Self { + let macro_braces = macro_braces(conf.clone()); + Self { + macro_braces, + done: FxHashSet::default(), + } + } +} + +impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); + +impl EarlyLintPass for MacroBraces { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) { + let span = item.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } + + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { + if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) { + let span = stmt.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) { + let span = expr.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } + + fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { + if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) { + let span = ty.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } +} + +fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option> { + if_chain! { + if in_macro(span); + if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces); + if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site); + let c = snip.replace(" ", ""); // make formatting consistent + if !c.starts_with(&format!("{}!{}", name, braces.0)); + if !this.done.contains(&span.ctxt().outer_expn_data().call_site); + then { + Some((name, braces, snip)) + } else { + None + } + } +} + +fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) { + let with_space = &format!("! {}", braces.0); + let without_space = &format!("!{}", braces.0); + let mut help = snip; + for b in BRACES.iter().filter(|b| b.0 != braces.0) { + help = help.replace(b.0, &braces.0).replace(b.1, &braces.1); + // Only `{` traditionally has space before the brace + if braces.0 != "{" && help.contains(with_space) { + help = help.replace(with_space, without_space); + } else if braces.0 == "{" && help.contains(without_space) { + help = help.replace(without_space, with_space); + } + } + span_lint_and_help( + cx, + NONSTANDARD_MACRO_BRACES, + span, + &format!("use of irregular braces for `{}!` macro", name), + Some(span), + &format!("consider writing `{}`", help), + ); +} + +fn find_matching_macro( + span: Span, + braces: &FxHashMap, +) -> Option<(&String, &(String, String))> { + braces + .iter() + .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some()) +} + +fn macro_braces(conf: FxHashSet) -> FxHashMap { + let mut braces = vec![ + macro_matcher!( + name: "print", + braces: ("(", ")"), + ), + macro_matcher!( + name: "println", + braces: ("(", ")"), + ), + macro_matcher!( + name: "eprint", + braces: ("(", ")"), + ), + macro_matcher!( + name: "eprintln", + braces: ("(", ")"), + ), + macro_matcher!( + name: "write", + braces: ("(", ")"), + ), + macro_matcher!( + name: "writeln", + braces: ("(", ")"), + ), + macro_matcher!( + name: "format", + braces: ("(", ")"), + ), + macro_matcher!( + name: "format_args", + braces: ("(", ")"), + ), + macro_matcher!( + name: "vec", + braces: ("[", "]"), + ), + ] + .into_iter() + .collect::>(); + // We want users items to override any existing items + for it in conf { + braces.insert(it.name, it.braces); + } + braces +} + +macro_rules! macro_matcher { + (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => { + ($name.to_owned(), ($open.to_owned(), $close.to_owned())) + }; +} +pub(crate) use macro_matcher; + +#[derive(Clone, Debug)] +pub struct MacroMatcher { + name: String, + braces: (String, String), +} + +impl Hash for MacroMatcher { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialEq for MacroMatcher { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} +impl Eq for MacroMatcher {} + +impl<'de> Deserialize<'de> for MacroMatcher { + fn deserialize(deser: D) -> Result + where + D: de::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + Name, + Brace, + } + struct MacVisitor; + impl<'de> de::Visitor<'de> for MacVisitor { + type Value = MacroMatcher; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("struct MacroMatcher") + } + + fn visit_map(self, mut map: V) -> Result + where + V: de::MapAccess<'de>, + { + let mut name = None; + let mut brace: Option<&str> = None; + while let Some(key) = map.next_key()? { + match key { + Field::Name => { + if name.is_some() { + return Err(de::Error::duplicate_field("name")); + } + name = Some(map.next_value()?); + }, + Field::Brace => { + if brace.is_some() { + return Err(de::Error::duplicate_field("brace")); + } + brace = Some(map.next_value()?); + }, + } + } + let name = name.ok_or_else(|| de::Error::missing_field("name"))?; + let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?; + Ok(MacroMatcher { + name, + braces: BRACES + .iter() + .find(|b| b.0 == brace) + .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) + .ok_or_else(|| { + de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace)) + })?, + }) + } + } + + const FIELDS: &[&str] = &["name", "brace"]; + deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor) + } +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ad2cb27650e..cb335522f61 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -26,13 +26,13 @@ impl TryConf { macro_rules! define_Conf { ($( - #[doc = $doc:literal] + $(#[doc = $doc:literal])* $(#[conf_deprecated($dep:literal)])? ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { - $(#[doc = $doc] pub $name: $ty,)* + $($(#[doc = $doc])* pub $name: $ty,)* } mod defaults { @@ -109,7 +109,7 @@ macro_rules! define_Conf { stringify!($name), stringify!($ty), format!("{:?}", super::defaults::$name()), - $doc, + concat!($($doc,)*), deprecation_reason, ) }, @@ -182,9 +182,9 @@ define_Conf! { (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: u64 = 3), - /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have + /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have (max_struct_bools: u64 = 3), - /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have + /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have (max_fn_params_bools: u64 = 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports: bool = false), @@ -198,6 +198,12 @@ define_Conf! { (upper_case_acronyms_aggressive: bool = false), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. (cargo_ignore_publish: bool = false), + /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. + /// + /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. + /// If the macro is could be used with a full path two `MacroMatcher`s have to be added one + /// with the full path `crate_name::macro_name` and one with just the macro name. + (standard_macro_braces: Vec = Vec::new()), } /// Search for the configuration file. diff --git a/tests/ui-toml/nonstandard_macro_braces/clippy.toml b/tests/ui-toml/nonstandard_macro_braces/clippy.toml new file mode 100644 index 00000000000..bced8948a02 --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/clippy.toml @@ -0,0 +1,6 @@ +standard-macro-braces = [ + { name = "quote", brace = "{" }, + { name = "quote::quote", brace = "{" }, + { name = "eprint", brace = "[" }, + { name = "type_pos", brace = "[" }, +] diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs new file mode 100644 index 00000000000..4ae6864cbb0 --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -0,0 +1,44 @@ +// #![warn(clippy::nonstandard_macro_braces)] + +extern crate quote; + +use quote::quote; + +#[rustfmt::skip] +macro_rules! test { + () => { + vec!{0, 0, 0} + }; +} + +#[rustfmt::skip] +macro_rules! test2 { + ($($arg:tt)*) => { + format_args!($($arg)*) + }; +} + +macro_rules! type_pos { + ($what:ty) => { + Vec<$what> + }; +} + +#[rustfmt::skip] +fn main() { + let _ = vec! {1, 2, 3}; + let _ = format!["ugh {} stop being such a good compiler", "hello"]; + let _ = quote!(let x = 1;); + let _ = quote::quote!(match match match); + let _ = test!(); + let _ = vec![1,2,3]; + + let _ = quote::quote! {true || false}; + let _ = vec! [0 ,0 ,0]; + let _ = format!("fds{}fds", 10); + let _ = test2!["{}{}{}", 1, 2, 3]; + + let _: type_pos!(usize) = vec![]; + + eprint!("test if user config overrides defaults"); +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr new file mode 100644 index 00000000000..7bcd1829524 --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -0,0 +1,94 @@ +error: use of irregular braces for `vec!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:29:13 + | +LL | let _ = vec! {1, 2, 3}; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings` +help: consider writing `vec![1, 2, 3]` + --> $DIR/conf_nonstandard_macro_braces.rs:29:13 + | +LL | let _ = vec! {1, 2, 3}; + | ^^^^^^^^^^^^^^ + +error: use of irregular braces for `format!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:30:13 + | +LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `format!("ugh () stop being such a good compiler", "hello")` + --> $DIR/conf_nonstandard_macro_braces.rs:30:13 + | +LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `quote!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:31:13 + | +LL | let _ = quote!(let x = 1;); + | ^^^^^^^^^^^^^^^^^^ + | +help: consider writing `quote! {let x = 1;}` + --> $DIR/conf_nonstandard_macro_braces.rs:31:13 + | +LL | let _ = quote!(let x = 1;); + | ^^^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `quote::quote!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:32:13 + | +LL | let _ = quote::quote!(match match match); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `quote::quote! {match match match}` + --> $DIR/conf_nonstandard_macro_braces.rs:32:13 + | +LL | let _ = quote::quote!(match match match); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `vec!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:10:9 + | +LL | vec!{0, 0, 0} + | ^^^^^^^^^^^^^ +... +LL | let _ = test!(); + | ------- in this macro invocation + | +help: consider writing `vec![0, 0, 0]` + --> $DIR/conf_nonstandard_macro_braces.rs:10:9 + | +LL | vec!{0, 0, 0} + | ^^^^^^^^^^^^^ +... +LL | let _ = test!(); + | ------- in this macro invocation + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: use of irregular braces for `type_pos!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:41:12 + | +LL | let _: type_pos!(usize) = vec![]; + | ^^^^^^^^^^^^^^^^ + | +help: consider writing `type_pos![usize]` + --> $DIR/conf_nonstandard_macro_braces.rs:41:12 + | +LL | let _: type_pos!(usize) = vec![]; + | ^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `eprint!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:43:5 + | +LL | eprint!("test if user config overrides defaults"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `eprint!["test if user config overrides defaults"];` + --> $DIR/conf_nonstandard_macro_braces.rs:43:5 + | +LL | eprint!("test if user config overrides defaults"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + 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 06d70b66fda..f179479cbec 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 `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 7fbce58a82f..bda0f2c47cd 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] fn main() { // casting integer literal to float is unnecessary diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index a71363ea4d2..f7a4f2a5988 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] fn main() { // casting integer literal to float is unnecessary diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index da35f2e5c1b..318f9c2dceb 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] #[derive(Debug)] diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index e9ed83e5c5a..d7673ce3e64 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] #[derive(Debug)] -- cgit 1.4.1-3-g733a5 From a40fd6da7e49eea2c8b77a95f274d249186ba7ce Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 18 Jun 2021 22:18:16 +0200 Subject: Move from-iter-instead-of-collect to pedantic Since FromIterator will become part of the prelude, this lint being warn by default is incongruous with the libs team position on the topic. --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/methods/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5ab333f8aa1..f5082468a77 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1092,6 +1092,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::CLONED_INSTEAD_OF_COPIED), LintId::of(methods::FILTER_MAP_NEXT), LintId::of(methods::FLAT_MAP_OPTION), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::MAP_FLATTEN), @@ -1288,7 +1289,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), @@ -1504,7 +1504,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b556dcb4dfe..c6bef7bc5d5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1530,7 +1530,7 @@ declare_clippy_lint! { /// assert_eq!(v, vec![5, 5, 5, 5, 5]); /// ``` pub FROM_ITER_INSTEAD_OF_COLLECT, - style, + pedantic, "use `.collect()` instead of `::from_iter()`" } -- cgit 1.4.1-3-g733a5 From 8276f26a4d189e9898aabf09e16605f35fa92318 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 20 Jun 2021 13:48:44 +0200 Subject: Fix wrong config option being suggested for deprecated wrong_pub_self_convention lint Problem: for code like ```` fn main() { println!("Hello, world!"); } ```` clippy will issue a warning to use a clippy.toml option instead: ```` warning: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items --> src/main.rs:2:9 | 2 | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(renamed_and_removed_lints)]` on by default ```` But using the lint name as seen in the warning message echo "avoid_breaking_exported_api = true\n" > clippy.toml Will cause an error: ```` error: error reading Clippy's configuration file `/tmp/clippytest/clippy.toml`: unknown field `avoid_breaking_exported_api`, expected one of `avoid-breaking-exported-api`, ... ```` Replace the underscores with dashes in the deprecation message. changelog: avoid_breaking_exported_api: suggest correct clippy config toml option in the deprecation message --- clippy_lints/src/deprecated_lints.rs | 4 ++-- clippy_lints/src/lib.rs | 4 ++-- tests/ui/deprecated.stderr | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 04f3d77464f..2933fbc9341 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -149,7 +149,7 @@ declare_deprecated_lint! { /// enables the `enum_variant_names` lint for public items. /// ``` pub PUB_ENUM_VARIANT_NAMES, - "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items" + "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" } declare_deprecated_lint! { @@ -158,5 +158,5 @@ declare_deprecated_lint! { /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which /// enables the `wrong_self_conversion` lint for public items. pub WRONG_PUB_SELF_CONVENTION, - "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items" + "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cc12d9b0d45..03e093f4c03 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -484,11 +484,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_removed( "clippy::pub_enum_variant_names", - "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items", + "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items", ); store.register_removed( "clippy::wrong_pub_self_convention", - "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items", + "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 03c9f438891..0af6b500115 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -84,13 +84,13 @@ error: lint `clippy::filter_map` has been removed: this lint has been replaced b LL | #[warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ -error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items +error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items --> $DIR/deprecated.rs:15:8 | LL | #[warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items +error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items --> $DIR/deprecated.rs:16:8 | LL | #[warn(clippy::wrong_pub_self_convention)] -- cgit 1.4.1-3-g733a5 From 96a9786b1c95b6647ff7e1478947c100db78e3ca Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 20 Jun 2021 22:32:32 +0200 Subject: Fixed broken deploy script due to multiline configuration docs --- clippy_lints/src/utils/conf.rs | 15 +++++++-------- util/lintlib.py | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index cb335522f61..2f8064499fd 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -24,15 +24,18 @@ impl TryConf { } } +/// Note that the configuration parsing currently doesn't support documentation that will +/// that spans over several lines. This will be possible with the new implementation +/// See (rust-clippy#7172) macro_rules! define_Conf { ($( - $(#[doc = $doc:literal])* + #[doc = $doc:literal] $(#[conf_deprecated($dep:literal)])? ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { - $($(#[doc = $doc])* pub $name: $ty,)* + $(#[doc = $doc] pub $name: $ty,)* } mod defaults { @@ -109,7 +112,7 @@ macro_rules! define_Conf { stringify!($name), stringify!($ty), format!("{:?}", super::defaults::$name()), - concat!($($doc,)*), + $doc, deprecation_reason, ) }, @@ -198,11 +201,7 @@ define_Conf! { (upper_case_acronyms_aggressive: bool = false), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. (cargo_ignore_publish: bool = false), - /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. - /// - /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. - /// If the macro is could be used with a full path two `MacroMatcher`s have to be added one - /// with the full path `crate_name::macro_name` and one with just the macro name. + /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified.
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name. (standard_macro_braces: Vec = Vec::new()), } diff --git a/util/lintlib.py b/util/lintlib.py index 5707cf0ce0f..3b6e8c372ed 100644 --- a/util/lintlib.py +++ b/util/lintlib.py @@ -12,7 +12,7 @@ Config = collections.namedtuple('Config', 'name ty doc default') lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''') group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''') -conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE) +conf_re = re.compile(r'''define_Conf! {\n((?!\n})[\s\S])*\n}''', re.MULTILINE) confvar_re = re.compile( r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE) comment_re = re.compile(r'''\s*/// ?(.*)''') @@ -91,7 +91,7 @@ def parse_configs(path): contents = fp.read() match = re.search(conf_re, contents) - confvars = re.findall(confvar_re, match.group(1)) + confvars = re.findall(confvar_re, match.group(0)) for (lints, doc, name, ty, default) in confvars: for lint in lints.split(','): -- cgit 1.4.1-3-g733a5 From 20cb1bc7c1a381a702f4b81511ad4e92df69c34c Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Mon, 14 Jun 2021 12:23:33 -0700 Subject: check for unbalanced tick pairs in doc-markdown --- clippy_lints/src/doc.rs | 64 +++++++--- clippy_lints/src/if_let_mutex.rs | 2 +- tests/ui/doc.rs | 221 ----------------------------------- tests/ui/doc.stderr | 190 ------------------------------ tests/ui/doc/doc.rs | 221 +++++++++++++++++++++++++++++++++++ tests/ui/doc/doc.stderr | 190 ++++++++++++++++++++++++++++++ tests/ui/doc/unbalanced_ticks.rs | 36 ++++++ tests/ui/doc/unbalanced_ticks.stderr | 64 ++++++++++ 8 files changed, 562 insertions(+), 426 deletions(-) delete mode 100644 tests/ui/doc.rs delete mode 100644 tests/ui/doc.stderr create mode 100644 tests/ui/doc/doc.rs create mode 100644 tests/ui/doc/doc.stderr create mode 100644 tests/ui/doc/unbalanced_ticks.rs create mode 100644 tests/ui/doc/unbalanced_ticks.stderr (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index e67ec4e06c5..4e164d33a05 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note}; +use clippy_utils::source::first_line_of_span; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; use if_chain::if_chain; @@ -37,7 +38,8 @@ declare_clippy_lint! { /// consider that. /// /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks - /// for is limited, and there are still false positives. + /// for is limited, and there are still false positives. HTML elements and their + /// content are not linted. /// /// In addition, when writing documentation comments, including `[]` brackets /// inside a link text would trip the parser. Therfore, documenting link with @@ -469,11 +471,11 @@ fn check_doc<'a, Events: Iterator, Range DocHeaders { // true if a safety header was found - use pulldown_cmark::CodeBlockKind; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; - use pulldown_cmark::Tag::{CodeBlock, Heading, Link}; + use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; + use pulldown_cmark::{CodeBlockKind, CowStr}; let mut headers = DocHeaders { safety: false, @@ -485,6 +487,9 @@ fn check_doc<'a, Events: Iterator, Range, Span)> = Vec::new(); + let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1; for (event, range) in events { match event { Start(CodeBlock(ref kind)) => { @@ -510,13 +515,42 @@ fn check_doc<'a, Events: Iterator, Range in_link = Some(url), End(Link(..)) => in_link = None, - Start(Heading(_)) => in_heading = true, - End(Heading(_)) => in_heading = false, + Start(Heading(_) | Paragraph | Item) => { + if let Start(Heading(_)) = event { + in_heading = true; + } + ticks_unbalanced = false; + let (_, span) = get_current_span(spans, range.start); + paragraph_span = first_line_of_span(cx, span); + }, + End(Heading(_) | Paragraph | Item) => { + if let End(Heading(_)) = event { + in_heading = false; + } + if ticks_unbalanced { + span_lint_and_help( + cx, + DOC_MARKDOWN, + paragraph_span, + "backticks are unbalanced", + None, + "a backtick may be missing a pair", + ); + } else { + for (text, span) in text_to_check { + check_text(cx, valid_idents, &text, span); + } + } + text_to_check = Vec::new(); + }, Start(_tag) | End(_tag) => (), // We don't care about other tags Html(_html) => (), // HTML is weird, just ignore it SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { - if Some(&text) == in_link.as_ref() { + let (begin, span) = get_current_span(spans, range.start); + paragraph_span = paragraph_span.with_hi(span.hi()); + ticks_unbalanced |= text.contains('`'); + if Some(&text) == in_link.as_ref() || ticks_unbalanced { // Probably a link of the form `` // Which are represented as a link to "http://example.com" with // text "http://example.com" by pulldown-cmark @@ -525,11 +559,6 @@ fn check_doc<'a, Events: Iterator, Range o, - Err(e) => e - 1, - }; - let (begin, span) = spans[index]; if in_code { if is_rust { let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition()); @@ -538,8 +567,7 @@ fn check_doc<'a, Events: Iterator, Range, Range (usize, Span) { + let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { + Ok(o) => o, + Err(e) => e - 1, + }; + spans[index] +} + fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { fn has_needless_main(code: &str, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index f661f7ede82..5403d76ea30 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { } } -/// Checks if `Mutex::lock` is called in the `if let _ = expr. +/// Checks if `Mutex::lock` is called in the `if let` expr. pub struct OppVisitor<'a, 'tcx> { mutex_lock_called: bool, found_mutex: Option<&'tcx Expr<'tcx>>, diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs deleted file mode 100644 index 8afef6b23d4..00000000000 --- a/tests/ui/doc.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! This file tests for the `DOC_MARKDOWN` lint. - -#![allow(dead_code, incomplete_features)] -#![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] -#![rustfmt::skip] - -/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) -/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun -/// which should be reported only once despite being __doubly bad__. -/// Here be ::a::global:path. -/// That's not code ~NotInCodeBlock~. -/// be_sure_we_got_to_the_end_of_it -fn foo_bar() { -} - -/// That one tests multiline ticks. -/// ```rust -/// foo_bar FOO_BAR -/// _foo bar_ -/// ``` -/// -/// ~~~rust -/// foo_bar FOO_BAR -/// _foo bar_ -/// ~~~ -/// be_sure_we_got_to_the_end_of_it -fn multiline_codeblock() { -} - -/// This _is a test for -/// multiline -/// emphasis_. -/// be_sure_we_got_to_the_end_of_it -fn test_emphasis() { -} - -/// This tests units. See also #835. -/// kiB MiB GiB TiB PiB EiB -/// kib Mib Gib Tib Pib Eib -/// kB MB GB TB PB EB -/// kb Mb Gb Tb Pb Eb -/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB -/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib -/// 32kB 32MB 32GB 32TB 32PB 32EB -/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb -/// NaN -/// be_sure_we_got_to_the_end_of_it -fn test_units() { -} - -/// This tests allowed identifiers. -/// KiB MiB GiB TiB PiB EiB -/// DirectX -/// ECMAScript -/// GPLv2 GPLv3 -/// GitHub GitLab -/// IPv4 IPv6 -/// ClojureScript CoffeeScript JavaScript PureScript TypeScript -/// NaN NaNs -/// OAuth GraphQL -/// OCaml -/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS -/// WebGL -/// TensorFlow -/// TrueType -/// iOS macOS FreeBSD -/// TeX LaTeX BibTeX BibLaTeX -/// MinGW -/// 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]. -/// It can also be [inline_link2]. -/// -/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example -/// [inline_link]: https://foobar -/// [inline_link2]: https://foobar -/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and -/// `multiline_ticks` functions. -/// -/// expression of the type `_ m c` (where `` -/// is one of {`&`, '|'} and `` is one of {`!=`, `>=`, `>` , -/// be_sure_we_got_to_the_end_of_it -fn main() { - foo_bar(); - multiline_codeblock(); - test_emphasis(); - test_units(); -} - -/// ## CamelCaseThing -/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897. -/// -/// # CamelCaseThing -/// -/// Not a title #897 CamelCaseThing -/// be_sure_we_got_to_the_end_of_it -fn issue897() { -} - -/// I am confused by brackets? (`x_y`) -/// I am confused by brackets? (foo `x_y`) -/// I am confused by brackets? (`x_y` foo) -/// be_sure_we_got_to_the_end_of_it -fn issue900() { -} - -/// Diesel queries also have a similar problem to [Iterator][iterator], where -/// /// More talking -/// returning them from a function requires exposing the implementation of that -/// function. The [`helper_types`][helper_types] module exists to help with this, -/// but you might want to hide the return type or have it conditionally change. -/// Boxing can achieve both. -/// -/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html -/// [helper_types]: ../helper_types/index.html -/// be_sure_we_got_to_the_end_of_it -fn issue883() { -} - -/// `foo_bar -/// baz_quz` -/// [foo -/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html) -fn multiline() { -} - -/** E.g., serialization of an empty list: FooBar -``` -That's in a code block: `PackedNode` -``` - -And BarQuz too. -be_sure_we_got_to_the_end_of_it -*/ -fn issue1073() { -} - -/** E.g., serialization of an empty list: FooBar -``` -That's in a code block: PackedNode -``` - -And BarQuz too. -be_sure_we_got_to_the_end_of_it -*/ -fn issue1073_alt() { -} - -/// Tests more than three quotes: -/// ```` -/// DoNotWarn -/// ``` -/// StillDont -/// ```` -/// be_sure_we_got_to_the_end_of_it -fn four_quotes() { -} - -/// See [NIST SP 800-56A, revision 2]. -/// -/// [NIST SP 800-56A, revision 2]: -/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419 -fn issue_902_comment() {} - -#[cfg_attr(feature = "a", doc = " ```")] -#[cfg_attr(not(feature = "a"), doc = " ```ignore")] -/// fn main() { -/// let s = "localhost:10000".to_string(); -/// println!("{}", s); -/// } -/// ``` -fn issue_1469() {} - -/** - * This is a doc comment that should not be a list - *This would also be an error under a strict common mark interpretation - */ -fn issue_1920() {} - -/// Ok: -/// -/// Not ok: http://www.unicode.org -/// Not ok: https://www.unicode.org -/// Not ok: http://www.unicode.org/ -/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels -fn issue_1832() {} - -/// An iterator over mycrate::Collection's values. -/// It should not lint a `'static` lifetime in ticks. -fn issue_2210() {} - -/// This should not cause the lint to trigger: -/// #REQ-data-family.lint_partof_exists -fn issue_2343() {} - -/// This should not cause an ICE: -/// __|_ _|__||_| -fn pulldown_cmark_crash() {} - -// issue #7033 - const_evaluatable_checked ICE -struct S -where [(); N.checked_next_power_of_two().unwrap()]: { - arr: [T; N.checked_next_power_of_two().unwrap()], - n: usize, -} - -impl S -where [(); N.checked_next_power_of_two().unwrap()]: { - fn new() -> Self { - Self { - arr: [T::default(); N.checked_next_power_of_two().unwrap()], - n: 0, - } - } -} diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr deleted file mode 100644 index 7eab8a85f09..00000000000 --- a/tests/ui/doc.stderr +++ /dev/null @@ -1,190 +0,0 @@ -error: you should put `foo_bar` between ticks in the documentation - --> $DIR/doc.rs:8:9 - | -LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) - | ^^^^^^^ - | - = note: `-D clippy::doc-markdown` implied by `-D warnings` - -error: you should put `foo::bar` between ticks in the documentation - --> $DIR/doc.rs:8:51 - | -LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) - | ^^^^^^^^ - -error: you should put `Foo::some_fun` between ticks in the documentation - --> $DIR/doc.rs:9:83 - | -LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun - | ^^^^^^^^^^^^^ - -error: you should put `a::global:path` between ticks in the documentation - --> $DIR/doc.rs:11:15 - | -LL | /// Here be ::a::global:path. - | ^^^^^^^^^^^^^^ - -error: you should put `NotInCodeBlock` between ticks in the documentation - --> $DIR/doc.rs:12:22 - | -LL | /// That's not code ~NotInCodeBlock~. - | ^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:13: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:27: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:34: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:48: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:71: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:75: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:78: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:88:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:96:8 - | -LL | /// ## CamelCaseThing - | ^^^^^^^^^^^^^^ - -error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:99:7 - | -LL | /// # CamelCaseThing - | ^^^^^^^^^^^^^^ - -error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:101: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:102: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:109: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:122:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:133:43 - | -LL | /** E.g., serialization of an empty list: FooBar - | ^^^^^^ - -error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:138: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:139:1 - | -LL | be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:144:43 - | -LL | /** E.g., serialization of an empty list: FooBar - | ^^^^^^ - -error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:149: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:150: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:161: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:188:13 - | -LL | /// Not ok: http://www.unicode.org - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:189:13 - | -LL | /// Not ok: https://www.unicode.org - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:190:13 - | -LL | /// Not ok: http://www.unicode.org/ - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:191: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:194:22 - | -LL | /// An iterator over mycrate::Collection's values. - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 31 previous errors - diff --git a/tests/ui/doc/doc.rs b/tests/ui/doc/doc.rs new file mode 100644 index 00000000000..8afef6b23d4 --- /dev/null +++ b/tests/ui/doc/doc.rs @@ -0,0 +1,221 @@ +//! This file tests for the `DOC_MARKDOWN` lint. + +#![allow(dead_code, incomplete_features)] +#![warn(clippy::doc_markdown)] +#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] +#![rustfmt::skip] + +/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) +/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun +/// which should be reported only once despite being __doubly bad__. +/// Here be ::a::global:path. +/// That's not code ~NotInCodeBlock~. +/// be_sure_we_got_to_the_end_of_it +fn foo_bar() { +} + +/// That one tests multiline ticks. +/// ```rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ``` +/// +/// ~~~rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ~~~ +/// be_sure_we_got_to_the_end_of_it +fn multiline_codeblock() { +} + +/// This _is a test for +/// multiline +/// emphasis_. +/// be_sure_we_got_to_the_end_of_it +fn test_emphasis() { +} + +/// This tests units. See also #835. +/// kiB MiB GiB TiB PiB EiB +/// kib Mib Gib Tib Pib Eib +/// kB MB GB TB PB EB +/// kb Mb Gb Tb Pb Eb +/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB +/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib +/// 32kB 32MB 32GB 32TB 32PB 32EB +/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb +/// NaN +/// be_sure_we_got_to_the_end_of_it +fn test_units() { +} + +/// This tests allowed identifiers. +/// KiB MiB GiB TiB PiB EiB +/// DirectX +/// ECMAScript +/// GPLv2 GPLv3 +/// GitHub GitLab +/// IPv4 IPv6 +/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// NaN NaNs +/// OAuth GraphQL +/// OCaml +/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS +/// WebGL +/// TensorFlow +/// TrueType +/// iOS macOS FreeBSD +/// TeX LaTeX BibTeX BibLaTeX +/// MinGW +/// 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]. +/// It can also be [inline_link2]. +/// +/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example +/// [inline_link]: https://foobar +/// [inline_link2]: https://foobar +/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and +/// `multiline_ticks` functions. +/// +/// expression of the type `_ m c` (where `` +/// is one of {`&`, '|'} and `` is one of {`!=`, `>=`, `>` , +/// be_sure_we_got_to_the_end_of_it +fn main() { + foo_bar(); + multiline_codeblock(); + test_emphasis(); + test_units(); +} + +/// ## CamelCaseThing +/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897. +/// +/// # CamelCaseThing +/// +/// Not a title #897 CamelCaseThing +/// be_sure_we_got_to_the_end_of_it +fn issue897() { +} + +/// I am confused by brackets? (`x_y`) +/// I am confused by brackets? (foo `x_y`) +/// I am confused by brackets? (`x_y` foo) +/// be_sure_we_got_to_the_end_of_it +fn issue900() { +} + +/// Diesel queries also have a similar problem to [Iterator][iterator], where +/// /// More talking +/// returning them from a function requires exposing the implementation of that +/// function. The [`helper_types`][helper_types] module exists to help with this, +/// but you might want to hide the return type or have it conditionally change. +/// Boxing can achieve both. +/// +/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html +/// [helper_types]: ../helper_types/index.html +/// be_sure_we_got_to_the_end_of_it +fn issue883() { +} + +/// `foo_bar +/// baz_quz` +/// [foo +/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html) +fn multiline() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: `PackedNode` +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: PackedNode +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073_alt() { +} + +/// Tests more than three quotes: +/// ```` +/// DoNotWarn +/// ``` +/// StillDont +/// ```` +/// be_sure_we_got_to_the_end_of_it +fn four_quotes() { +} + +/// See [NIST SP 800-56A, revision 2]. +/// +/// [NIST SP 800-56A, revision 2]: +/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419 +fn issue_902_comment() {} + +#[cfg_attr(feature = "a", doc = " ```")] +#[cfg_attr(not(feature = "a"), doc = " ```ignore")] +/// fn main() { +/// let s = "localhost:10000".to_string(); +/// println!("{}", s); +/// } +/// ``` +fn issue_1469() {} + +/** + * This is a doc comment that should not be a list + *This would also be an error under a strict common mark interpretation + */ +fn issue_1920() {} + +/// Ok: +/// +/// Not ok: http://www.unicode.org +/// Not ok: https://www.unicode.org +/// Not ok: http://www.unicode.org/ +/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels +fn issue_1832() {} + +/// An iterator over mycrate::Collection's values. +/// It should not lint a `'static` lifetime in ticks. +fn issue_2210() {} + +/// This should not cause the lint to trigger: +/// #REQ-data-family.lint_partof_exists +fn issue_2343() {} + +/// This should not cause an ICE: +/// __|_ _|__||_| +fn pulldown_cmark_crash() {} + +// issue #7033 - const_evaluatable_checked ICE +struct S +where [(); N.checked_next_power_of_two().unwrap()]: { + arr: [T; N.checked_next_power_of_two().unwrap()], + n: usize, +} + +impl S +where [(); N.checked_next_power_of_two().unwrap()]: { + fn new() -> Self { + Self { + arr: [T::default(); N.checked_next_power_of_two().unwrap()], + n: 0, + } + } +} diff --git a/tests/ui/doc/doc.stderr b/tests/ui/doc/doc.stderr new file mode 100644 index 00000000000..7eab8a85f09 --- /dev/null +++ b/tests/ui/doc/doc.stderr @@ -0,0 +1,190 @@ +error: you should put `foo_bar` between ticks in the documentation + --> $DIR/doc.rs:8:9 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + +error: you should put `foo::bar` between ticks in the documentation + --> $DIR/doc.rs:8:51 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^^ + +error: you should put `Foo::some_fun` between ticks in the documentation + --> $DIR/doc.rs:9:83 + | +LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun + | ^^^^^^^^^^^^^ + +error: you should put `a::global:path` between ticks in the documentation + --> $DIR/doc.rs:11:15 + | +LL | /// Here be ::a::global:path. + | ^^^^^^^^^^^^^^ + +error: you should put `NotInCodeBlock` between ticks in the documentation + --> $DIR/doc.rs:12:22 + | +LL | /// That's not code ~NotInCodeBlock~. + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:13: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:27: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:34: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:48: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:71: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:75: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:78: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:88:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:96:8 + | +LL | /// ## CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:99:7 + | +LL | /// # CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:101: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:102: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:109: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:122:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:133:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:138: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:139:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:144:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:149: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:150: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:161: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:188:13 + | +LL | /// Not ok: http://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:189:13 + | +LL | /// Not ok: https://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:190:13 + | +LL | /// Not ok: http://www.unicode.org/ + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:191: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:194:22 + | +LL | /// An iterator over mycrate::Collection's values. + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 31 previous errors + diff --git a/tests/ui/doc/unbalanced_ticks.rs b/tests/ui/doc/unbalanced_ticks.rs new file mode 100644 index 00000000000..78e87bc6906 --- /dev/null +++ b/tests/ui/doc/unbalanced_ticks.rs @@ -0,0 +1,36 @@ +//! This file tests for the `DOC_MARKDOWN` lint, specifically cases +//! where ticks are unbalanced (see issue #6753). + +#![allow(dead_code)] +#![warn(clippy::doc_markdown)] + +/// This is a doc comment with `unbalanced_tick marks and several words that +/// should be `encompassed_by` tick marks because they `contain_underscores`. +/// Because of the initial `unbalanced_tick` pair, the error message is +/// very `confusing_and_misleading`. +fn main() {} + +/// This paragraph has `unbalanced_tick marks and should stop_linting. +/// +/// This paragraph is fine and should_be linted normally. +/// +/// Double unbalanced backtick from ``here to here` should lint. +/// +/// Double balanced back ticks ``start end`` is fine. +fn multiple_paragraphs() {} + +/// ``` +/// // Unbalanced tick mark in code block shouldn't warn: +/// ` +/// ``` +fn in_code_block() {} + +/// # `Fine` +/// +/// ## not_fine +/// +/// ### `unbalanced +/// +/// - This `item has unbalanced tick marks +/// - This item needs backticks_here +fn other_markdown() {} diff --git a/tests/ui/doc/unbalanced_ticks.stderr b/tests/ui/doc/unbalanced_ticks.stderr new file mode 100644 index 00000000000..45ca34e2a8c --- /dev/null +++ b/tests/ui/doc/unbalanced_ticks.stderr @@ -0,0 +1,64 @@ +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:7:1 + | +LL | / /// This is a doc comment with `unbalanced_tick marks and several words that +LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`. +LL | | /// Because of the initial `unbalanced_tick` pair, the error message is +LL | | /// very `confusing_and_misleading`. + | |____________________________________^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + = help: a backtick may be missing a pair + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:13:1 + | +LL | /// This paragraph has `unbalanced_tick marks and should stop_linting. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: you should put `should_be` between ticks in the documentation + --> $DIR/unbalanced_ticks.rs:15:32 + | +LL | /// This paragraph is fine and should_be linted normally. + | ^^^^^^^^^ + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:17:1 + | +LL | /// Double unbalanced backtick from ``here to here` should lint. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: you should put `not_fine` between ticks in the documentation + --> $DIR/unbalanced_ticks.rs:30:8 + | +LL | /// ## not_fine + | ^^^^^^^^ + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:32:1 + | +LL | /// ### `unbalanced + | ^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:34:1 + | +LL | /// - This `item has unbalanced tick marks + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: you should put `backticks_here` between ticks in the documentation + --> $DIR/unbalanced_ticks.rs:35:23 + | +LL | /// - This item needs backticks_here + | ^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From 10122e4a48c3a31276f8e117af66c949c8f962a3 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Tue, 22 Jun 2021 18:45:12 -0700 Subject: Remove shadowed receiver in check invocation --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c6bef7bc5d5..21585543b0a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2036,7 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { match (name, args) { - ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => { + ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); }, ("and_then", [arg]) => { -- cgit 1.4.1-3-g733a5 From 9492de584319c0331619991e5b201ea6e00b9b2f Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 31 May 2021 14:15:17 -0400 Subject: Add import_rename lint, this adds a field on the Conf struct Rename lint and fix review issues --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/missing_enforced_import_rename.rs | 102 +++++++++++++++++++++ clippy_lints/src/utils/conf.rs | 9 ++ .../missing_enforced_import_rename/clippy.toml | 10 ++ .../conf_missing_enforced_import_rename.rs | 16 ++++ .../conf_missing_enforced_import_rename.stderr | 40 ++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 8 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/missing_enforced_import_rename.rs create mode 100644 tests/ui-toml/missing_enforced_import_rename/clippy.toml create mode 100644 tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs create mode 100644 tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 67c2b41b200..5f62a172035 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2657,6 +2657,7 @@ Released 2018-09-13 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 03e093f4c03..9cffeeb0224 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,6 +267,7 @@ mod misc; mod misc_early; mod missing_const_for_fn; mod missing_doc; +mod missing_enforced_import_rename; mod missing_inline; mod modulo_arithmetic; mod multiple_crate_versions; @@ -813,6 +814,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: misc_early::ZERO_PREFIXED_LITERAL, missing_const_for_fn::MISSING_CONST_FOR_FN, missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, + missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, modulo_arithmetic::MODULO_ARITHMETIC, multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, @@ -1017,6 +1019,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc::FLOAT_CMP_CONST), LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), + LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), @@ -2077,6 +2080,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unused_async::UnusedAsync); let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types)); + let import_renames = conf.enforced_import_renames.clone(); + store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone())); } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs new file mode 100644 index 00000000000..59565350f72 --- /dev/null +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -0,0 +1,102 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::{def::Res, def_id::DefId, Crate, Item, ItemKind, UseKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Symbol; + +use crate::utils::conf::Rename; + +declare_clippy_lint! { + /// **What it does:** Checks for imports that do not rename the item as specified + /// in the `enforce-import-renames` config option. + /// + /// **Why is this bad?** Consistency is important, if a project has defined import + /// renames they should be followed. More practically, some item names are too + /// vague outside of their defining scope this can enforce a more meaningful naming. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }] + /// ``` + /// + /// ```rust,ignore + /// use serde_json::Value; + /// ``` + /// Use instead: + /// ```rust,ignore + /// use serde_json::Value as JsonValue; + /// ``` + pub MISSING_ENFORCED_IMPORT_RENAMES, + restriction, + "enforce import renames" +} + +pub struct ImportRename { + conf_renames: Vec, + renames: FxHashMap, +} + +impl ImportRename { + pub fn new(conf_renames: Vec) -> Self { + Self { + conf_renames, + renames: FxHashMap::default(), + } + } +} + +impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); + +impl LateLintPass<'_> for ImportRename { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + for Rename { path, rename } in &self.conf_renames { + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::>()) { + self.renames.insert(id, Symbol::intern(rename)); + } + } + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Use(path, UseKind::Single) = &item.kind; + if let Res::Def(_, id) = path.res; + if let Some(name) = self.renames.get(&id); + // Remove semicolon since it is not present for nested imports + let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); + if let Some(snip) = snippet_opt(cx, span_without_semi); + if let Some(import) = match snip.split_once(" as ") { + None => Some(snip.as_str()), + Some((import, rename)) => { + if rename.trim() == &*name.as_str() { + None + } else { + Some(import.trim()) + } + }, + }; + then { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!( + "{} as {}", + import, + name, + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2f8064499fd..6fc4998318c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -8,6 +8,13 @@ use std::error::Error; use std::path::{Path, PathBuf}; use std::{env, fmt, fs, io}; +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. +#[derive(Clone, Debug, Deserialize)] +pub struct Rename { + pub path: String, + pub rename: String, +} + /// Conf with parse errors #[derive(Default)] pub struct TryConf { @@ -203,6 +210,8 @@ define_Conf! { (cargo_ignore_publish: bool = false), /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified.
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name. (standard_macro_braces: Vec = Vec::new()), + /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename. + (enforced_import_renames: Vec = Vec::new()), } /// Search for the configuration file. diff --git a/tests/ui-toml/missing_enforced_import_rename/clippy.toml b/tests/ui-toml/missing_enforced_import_rename/clippy.toml new file mode 100644 index 00000000000..05ba822874d --- /dev/null +++ b/tests/ui-toml/missing_enforced_import_rename/clippy.toml @@ -0,0 +1,10 @@ +enforced-import-renames = [ + { path = "std::option::Option", rename = "Maybe" }, + { path = "std::process::Child", rename = "Kid" }, + { path = "std::process::exit", rename = "goodbye" }, + { path = "std::collections::BTreeMap", rename = "Map" }, + { path = "std::clone", rename = "foo" }, + { path = "std::thread::sleep", rename = "thread_sleep" }, + { path = "std::any::type_name", rename = "ident" }, + { path = "std::sync::Mutex", rename = "StdMutie" } +] diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs new file mode 100644 index 00000000000..f60058c8628 --- /dev/null +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs @@ -0,0 +1,16 @@ +#![warn(clippy::missing_enforced_import_renames)] + +use std::alloc as colla; +use std::option::Option as Maybe; +use std::process::{exit as wrong_exit, Child as Kid}; +use std::thread::sleep; +#[rustfmt::skip] +use std::{ + any::{type_name, Any}, + clone, + sync :: Mutex, +}; + +fn main() { + use std::collections::BTreeMap as OopsWrongRename; +} diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr new file mode 100644 index 00000000000..45de8fdffef --- /dev/null +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr @@ -0,0 +1,40 @@ +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:5:20 + | +LL | use std::process::{exit as wrong_exit, Child as Kid}; + | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye` + | + = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:6:1 + | +LL | use std::thread::sleep; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::thread::sleep as thread_sleep` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:9:11 + | +LL | any::{type_name, Any}, + | ^^^^^^^^^ help: try: `type_name as ident` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:10:5 + | +LL | clone, + | ^^^^^ help: try: `clone as foo` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:11:5 + | +LL | sync :: Mutex, + | ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:15:5 + | +LL | use std::collections::BTreeMap as OopsWrongRename; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map` + +error: aborting due to 6 previous errors + 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 f179479cbec..aa7bfa2cc8c 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 `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 39856b17ec5a7deec4fa2c394e595616089fc6bd Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Tue, 22 Jun 2021 15:34:46 +0300 Subject: Improve lint message for match-same-arms lint Simplify error message producing & remove extra example --- clippy_lints/src/matches.rs | 3 ++- tests/ui/match_same_arms.stderr | 7 +++++++ tests/ui/match_same_arms2.stderr | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index cd3e3b97928..386349d9f59 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2266,7 +2266,8 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { ), ); } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); + diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,)) + .help("...or consider changing the match arm bodies"); } }, ); diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index 0549886a1e8..e48451acfe8 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -32,6 +32,7 @@ help: consider refactoring into `(1, .., 3) | (.., 3)` | LL | (1, .., 3) => 42, | ^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:24:15 @@ -49,6 +50,7 @@ help: consider refactoring into `42 | 51` | LL | 42 => 1, | ^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:26:15 @@ -66,6 +68,7 @@ help: consider refactoring into `41 | 52` | LL | 41 => 2, | ^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:32:14 @@ -83,6 +86,7 @@ help: consider refactoring into `1 | 2` | LL | 1 => 2, | ^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:33:14 @@ -100,6 +104,7 @@ help: consider refactoring into `1 | 3` | LL | 1 => 2, | ^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:33:14 @@ -117,6 +122,7 @@ help: consider refactoring into `2 | 3` | LL | 2 => 2, //~ ERROR 2nd matched arms have same body | ^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:50:55 @@ -134,6 +140,7 @@ help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo | LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: aborting due to 8 previous errors diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 430021a0f7f..e1ed12f9370 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -53,6 +53,7 @@ help: consider refactoring into `42 | 51` | LL | 42 => foo(), | ^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:40:17 @@ -70,6 +71,7 @@ help: consider refactoring into `Some(_) | None` | LL | Some(_) => 24, | ^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:62:28 @@ -87,6 +89,7 @@ help: consider refactoring into `(Some(a), None) | (None, Some(a))` | LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:68:26 @@ -104,6 +107,7 @@ help: consider refactoring into `(Some(a), ..) | (.., Some(a))` | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:102:29 @@ -121,6 +125,7 @@ help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: this `match` has identical arm bodies @@ -139,6 +144,7 @@ help: consider refactoring into `Ok(3) | Ok(_)` | LL | Ok(3) => println!("ok"), | ^^^^^ + = help: ...or consider changing the match arm bodies = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: this `match` has identical arm bodies @@ -163,6 +169,7 @@ help: consider refactoring into `0 | 1` | LL | 0 => { | ^ + = help: ...or consider changing the match arm bodies error: match expression looks like `matches!` macro --> $DIR/match_same_arms2.rs:162:16 -- cgit 1.4.1-3-g733a5 From 28d3873ef52fd1e4f9efa115d18235e92fecfa83 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 19 Jun 2021 21:14:05 +0300 Subject: Do not spawn blacklisted_name lint in test context Fix detecting of the 'test' attribute Update UI test to actually check that warning is not triggered in the test code Fix approach for detecting the test module Add nested test case Remove code duplication by extracting 'is_test_module_or_function' into 'clippy_utils' Cleanup the code --- clippy_lints/src/blacklisted_name.rs | 31 ++++++++++++++++++++++++++++--- clippy_lints/src/wildcard_imports.rs | 12 ++++-------- clippy_utils/src/lib.rs | 12 ++++++++++++ tests/ui/blacklisted_name.rs | 12 ++++++++++++ 4 files changed, 56 insertions(+), 11 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/blacklisted_name.rs b/clippy_lints/src/blacklisted_name.rs index b26ef33e056..8eb94f3c28e 100644 --- a/clippy_lints/src/blacklisted_name.rs +++ b/clippy_lints/src/blacklisted_name.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::{diagnostics::span_lint, is_test_module_or_function}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Pat, PatKind}; +use rustc_hir::{Item, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -25,18 +25,37 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct BlacklistedName { blacklist: FxHashSet, + test_modules_deep: u32, } impl BlacklistedName { pub fn new(blacklist: FxHashSet) -> Self { - Self { blacklist } + Self { + blacklist, + test_modules_deep: 0, + } + } + + fn in_test_module(&self) -> bool { + self.test_modules_deep != 0 } } impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]); impl<'tcx> LateLintPass<'tcx> for BlacklistedName { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_test_module_or_function(cx.tcx, item) { + self.test_modules_deep = self.test_modules_deep.saturating_add(1); + } + } + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + // Check whether we are under the `test` attribute. + if self.in_test_module() { + return; + } + if let PatKind::Binding(.., ident, _) = pat.kind { if self.blacklist.contains(&ident.name.to_string()) { span_lint( @@ -48,4 +67,10 @@ impl<'tcx> LateLintPass<'tcx> for BlacklistedName { } } } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_test_module_or_function(cx.tcx, item) { + self.test_modules_deep = self.test_modules_deep.saturating_sub(1); + } + } } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 51c1117d206..520586b3a1f 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::{in_macro, is_test_module_or_function}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -106,7 +106,7 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(item) { + if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { @@ -183,8 +183,8 @@ impl LateLintPass<'_> for WildcardImports { } } - fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(item) { + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_sub(1); } } @@ -208,7 +208,3 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.name == kw::Super } - -fn is_test_module_or_function(item: &Item<'_>) -> bool { - matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 66e75b0c206..ef4854afc83 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1727,3 +1727,15 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } } + +/// Checks whether item either has `test` attribute applied, or +/// is a module with `test` in its name. +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { + if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) { + if tcx.has_attr(def_id.to_def_id(), sym::test) { + return true; + } + } + + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") +} diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index cb15bdd2f1b..57d7139fef5 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -43,3 +43,15 @@ fn issue_1647_ref_mut() { let ref mut baz = 0; if let Some(ref mut quux) = Some(42) {} } + +mod tests { + fn issue_7305() { + // `blackisted_name` lint should not be triggered inside of the test code. + let foo = 0; + + // Check that even in nested functions warning is still not triggere. + fn nested() { + let foo = 0; + } + } +} -- cgit 1.4.1-3-g733a5 From 7e21db5b5c18f47a5f43e36f0bf34f1bbd1a1466 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 14 Jun 2021 10:01:43 -0500 Subject: Add suspicious group --- README.md | 21 +++++++++++---------- clippy_dev/src/main.rs | 1 + clippy_dev/src/update_lints.rs | 5 ++++- clippy_lints/src/lib.rs | 11 ++++++++--- .../src/utils/internal_lints/metadata_collector.rs | 3 ++- util/lintlib.py | 1 + 6 files changed, 27 insertions(+), 15 deletions(-) (limited to 'clippy_lints/src') diff --git a/README.md b/README.md index 6c556f579ca..29bfebe4583 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,17 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. -| Category | Description | Default level | -| --------------------- | ----------------------------------------------------------------------- | ------------- | -| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** | -| `clippy::correctness` | code that is outright wrong or very useless | **deny** | -| `clippy::style` | code that should be written in a more idiomatic way | **warn** | -| `clippy::complexity` | code that does something simple but in a complex way | **warn** | -| `clippy::perf` | code that can be written to run faster | **warn** | -| `clippy::pedantic` | lints which are rather strict or might have false positives | allow | -| `clippy::nursery` | new lints that are still under development | allow | -| `clippy::cargo` | lints for the cargo manifest | allow | +| Category | Description | Default level | +| --------------------- | ----------------------------------------------------------------------------------- | ------------- | +| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** | +| `clippy::correctness` | code that is outright wrong or useless | **deny** | +| `clippy::suspicious` | code that is most likely wrong or useless | **warn** | +| `clippy::style` | code that should be written in a more idiomatic way | **warn** | +| `clippy::complexity` | code that does something simple but in a complex way | **warn** | +| `clippy::perf` | code that can be written to run faster | **warn** | +| `clippy::pedantic` | lints which are rather strict or might have false positives | allow | +| `clippy::nursery` | new lints that are still under development | allow | +| `clippy::cargo` | lints for the cargo manifest | allow | More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index f5bd08657ea..fbc530eb606 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -137,6 +137,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .possible_values(&[ "style", "correctness", + "suspicious", "complexity", "perf", "pedantic", diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index edf6c5f57a4..db467c26f15 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -92,7 +92,10 @@ pub fn run(update_mode: UpdateMode) { || { // clippy::all should only include the following lint groups: let all_group_lints = usable_lints.iter().filter(|l| { - l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf" + matches!( + &*l.group, + "correctness" | "suspicious" | "style" | "complexity" | "perf" + ) }); gen_lint_group_list(all_group_lints) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9cffeeb0224..4ce1d511d27 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -60,9 +60,9 @@ use rustc_session::Session; /// 4. The `description` that contains a short explanation on what's wrong with code where the /// lint is triggered. /// -/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. -/// As said in the README.md of this repository, if the lint level mapping changes, please update -/// README.md. +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are +/// enabled by default. As said in the README.md of this repository, if the lint level mapping +/// changes, please update README.md. /// /// # Example /// @@ -106,6 +106,11 @@ macro_rules! declare_clippy_lint { $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true } }; + { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { declare_tool_lint! { $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c980a0246fd..e877af09e28 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -47,8 +47,9 @@ const DEPRECATED_LINT_GROUP_STR: &str = "deprecated"; const DEPRECATED_LINT_LEVEL: &str = "none"; /// This array holds Clippy's lint groups with their corresponding default lint level. The /// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`. -const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [ +const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[ ("correctness", "deny"), + ("suspicious", "warn"), ("restriction", "allow"), ("style", "warn"), ("pedantic", "allow"), diff --git a/util/lintlib.py b/util/lintlib.py index 3b6e8c372ed..9cefb2dbb19 100644 --- a/util/lintlib.py +++ b/util/lintlib.py @@ -19,6 +19,7 @@ comment_re = re.compile(r'''\s*/// ?(.*)''') lint_levels = { "correctness": 'Deny', + "suspicious": 'Warn', "style": 'Warn', "complexity": 'Warn', "perf": 'Warn', -- cgit 1.4.1-3-g733a5 From f02632cee63137fb0184ecb3828fdd6d8f9ce6dd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 14 Jun 2021 14:09:17 -0500 Subject: Move some lints to suspicious --- clippy_lints/src/assign_ops.rs | 2 +- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/float_equality_without_abs.rs | 2 +- clippy_lints/src/formatting.rs | 6 ++--- clippy_lints/src/lib.rs | 31 ++++++++++++++------------ clippy_lints/src/loops/mod.rs | 6 ++--- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/mut_key.rs | 2 +- clippy_lints/src/suspicious_trait_impl.rs | 4 ++-- tests/ui/mut_key.stderr | 2 +- tests/ui/suspicious_arithmetic_impl.stderr | 2 +- 12 files changed, 33 insertions(+), 30 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index bc6eec0051a..a8c527fe2e3 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -55,7 +55,7 @@ declare_clippy_lint! { /// a += a + b; /// ``` pub MISREFACTORED_ASSIGN_OP, - complexity, + suspicious, "having a variable on both sides of an assign op" } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 932cd58bf62..f272ed010a1 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -173,7 +173,7 @@ declare_clippy_lint! { /// #![deny(clippy::as_conversions)] /// ``` pub BLANKET_CLIPPY_RESTRICTION_LINTS, - style, + suspicious, "enabling the complete restriction group" } diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 5fdf5bc9e9d..03a8b40df55 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// let a = tmp + x; /// ``` pub EVAL_ORDER_DEPENDENCE, - complexity, + suspicious, "whether a variable read occurs before a write depends on sub-expression evaluation order" } diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index b5ebe5f90ba..1e503cc795c 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// } /// ``` pub FLOAT_EQUALITY_WITHOUT_ABS, - correctness, + suspicious, "float equality check without `.abs()`" } diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 3bd6a09d365..8aefb8d46f6 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`? /// ``` pub SUSPICIOUS_ASSIGNMENT_FORMATTING, - style, + suspicious, "suspicious formatting of `*=`, `-=` or `!=`" } @@ -44,7 +44,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_UNARY_OP_FORMATTING, - style, + suspicious, "suspicious formatting of unary `-` or `!` on the RHS of a BinOp" } @@ -80,7 +80,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_ELSE_FORMATTING, - style, + suspicious, "suspicious formatting of `else`" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4ce1d511d27..20eea61d78d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1461,7 +1461,6 @@ 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(blacklisted_name::BLACKLISTED_NAME), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), @@ -1479,9 +1478,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(eq_op::OP_REF), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(from_over_into::FROM_OVER_INTO), LintId::of(from_str_radix_10::FROM_STR_RADIX_10), LintId::of(functions::DOUBLE_MUST_USE), @@ -1494,7 +1490,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(len_zero::LEN_ZERO), LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_KV_MAP), LintId::of(loops::NEEDLESS_RANGE_LOOP), LintId::of(loops::SAME_ITEM_PUSH), @@ -1574,7 +1569,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(casts::CHAR_LIT_AS_U8), @@ -1584,7 +1578,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), @@ -1595,7 +1588,6 @@ 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::MANUAL_FLATTEN), - LintId::of(loops::MUT_RANGE_BOUND), LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), LintId::of(manual_strip::MANUAL_STRIP), @@ -1619,7 +1611,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::SUSPICIOUS_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::USELESS_ASREF), LintId::of(misc::SHORT_CIRCUIT_STATEMENT), @@ -1688,7 +1679,6 @@ 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), @@ -1698,7 +1688,6 @@ 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_LOOPS_OVER_FALLIBLES), LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), @@ -1713,7 +1702,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc::CMP_NAN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::MODULO_ONE), - LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), @@ -1724,8 +1712,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap::ALMOST_SWAPPED), LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), @@ -1742,6 +1728,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), ]); + store.register_group(true, "clippy::suspicious", None, vec![ + LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(loops::EMPTY_LOOP), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(mut_key::MUTABLE_KEY_TYPE), + LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + ]); + store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(entry::MAP_ENTRY), LintId::of(escape::BOXED_LOCAL), diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index a4bc3e6bd10..56a123b69c6 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -199,7 +199,7 @@ declare_clippy_lint! { /// } /// ``` pub FOR_LOOPS_OVER_FALLIBLES, - correctness, + suspicious, "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } @@ -313,7 +313,7 @@ declare_clippy_lint! { /// loop {} /// ``` pub EMPTY_LOOP, - style, + suspicious, "empty `loop {}`, which should block or sleep" } @@ -401,7 +401,7 @@ declare_clippy_lint! { /// } /// ``` pub MUT_RANGE_BOUND, - complexity, + suspicious, "for loop over a range where one of the bounds is a mutable variable" } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 21585543b0a..283fcf281df 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1248,7 +1248,7 @@ declare_clippy_lint! { /// let _ = (0..3).map(|x| x + 2).count(); /// ``` pub SUSPICIOUS_MAP, - complexity, + suspicious, "suspicious usage of map" } diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 1786d5805d7..6c87136e5e1 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// } /// ``` pub MUTABLE_KEY_TYPE, - correctness, + suspicious, "Check for mutable `Map`/`Set` key type" } diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 512abde11a6..2203ab57b10 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_ARITHMETIC_IMPL, - correctness, + suspicious, "suspicious use of operators in impl of arithmetic trait" } @@ -47,7 +47,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_OP_ASSIGN_IMPL, - correctness, + suspicious, "suspicious use of operators in impl of OpAssign trait" } diff --git a/tests/ui/mut_key.stderr b/tests/ui/mut_key.stderr index 8d6a259c7e3..a8460b06ca6 100644 --- a/tests/ui/mut_key.stderr +++ b/tests/ui/mut_key.stderr @@ -4,7 +4,7 @@ error: mutable key type LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::mutable_key_type)]` on by default + = note: `-D clippy::mutable-key-type` implied by `-D warnings` error: mutable key type --> $DIR/mut_key.rs:27:72 diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 388fc740082..63fc9ecb79a 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -12,7 +12,7 @@ error: suspicious use of binary operator in `AddAssign` impl LL | *self = *self - other; | ^ | - = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default + = note: `-D clippy::suspicious-op-assign-impl` implied by `-D warnings` error: suspicious use of binary operator in `MulAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:32:16 -- cgit 1.4.1-3-g733a5 From 38569c03eb0d0917698d83aea5fbbc35acf7305c Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 26 Jun 2021 15:22:15 +0200 Subject: Don't suggest unstable and doc(hidden) variants. --- clippy_lints/src/matches.rs | 8 ++++---- clippy_utils/src/attrs.rs | 5 +++++ tests/ui/auxiliary/non-exhaustive-enum.rs | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index cd3e3b97928..da5ac96e3db 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -992,9 +992,9 @@ impl CommonPrefixSearcher<'a> { } } -fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { +fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { let attrs = cx.tcx.get_attrs(variant_def.def_id); - clippy_utils::attrs::is_doc_hidden(attrs) + clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs) } #[allow(clippy::too_many_lines)] @@ -1033,7 +1033,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let mut missing_variants: Vec<_> = adt_def.variants.iter().collect(); + let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -1118,7 +1118,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 0318c483959..c19b558cd8c 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -157,3 +157,8 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } + +/// Return true if the attributes contain `#[unstable]` +pub fn is_unstable(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| attr.has_name(sym::unstable)) +} diff --git a/tests/ui/auxiliary/non-exhaustive-enum.rs b/tests/ui/auxiliary/non-exhaustive-enum.rs index 67d4d255701..18560bc5e1e 100644 --- a/tests/ui/auxiliary/non-exhaustive-enum.rs +++ b/tests/ui/auxiliary/non-exhaustive-enum.rs @@ -20,4 +20,6 @@ pub enum ErrorKind { UnexpectedEof, Unsupported, OutOfMemory, + #[doc(hidden)] + Uncategorized, } -- cgit 1.4.1-3-g733a5 From 8827e96303a15f9accc7c02ae2a2bd42573b2739 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 17 Jun 2021 11:19:33 -0500 Subject: Make ItemKind check dry --- clippy_lints/src/use_self.rs | 79 ++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 44 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f71dfd02499..9cac3b20ac5 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -87,59 +87,42 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if !is_item_interesting(item) { + // This does two things: + // 1) Reduce needless churn on `self.stack` + // 2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`, + // in order to lint `foo() -> impl <..>` + return; + } // We push the self types of `impl`s on a stack here. Only the top type on the stack is // relevant for linting, since this is the self type of the `impl` we're currently in. To // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // we're in an `impl` or nested item, that we don't want to lint - // - // NB: If you push something on the stack in this method, remember to also pop it in the - // `check_item_post` method. - match &item.kind { - ItemKind::Impl(Impl { - self_ty: hir_self_ty, - of_trait, - .. - }) => { - let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind { - let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - parameters.as_ref().map_or(true, |params| { - !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) - }) - } else { - false - }; + let stack_item = if_chain! { + if let ItemKind::Impl(Impl { self_ty, ref of_trait, .. }) = item.kind; + if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; + let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; + if parameters.as_ref().map_or(true, |params| { + !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) + }); + then { let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id())); - if should_check { - self.stack.push(StackItem::Check { - hir_id: hir_self_ty.hir_id, - impl_trait_ref_def_id, - types_to_lint: Vec::new(), - types_to_skip: Vec::new(), - }); - } else { - self.stack.push(StackItem::NoCheck); + StackItem::Check { + hir_id: self_ty.hir_id, + impl_trait_ref_def_id, + types_to_lint: Vec::new(), + types_to_skip: Vec::new(), } - }, - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::Fn(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::Union(..) - | ItemKind::Trait(..) => { - self.stack.push(StackItem::NoCheck); - }, - _ => (), - } + } else { + StackItem::NoCheck + } + }; + self.stack.push(stack_item); } fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { - use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union}; - match item.kind { - Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => { - self.stack.pop(); - }, - _ => (), + if is_item_interesting(item) { + self.stack.pop(); } } @@ -359,6 +342,14 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { } } +fn is_item_interesting(item: &Item<'_>) -> bool { + use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union}; + matches!( + item.kind, + Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) + ) +} + fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> { if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) { hir_ty_to_ty(cx.tcx, hir_ty) -- cgit 1.4.1-3-g733a5 From 4913d82175366c1b303afce17071d070d8203874 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 16 Jun 2021 12:23:09 -0500 Subject: Simplify in impl check --- clippy_lints/src/use_self.rs | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 9cac3b20ac5..fea4b6c5c91 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::same_type_and_consts; use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{ self as hir, @@ -75,7 +76,7 @@ enum StackItem { Check { hir_id: HirId, impl_trait_ref_def_id: Option, - types_to_skip: Vec, + types_to_skip: FxHashSet, types_to_lint: Vec, }, NoCheck, @@ -111,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { hir_id: self_ty.hir_id, impl_trait_ref_def_id, types_to_lint: Vec::new(), - types_to_skip: Vec::new(), + types_to_skip: std::iter::once(self_ty.hir_id).collect(), } } else { StackItem::NoCheck @@ -216,7 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { - if !in_macro(hir_ty.span) && !in_impl(cx, hir_ty); + if !in_macro(hir_ty.span); if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(StackItem::Check { hir_id, @@ -358,20 +359,6 @@ fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> { } } -fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool { - let map = cx.tcx.hir(); - let parent = map.get_parent_node(hir_ty.hir_id); - if_chain! { - if let Some(Node::Item(item)) = map.find(parent); - if let ItemKind::Impl { .. } = item.kind; - then { - true - } else { - false - } - } -} - fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if_chain! { if same_type_and_consts(ty, self_ty); -- cgit 1.4.1-3-g733a5 From 6e801e251fb6de6dc9563b0203ea3dab14e3d3ec Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 16 Jun 2021 15:43:48 -0500 Subject: Use type_of for impl self type --- clippy_lints/src/use_self.rs | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index fea4b6c5c91..3623e5139f9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -9,7 +9,7 @@ use rustc_hir::{ def::{CtorOf, DefKind, Res}, def_id::LocalDefId, intravisit::{walk_ty, NestedVisitorMap, Visitor}, - Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, QPath, TyKind, + Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -74,8 +74,7 @@ impl UseSelf { #[derive(Debug)] enum StackItem { Check { - hir_id: HirId, - impl_trait_ref_def_id: Option, + impl_id: LocalDefId, types_to_skip: FxHashSet, types_to_lint: Vec, }, @@ -87,7 +86,7 @@ impl_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { if !is_item_interesting(item) { // This does two things: // 1) Reduce needless churn on `self.stack` @@ -100,17 +99,15 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // we're in an `impl` or nested item, that we don't want to lint let stack_item = if_chain! { - if let ItemKind::Impl(Impl { self_ty, ref of_trait, .. }) = item.kind; + if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind; if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; if parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) }); then { - let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id())); StackItem::Check { - hir_id: self_ty.hir_id, - impl_trait_ref_def_id, + impl_id: item.def_id, types_to_lint: Vec::new(), types_to_skip: std::iter::once(self_ty.hir_id).collect(), } @@ -133,11 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if_chain! { if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind; if let Some(&mut StackItem::Check { - impl_trait_ref_def_id: Some(def_id), + impl_id, ref mut types_to_skip, .. }) = self.stack.last_mut(); - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id); + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id); then { // `self_ty` is the semantic self type of `impl for `. This cannot be // `Self`. @@ -195,13 +192,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // could only allow this lint on item scope. And we would have to check if those types are // already dealt with in `check_ty` anyway. if let Some(StackItem::Check { - hir_id, + impl_id, types_to_lint, types_to_skip, .. }) = self.stack.last_mut() { - let self_ty = ty_from_hir_id(cx, *hir_id); + let self_ty = cx.tcx.type_of(*impl_id); let mut visitor = LintTyCollector { cx, @@ -220,15 +217,14 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !in_macro(hir_ty.span); if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(StackItem::Check { - hir_id, + impl_id, types_to_lint, types_to_skip, - .. }) = self.stack.last(); if !types_to_skip.contains(&hir_ty.hir_id); if types_to_lint.contains(&hir_ty.hir_id) || { - let self_ty = ty_from_hir_id(cx, *hir_id); + let self_ty = cx.tcx.type_of(*impl_id); should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) }; let hir = cx.tcx.hir(); @@ -244,8 +240,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if_chain! { if !in_macro(expr.span); if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(StackItem::Check { hir_id, .. }) = self.stack.last(); - if cx.typeck_results().expr_ty(expr) == ty_from_hir_id(cx, *hir_id); + if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); + if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } } match expr.kind { @@ -351,14 +347,6 @@ fn is_item_interesting(item: &Item<'_>) -> bool { ) } -fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> { - if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) { - hir_ty_to_ty(cx.tcx, hir_ty) - } else { - unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`") - } -} - fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if_chain! { if same_type_and_consts(ty, self_ty); -- cgit 1.4.1-3-g733a5 From bae76f9c1756e0a8d25c8ddc0b2c8225df1b364e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 16 Jun 2021 12:14:23 -0500 Subject: Remove a visitor from use_self --- clippy_lints/src/use_self.rs | 97 ++++++++++---------------------------------- 1 file changed, 22 insertions(+), 75 deletions(-) (limited to 'clippy_lints/src') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 3623e5139f9..906ac10f461 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -13,7 +13,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{AssocKind, Ty}; +use rustc_middle::ty::AssocKind; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -75,8 +75,8 @@ impl UseSelf { enum StackItem { Check { impl_id: LocalDefId, + in_body: u32, types_to_skip: FxHashSet, - types_to_lint: Vec, }, NoCheck, } @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { then { StackItem::Check { impl_id: item.def_id, - types_to_lint: Vec::new(), + in_body: 0, types_to_skip: std::iter::once(self_ty.hir_id).collect(), } } else { @@ -182,33 +182,18 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) { + fn check_body(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) { // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`. // However the `node_type()` method can *only* be called in bodies. - // - // This method implementation determines which types should get linted in a `Body` and - // which shouldn't, with a visitor. We could directly lint in the visitor, but then we - // could only allow this lint on item scope. And we would have to check if those types are - // already dealt with in `check_ty` anyway. - if let Some(StackItem::Check { - impl_id, - types_to_lint, - types_to_skip, - .. - }) = self.stack.last_mut() - { - let self_ty = cx.tcx.type_of(*impl_id); + if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() { + *in_body = in_body.saturating_add(1); + } + } - let mut visitor = LintTyCollector { - cx, - self_ty, - types_to_lint: vec![], - types_to_skip: vec![], - }; - visitor.visit_expr(&body.value); - types_to_lint.extend(visitor.types_to_lint); - types_to_skip.extend(visitor.types_to_skip); + fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) { + if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() { + *in_body = in_body.saturating_sub(1); } } @@ -216,17 +201,20 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if_chain! { if !in_macro(hir_ty.span); if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(StackItem::Check { + if let Some(&StackItem::Check { impl_id, - types_to_lint, - types_to_skip, + in_body, + ref types_to_skip, }) = self.stack.last(); + if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; + if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _)); if !types_to_skip.contains(&hir_ty.hir_id); - if types_to_lint.contains(&hir_ty.hir_id) - || { - let self_ty = cx.tcx.type_of(*impl_id); - should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) - }; + let ty = if in_body > 0 { + cx.typeck_results().node_type(hir_ty.hir_id) + } else { + hir_ty_to_ty(cx.tcx, hir_ty) + }; + if same_type_and_consts(ty, cx.tcx.type_of(impl_id)); let hir = cx.tcx.hir(); let id = hir.get_parent_node(hir_ty.hir_id); if !hir.opt_span(id).map_or(false, in_macro); @@ -289,35 +277,6 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector { } } -struct LintTyCollector<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - self_ty: Ty<'tcx>, - types_to_lint: Vec, - types_to_skip: Vec, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) { - if_chain! { - if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id); - if should_lint_ty(hir_ty, ty, self.self_ty); - then { - self.types_to_lint.push(hir_ty.hir_id); - } else { - self.types_to_skip.push(hir_ty.hir_id); - } - } - - walk_ty(self, hir_ty); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - fn span_lint(cx: &LateContext<'_>, span: Span) { span_lint_and_sugg( cx, @@ -346,15 +305,3 @@ fn is_item_interesting(item: &Item<'_>) -> bool { Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) ) } - -fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { - if_chain! { - if same_type_and_consts(ty, self_ty); - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; - then { - !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _)) - } else { - false - } - } -} -- cgit 1.4.1-3-g733a5 From 018be41deedd086191b8ce45895164e0aa7046b0 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Wed, 30 Jun 2021 19:06:33 +0300 Subject: Implement 'disallowed_script_idents' lint --- CHANGELOG.md | 1 + clippy_lints/Cargo.toml | 1 + clippy_lints/src/disallowed_script_idents.rs | 112 +++++++++++++++++++++ clippy_lints/src/lib.rs | 6 +- clippy_lints/src/utils/conf.rs | 2 + .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/disallowed_script_idents.rs | 10 ++ tests/ui/disallowed_script_idents.stderr | 20 ++++ 8 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/disallowed_script_idents.rs create mode 100644 tests/ui/disallowed_script_idents.rs create mode 100644 tests/ui/disallowed_script_idents.stderr (limited to 'clippy_lints/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f62a172035..f3a80703238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2487,6 +2487,7 @@ Released 2018-09-13 [`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 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`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 diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d3d12062f07..42cf7547f51 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -23,6 +23,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } toml = "0.5.3" unicode-normalization = "0.1" +unicode-script = { version = "0.5.3", default-features = false } semver = "0.11" rustc-semver = "1.1.0" # NOTE: cargo requires serde feat in its url dep diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs new file mode 100644 index 00000000000..12c525634c5 --- /dev/null +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -0,0 +1,112 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast; +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{EarlyContext, EarlyLintPass, Level}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use unicode_script::{Script, UnicodeScript}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of unicode scripts other than those explicitly allowed + /// by the lint config. + /// + /// This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`. + /// It also ignores the `Common` script type. + /// While configuring, be sure to use official script name [aliases] from + /// [the list of supported scripts][supported_scripts]. + /// + /// See also: [`non_ascii_idents`]. + /// + /// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases + /// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html + /// + /// **Why is this bad?** It may be not desired to have many different scripts for + /// identifiers in the codebase. + /// + /// Note that if you only want to allow plain English, you might want to use + /// built-in [`non_ascii_idents`] lint instead. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Assuming that `clippy.toml` contains the following line: + /// // allowed-locales = ["Latin", "Cyrillic"] + /// let counter = 10; // OK, latin is allowed. + /// let счётчик = 10; // OK, cyrillic is allowed. + /// let zähler = 10; // OK, it's still latin. + /// let カウンタ = 10; // Will spawn the lint. + /// ``` + pub DISALLOWED_SCRIPT_IDENTS, + restriction, + "usage of non-allowed Unicode scripts" +} + +#[derive(Clone, Debug)] +pub struct DisallowedScriptIdents { + whitelist: FxHashSet

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